Любите загадки? Событие еще доступно на сайте.
Разработчикам

Советы по безопасности

Распространенные ошибки в коде, приводящие к уязвимостям безопасности в приложениях на Laravel.

0
Основы

Минимальные меры безопасности, которые вы должны принять для защиты вашего приложения.

Безопасность слишком обширная тема!

На этой страницы рассматриваются только основные ошибки связанные с написанием кода, но помните, что безопасность – это не только код, это также и конфигурация сервера, сетевая безопасность, управление учетными данными и многое другое.

Для аудита безопасности обратите внимание на профессиональные компании, такие как Positive Technologies.

Убедитесь, что ваше приложение не находится в режиме отладки при работе в продакшене. Для отключения режима отладки установите переменную окружения APP_DEBUG в значение false:

APP_DEBUG=false

Иначе в случае возникновения ошибки, пользователь увидит подробную информацию (включая ключи и пароли) о вашем приложении, что может быть использовано злоумышленниками для атаки

Установите безопасные разрешения на файлы и каталоги вашего приложения Laravel:

  • В общем случае все каталоги Laravel должны быть настроены с максимальным уровнем разрешений 775,
  • Неисполняемые файлы должны иметь разрешения 664, чтобы обеспечить безопасность и предотвратить возможные атаки.
1
Межсайтовый скриптинг (XSS)

Не дайте чужому коду проникнуть в браузер пользователя.

В шаблонизаторе Blade используются операторы вывода {{ }}, которые автоматически защищают вывод с помощью функции htmlspecialchars PHP.

Это необходимо для предотвращения атак XSS, при которой злоумышленник внедряет вредоносный скрипт в веб-приложение, который выполняется в браузере пользователя. Это может привести к краже сессионных cookie, перенаправлению на вредоносные сайты, изменению содержимого страницы и другим нежелательным последствиям.

Также в Blade есть возможность отображать данные без экранирования, используя синтаксис {!! !!}. Однако этот подход следует применять только к надежным данным, так как иначе ваше приложение станет уязвимым для атак XSS.

Пример использования небезопасного синтаксиса в шаблоне Blade:

{!! request()->input('somedata') !!}

Для обеспечения безопасности следует предпочитать следующий подход:

{{ request()->input('somedata') }}
2
Массовое присвоение

Предотвратите несанкционированные изменения данных.

Массовое присвоение – это уязвимость, когда ORM позволяет изменять данные, которые пользователь обычно не должен иметь возможность изменять.

Рассмотрим следующий код:

Route::any('/profile', function (Request $request) {
    $user = $request->user();
    
    $user->forceFill($request->all())->save();

    return response()->json(['user' => $user]);
})->middleware('auth');

Этот маршрут позволяет пользователям изменять информацию в своем профиле.

Однако, предположим, что в таблице пользователей есть столбец is_admin. Вы, вероятно, не хотите, чтобы пользователь мог изменять это значение. Но код выше позволяет им изменять любые значения в их профиле, включая is_admin. Это уязвимость массового присвоения.

В Laravel по умолчанию встроены функции для защиты от этой уязвимости. Чтобы оставаться в безопасности:

  • Ограничьте параметры, которые вы хотите обновить, используя $request->only или $request->validated, а не $request->all.
  • Относитесь с осторожностью устанавливая значения $guarded и $fillable. В большинстве случаев, вы должны использовать $fillable для разрешения массового присвоения, а не $guarded. Однако, если вы используете $guarded, убедитесь, что вы добавили все поля, которые вы хотите защитить.
  • Избегайте использования методов, таких как forceFill или forceCreate, которые могут обойти защиту. Однако, если вы передаете проверенный массив значений, вы можете использовать их.
3
Загрузка файлов

Всегда проверяйте тип файла и не доверяйте пользователю определять имена файлов или пути.

Всегда проверяйте тип файла (расширение или MIME-тип), чтобы избежать выполнение удаленного кода:

$request->validate([
    'photo' => 'file|size:100|mimes:jpg,bmp,png'
]);

Атаки на выполнение удаленного кода включают загрузку вредоносных исполняемых файлов (например файлы PHP) и затем запуск их вредоносного кода, посещая URL-адрес файла (если он публичный).

По возможности старайтесь избегать обработки ZIP/XML файлов, так как они могут быть использованы для атак. Например, XXE и XEE атаки для XML и атаки на отказ в обслуживании для ZIP файлов (ZIP-бомбы).

Если ваше приложение позволяет пользовательским данным управлять путем файла для загрузки, это может привести к перезаписи критического файла или сохранению файла в неподходящем месте.

Рассмотрим следующий код:

Route::post('/upload', function (Request $request) {
    $request->file('file')->storeAs(
        $request->user()->id(),
        $request->input('filename')
     );

    return back();
});

Этот маршрут сохраняет файл в каталоге, специфическом для идентификатора пользователя. Здесь мы полагаемся на данные ввода пользователя filename, и это может привести к уязвимости, поскольку имя файла может быть что-то вроде ../2/filename.pdf. Это приведет к загрузке файла в каталог пользователя с идентификатором 2, а не в каталог, соответствующий текущему вошедшему пользователю.

Чтобы исправить это, мы должны использовать функцию PHP basename, чтобы удалить любую информацию о директории из данных ввода filename:

Route::post('/upload', function (Request $request) {
    $request->file('file')->storeAs(
        $request->user()->id(),
        basename($request->input('filename'))
     );

    return back();
});
4
Обход каталога

Не доверяйте даже имени файла, которое вы получаете от пользователя.

Обход каталога (Directory Traversal) – это атака, направленная на получение доступа к файлам путем изменения данных запроса. Это достигается за счет использования последовательностей ../ и их вариаций, а также абсолютных путей к файлам.

Если ваше приложение позволяет пользователям загружать файлы с сохранением оригинального имени файла, оно может столкнуться с уязвимостью Directory Traversal, если не проводится достаточная фильтрация входных данных для удаления информации о директории.

Давайте рассмотрим следующий пример кода:

Route::get('/download', function(Request $request) {
    return response()->download(
        storage_path('content/').$request->input('filename')
    );
});

В данном случае имя файла не очищается от информации о директории, что означает, что некорректное имя файла, например, ../../.env, может позволить злоумышленнику получить доступ к конфиденциальным данным вашего приложения.

Чтобы избежать этого, необходимо использовать функцию PHP basename для очистки информации о директории, например:

Route::get('/download', function(Request $request) {
    return response()->download(
        storage_path('content/').basename($request->input('filename'))
    );
});
5
SQL-инъекции

Всегда используйте привязку данных для запросов, избегайте пользовательских данных в именах столбцов и будьте внимательны при валидации.

По умолчанию, Eloquent ORM Laravel защищает от SQL-инъекций путем параметризации запросов и использования привязок SQL. Например, рассмотрим следующий запрос:

use App\Models\User;

User::where('email', $email)->get();

Приведенный выше код выполняет следующий запрос:

select *
from `users`
where `email` = ?

Таким образом, даже если $email является ненадежными данными ввода пользователя, вы защищены от атак SQL-инъекций. Однако есть несколько случаев, когда вы можете быть уязвимы к SQL-инъекция:

Необработанные SQL-запросы

Хотя Laravel предоставляет возможность использовать необработанные выражения запросов для создания более сложных или специфичных запросов (Например, для не поддерживаемой базы данных), следует быть осторожным и всегда использовать привязку данных. Например:

use Illuminate\Support\Facades\DB;
use App\Models\User;

User::whereRaw('email = "'.$request->input('email').'"')
    ->get();

// или так:
DB::table('users')
    ->whereRaw('email = "'.$request->input('email').'"')
    ->get();

Эти запросы уязвимы к инъекциям, так как не используют привязки SQL для данных полученных от пользователя. Мы можем исправить код выше, сделав следующую модификацию:

use App\Models\User;

User::whereRaw('email = ?', [
    $request->input('email')
])->get();

Мы даже можем использовать именованные привязки SQL, как показано ниже:

use App\Models\User;

User::whereRaw('email = :email', [
    'email' => $request->input('email')
])->get();

SQL-инъекции по именам столбцов

Вы никогда не должны разрешать пользовательские данные влиять на имена столбцов, на которые ссылаются ваши запросы.

Следующие запросы могут быть уязвимы к SQL-инъекциям:

use App\Models\User;

User::where($request->input('colname'), 'somedata')
    ->get();

User::query()->orderBy($request->input('sortBy'))
    ->get();

Важно отметить, что несмотря на то, что в Laravel имеются встроенные средства защиты от SQL-инъекций, такие как оборачивание имен столбцов, некоторые базы данных могут оставаться уязвимыми из-за ограничений или конфигураций.

Всегда проверяйте пользовательский ввод для таких ситуаций, как показано ниже:

use App\Models\User;

$request->validate([
    'sortBy' => Rule::in(['price', 'updated_at']),
]);

User::query()
    ->orderBy($request->validated()['sortBy'])
    ->get();

Правила валидации подверженные SQL-инъекциям

Некоторые механизмы валидации данных могут включать в себя возможность указания имен столбцов в базе данных. Эти правила подвержены уязвимостям SQL-инъекций, аналогично ситуации, когда SQL-инъекции направлены на имена столбцов, поскольку запросы формируются аналогичным образом.

Например, в следующем коде может существовать уязвимость:

use Illuminate\Validation\Rule;

$request->validate([
    'id' => Rule::unique('users')
                ->ignore($id, $request->input('colname'))
]);

На самом деле, этот код генерирует следующий запрос:

use App\Models\User;

$colname = $request->input('colname');

User::where($colname, $request->input('id'))
    ->where($colname, '<>', $id)
    ->count();

Поскольку имя столбца определяется пользовательским вводом, подобная ситуация эквивалентна SQL-инъекции по именам столбцов.

6
Инъекция команд

Команды оболочки, созданные на основе ввода пользователя, могут быть опасными.

Инъекции команд – это уязвимости, связанные с возможностью выполнения команд оболочки, создаваемых на основе ввода пользователей без соответствующего экранирования.

Для иллюстрации, рассмотрим следующий код, который выполняет команду whois для предоставленного пользователем доменного имени:

public function verifyDomain(Request $request)
{
    exec('whois '.$request->input('domain'));
}

Этот код подвержен уязвимостям из-за недостаточного экранирования пользовательских данных. Для исправления этой проблемы можно использовать функции PHP, такие как escapeshellcmd и/или escapeshellarg.

7
Другие инъекции

Не используйте опасные функции на ненадежных данных.

Атаки на инъекции объектов, внедрение кода через функцию eval и захват переменных с использованием функции extract включают в себя десериализацию, выполнение кода или использование функции extract на ненадежных данных, полученных от пользователя.

Некоторые примеры:

unserialize($request->input('data'));
eval($request->input('data'));
extract($request->all());

В целом, избегайте передачи любых ненадежных данных в эти опасные функции.

8
"Открытое" Перенаправление

Делайте отдельную страницу для перенаправления и предупреждайте пользователя.

Самостоятельные атаки, использующие открытое или свободное перенаправление, могут показаться не столь опасными на первый взгляд, однако они могут стать отправной точкой для атак типа “фишинг”.

Рассмотрим следующий код:

Route::get('/redirect', function (Request $request) {
    return redirect($request->input('url'));
});

Этот код осуществляет перенаправление пользователя на любой внешний URL, указанный пользовательским вводом. Это может стать уязвимостью, так как злоумышленники могут создавать видимо безопасные URL-адреса, например, https://example.com/redirect?url=http://evil.com. Такие URL-адреса могут быть использованы для фишинговых атак, например, подделки электронных писем о сбросе пароля, с целью заставить жертв отправить свои учетные данные на веб-сайт злоумышленника.