Рекомендации по безопасности
Ниже мы рассмотрим общие принципы безопасности и опишем, как избежать угроз при разработке приложений с использованием Yii. Большинство из этих принципов не уникальны для Yii, но применимы к разработке веб-сайтов или программного обеспечения в целом, поэтому вы также найдете ссылки для дальнейшего изучения общих идей, лежащих в основе этих принципов.
Основные принципы
Существует два основных принципа, касающихся безопасности, независимо от разрабатываемого приложения:
- Фильтрация входных данных.
- Экранирование вывода.
Фильтрация входных данных
Фильтрация входных данных означает, что входные данные никогда не должны считаться безопасными, и вы всегда должны проверять, действительно ли полученное значение входит в разрешенные. Например, если мы знаем, что сортировка может выполняться по трем полям title, created_at и status и поле может быть передано через пользовательский ввод, лучше проверить полученное значение сразу при его получении. С точки зрения базового PHP, это будет выглядеть следующим образом:
$sortBy = $_GET['sort'];
if (!in_array($sortBy, ['title', 'created_at', 'status'])) {
throw new Exception('Invalid sort value.');
}
В Yii, скорее всего, вы будете использовать валидацию форм, чтобы выполнять аналогичные проверки.
Дополнительное чтение по этой теме:
- https://www.owasp.org/index.php/Data_Validation
- https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet
Экранирование вывода
Экранирование вывода означает, что в зависимости от контекста, где мы используем данные, они должны быть экранированы. То есть в контексте HTML вы должны экранировать <, > и другие специальные символы. В контексте JavaScript или SQL это будет другой набор символов. Поскольку ручное экранирование всего подвержено ошибкам, Yii предоставляет различные инструменты для выполнения экранирования в различных контекстах.
Дополнительное чтение по этой теме:
- https://www.owasp.org/index.php/Command_Injection
- https://www.owasp.org/index.php/Code_Injection
- https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
Предотвращение SQL-инъекций
SQL-инъекция происходит, когда текст запроса формируется путем конкатенации неэкранированных строк, например:
$username = $_GET['username']; $sql = "SELECT * FROM user WHERE username = '$username'";
Вместо правильного имени пользователя злоумышленник может передать вашему приложению что-то вроде '; DROP TABLE user; --. Результирующий SQL будет следующим:
SELECT * FROM user WHERE username = ''; DROP TABLE user; --'
Это допустимый запрос, который будет искать пользователей с пустым именем пользователя и затем удалит user таблицу, что, скорее всего, приведет к сбою веб-сайта и потере данных (вы создали резервные копии, верно?).
В Yii большая часть работы с базами данных выполняется через Active Record, который правильно использует подготовленные операторы PDO внутри. В случае подготовленных операторов манипулировать запросом, как было показано выше, невозможно.
Тем не менее, иногда вам нужны сырые запросы или строитель запросов. В этом случае вы должны использовать безопасные способы передачи данных. Если данные используются для значений столбцов, предпочтительнее использовать подготовленные операторы:
// query builder
$userIDs = (new Query())
->select('id')
->from('user')
->where('status=:status', [':status' => $status])
->all();
// DAO
$userIDs = $connection
->createCommand('SELECT id FROM user where status=:status')
->bindValues([':status' => $status])
->queryColumn();
Если данные используются для указания имен столбцов или имен таблиц, лучше всего разрешить только предопределенный набор значений:
function actionList($orderBy = null)
{
if (!in_array($orderBy, ['name', 'status'])) {
throw new BadRequestHttpException('Only name and status are allowed to order by.')
}
// ...
}
Если это невозможно, имена таблиц и столбцов должны быть экранированы. Yii имеет специальный синтаксис для такого экранирования, который позволяет делать это одинаково для всех поддерживаемых баз данных:
$sql = "SELECT COUNT([[$column]]) FROM {{table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();
Подробности синтаксиса можно найти в Quoting Table and Column Names.
Дополнительное чтение по этой теме:
Предотвращение XSS
XSS (межсайтовый скриптинг) происходит, когда вывод не экранируется должным образом при выводе HTML в браузер. Например, если пользователь может ввести свое имя, а вместо Alexander он вводит <script>alert('Hello!');</script>, каждая страница, которая выводит имя пользователя без экранирования, выполнит JavaScript alert('Hello!');, что приведет к появлению всплывающего окна предупреждения в браузере. В зависимости от веб-сайта, вместо безобидного предупреждения такой скрипт может отправлять сообщения от вашего имени или даже проводить банковские операции.
Предотвращение XSS довольно просто в Yii. Существует, как правило, два случая:
- Вы хотите вывести данные как простой текст.
- Вы хотите вывести данные как HTML.
Если вам нужен только простой текст, экранирование выполняется так:
<?= \yii\helpers\Html::encode($username) ?>
Если это должен быть HTML, мы можем получить помощь от HtmlPurifier:
<?= \yii\helpers\HtmlPurifier::process($description) ?>
Обратите внимание, что обработка HtmlPurifier довольно ресурсоемка, поэтому рассмотрите добавление кеширования.
Дополнительное чтение по этой теме:
Предотвращение CSRF
CSRF — это аббревиатура от межсайтовой атаки с подделкой запроса. Идея заключается в том, что многие приложения предполагают, что запросы, поступающие от браузера пользователя, отправляются самим пользователем. Это может быть false.
Например, сайт an.example.com имеет URL /logout, который при простом доступе с помощью GET, вызывает выход пользователя из системы. Пока запрос выполняется самим пользователем, все в порядке, но однажды злоумышленники каким-то образом размещают <img src="http://an.example.com/logout"> на форуме, который часто посещает пользователь. Браузер не делает различий между запросом изображения и запросом страницы, поэтому, когда пользователь открывает страницу с таким img тегом, браузер отправит GET-запрос на этот URL, и пользователь будет выведен из системы an.example.com.
Это основная идея. Можно сказать, что выход пользователя из системы — нечто серьезное, но злоумышленники могут сделать гораздо больше, используя эту идею. Представьте, что на каком-то веб-сайте есть URL http://an.example.com/purse/transfer?to=anotherUser&amount=2000. Доступ к нему с помощью GET-запроса приводит к перечислению 2000 $ с учетной записи авторизованного пользователя пользователю anotherUser. Мы знаем, что браузер всегда отправляет GET-запрос для загрузки изображения, поэтому мы можем изменить код, чтобы принять только POST-запросы на этот URL. К сожалению, это не спасет нас, потому что злоумышленник может вставить JavaScript-код вместо <img> тега, что позволяет отправлять POST-запросы на этот URL.
Для предотвращения CSRF вы всегда должны:
- Следовать спецификации HTTP, т.е. GET не должен изменять состояние приложения.
- Включить защиту CSRF Yii.
Иногда вам нужно отключить валидацию CSRF на уровне контроллера и/или действия. Это можно сделать, установив свойство:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $enableCsrfValidation = false;
public function actionIndex()
{
// CSRF validation will not be applied to this and other actions
}
}
Для отключения валидации CSRF для настраиваемых действий можно сделать так:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function beforeAction($action)
{
// ...set `$this->enableCsrfValidation` here based on some conditions...
// call parent method that will check CSRF if such property is `true`.
return parent::beforeAction($action);
}
}
Отключение валидации CSRF в standalone actions должно выполняться в методе init(). Не размещайте этот код в метод beforeRun(), так как он не будет иметь эффекта.
<?php
namespace app\components;
use yii\base\Action;
class ContactAction extends Action
{
public function init()
{
parent::init();
$this->controller->enableCsrfValidation = false;
}
public function run()
{
$model = new ContactForm();
$request = Yii::$app->request;
if ($request->referrer === 'yiipowered.com'
&& $model->load($request->post())
&& $model->validate()
) {
$model->sendEmail();
}
}
}
Предупреждение: Отключение CSRF позволит любому сайту отправлять POST-запросы на ваш сайт. В этом случае важно реализовать дополнительные проверки, такие как проверка IP-адреса или секретного токена.
Дополнительное чтение по этой теме:
Предотвращение доступа к файлам
По умолчанию веб-корень сервера должен указывать на директорию web , где находится index.php. В случае совместных хостингов это может быть невозможно, и мы получим весь код, конфигурации и логи в корне веб-сервера.
Если это так, не забудьте запретить доступ ко всему, кроме web. Если это невозможно, рассмотрите возможность размещения приложения в другом месте.
Отключение отладочной информации и инструментов в производстве
В отладочном режиме Yii показывает достаточно подробные ошибки, что, безусловно, полезно при разработке. Дело в том, что эти подробные ошибки также полезны для злоумышленников, так как могут раскрывать структуру базы данных, значения конфигурации и части вашего кода. Никогда не запускайте приложения в продакшене с YII_DEBUG установленным на true в вашей index.php.
Никогда не включайте Gii или панель отладки в продакшен. Они могут быть использованы для получения информации о структуре базы данных, коде и просто для переписывания кода с помощью кода, сгенерированного Gii.
Панель отладки следует избегать в продакшене, если это не абсолютно необходимо. Она раскрывает все возможные детали приложения и конфигурации. Если вам она действительно необходима, дважды проверьте, что доступ к ней ограничен только вашим IP-адресом.
Дополнительное чтение по этой теме:
- https://www.owasp.org/index.php/Exception_Handling
- https://www.owasp.org/index.php/Top_10_2007-Information_Leakage
Использование защищенного соединения через TLS
Yii предоставляет функции, которые полагаются на cookie и/или PHP сессии. Они могут быть уязвимы, если ваше соединение скомпрометировано. Риск снижается, если приложение использует защищенное соединение через TLS (часто упоминается как SSL).
Обратитесь к документации вашего веб-сервера за инструкциями по его настройке. Вы также можете проверить примеры конфигураций, предоставленные проектом H5BP:
Безопасная настройка сервера
Цель этого раздела — выделить риски, которые необходимо учитывать при создании конфигурации сервера для обслуживания веб-сайта на базе Yii. Помимо рассматриваемых здесь пунктов, могут существовать и другие связанные с безопасностью параметры конфигурации, поэтому не рассматривайте этот раздел как исчерпывающий.
Предотвращение атак Host-заголовка
Классы, такие как yii\web\UrlManager и yii\helpers\Url, могут использовать текущее имя запрошенного хоста для генерации ссылок. Если веб-сервер настроен на обслуживание одного сайта независимо от значения заголовка Host, эта информация может быть ненадежной и может быть подделана пользователем, отправляющим HTTP-запрос. В таких ситуациях необходимо либо исправить конфигурацию веб-сервера, чтобы обслуживать сайт только для указанных имён хостов, либо явно задать или отфильтровать значение, установив свойство hostInfo компонента приложения request.
Для получения дополнительной информации о конфигурации сервера, обратитесь к документации вашего веб-сервера:
- Apache 2: http://httpd.apache.org/docs/trunk/vhosts/examples.html#defaultallports
- Nginx: https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/
Если у вас нет доступа к конфигурации сервера, вы можете настроить фильтр yii\filters\HostControl на уровне приложения, чтобы защититься от такого рода атаки:
// Web Application configuration file
return [
'as hostControl' => [
'class' => 'yii\filters\HostControl',
'allowedHosts' => [
'example.com',
'*.example.com',
],
'fallbackHostInfo' => 'https://example.com',
],
// ...
];
Примечание: всегда предпочтительнее использовать конфигурацию веб-сервера для защиты от атак с подделкой заголовка хоста вместо использования фильтра. yii\filters\HostControl следует использовать только в случае отсутствия возможности настройки конфигурации сервера.
© 2008–2017 by Yii Software LLC
Licensed under the three clause BSD license.
http://www.yiiframework.com/doc-2.0/guide-security-best-practices.html