Лучшие практики безопасности ============================ Ниже мы рассмотрим основные принципы безопасности и опишем, как избежать угроз при разработке на Yii. Основные принципы ----------------- Есть два основных принципа безопасности, независимо от того, какое приложение разрабатывается: 1. Фильтрация ввода. 2. Экранирование вывода. ### Фильтрация ввода Фильтрация ввода означает, что входные данные никогда не должны считаться безопасными и вы всегда должны проверять, являются ли полученные данные допустимыми. Например, если мы знаем, что сортировка может быть осуществлена только по трём полям `title`, `created_at` и `status`, и поле может передаваться через ввод пользователем, лучше проверить значение там, где мы его получили: ```php $sortBy = $_GET['sort']; if (!in_array($sortBy, ['title', 'created_at', 'status'])) { throw new Exception('Invalid sort value.'); } ``` В Yii, вы, скорее всего, будете использовать [валидацию форм](input-validation.md), чтоб делать такие проверки. ### Экранирование вывода Экранирование вывода означает, что данные в зависимости от контекста должны экранироваться, например в контексте HTML вы должны экранировать `<`, `>` и похожие специальные символы. В контексте JavaScript или SQL будет другой набор символов. Так как ручное экранирование чревато ошибками, Yii предоставляет различные утилиты для экранирования в различных контекстах. Как избежать SQL-иньекций ------------------------- SQL-иньекции происходят, когда текст запроса формируется склеиванием не экранированных строк, как показано ниже: ```php $username = $_GET['username']; $sql = "SELECT * FROM user WHERE username = '$username'"; ``` Вместо того, чтобы подставлять корректное имя пользователя, злоумышленник может передать вам в приложение что-то вроде `'; DROP TABLE user; --`. В результате SQL будет следующий: ```sql SELECT * FROM user WHERE username = ''; DROP TABLE user; --' ``` Это валидный запрос, который сначала будет искать пользователей с пустым именем, а затем удалит таблицу `user`. Скорее всего будет сломано приложение и будут потеряны данные (вы ведь делаете регулярное резервное копирование?). Большинство запросов к базе данных в Yii происходит через [Active Record](db-active-record.md), который правильно использует подготовленные запросы PDO внутри. При использовании подготовленных запросов невозможно манипулировать запросом как это показано выше. Тем не менее, иногда нужны [сырые запросы](db-dao.md) или [построитель запросов](db-query-builder.md). В этом случае вы должны использовать безопасные способы передачи данных. Если данные используются для сравнения со значением столбцов предпочтительнее использовать подготовленные запросы: ```php // 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(); ``` Если данные используются в качестве имён столбцов или таблиц, то лучший путь - это разрешить только предопределённый набор значений: ```php function actionList($orderBy = null) { if (!in_array($orderBy, ['name', 'status'])) { throw new BadRequestHttpException('Only name and status are allowed to order by.') } // ... } ``` Если это невозможно, то имена столбцов и таблиц должны экранироваться. Yii использует специальный синтаксис для экранирования для всех поддерживаемых баз данных: ```php $sql = "SELECT COUNT([[$column]]) FROM {{table}}"; $rowCount = $connection->createCommand($sql)->queryScalar(); ``` Вы можете получить подробную информацию о синтаксисе в [Экранирование имён таблиц и столбцов](db-dao.md#quoting-table-and-column-names). Как избежать XSS ---------------- XSS или кросс-сайтинговый скриптинг становится возможен, когда не экранированный выходной HTML попадает в браузер. Например, если пользователь должен ввести своё имя, но вместо `Alexander` он вводит ``, то все страницы, которые его выводят без экранирования, будут выполнять JavaScript `alert('Hello!');`, и в результате будет выводиться окно сообщения в браузере. В зависимости от сайта, вместо невинных скриптов с выводом всплывающего hello, злоумышленниками могут быть отправлены скрипты, похищающие личные данные пользователей сайта, либо выполняющие операции от их имени. В Yii избежать XSS легко. На месте вывода текста необходимо выбрать один из двух вариантов: 1. Вы хотите вывести данные в виде обычного текста. 2. Вы хотите вывести данные в виде HTML. Если вам нужно вывести простой текст, то экранировать лучше следующим образом: ```php ``` Если нужно вывести HTML, вам лучше воспользоваться HtmlPurifier: ```php ``` Обратите внимание, что обработка с помощью HtmlPurifier довольно тяжела, так что неплохо бы задуматься о кешировании. Как избежать CSRF ----------------- CSRF - это аббревиатура для межсайтинговой подмены запросов. Идея заключается в том, что многие приложения предполагают, что запросы, приходящие от браузера, отправляются самим пользователем. Это может быть неправдой. Например, сайт `an.example.com` имеет URL `/logout`, который, используя простой GET, разлогинивает пользователя. Пока это запрос выполняется самим пользователем - всё в порядке, но в один прекрасный день злоумышленники размещают код `` на форуме с большой посещаемостью. Браузер не делает никаких отличий между запросом изображения и запросом страницы, так что когда пользователь откроет страницу с таким тегом `img`, браузер отправит GET запрос на указанный адрес, и пользователь будет разлогинен. Вот основная идея. Можно сказать, что в разлогировании пользователя нет ничего серьёзного, но с помощью этой уязвимости, можно выполнять опасные операции. Представьте, что существует страница http://an.example.com/purse/transfer?to=anotherUser&amount=2000, обращение к которой с помощью GET запроса, приводит к перечислению 2000 единиц валюты со счета авторизированного пользователя на счет пользователя с логином anotherUser. Учитывая, что браузер для загрузки контента отправляет GET запросы, можно подумать, что разрешение на выполнение такой операции только POST запросом на 100% обезопасит от проблем. К сожалению, это не совсем правда. Учитывайте, что вместо тега , злоумышленник может внедрить JavaScript код, который будет отправлять нужные POST запросы на этот URL. Для того, чтоб избежать CSRF вы должны всегда: 1. Следуйте спецификациям HTTP, например запрос GET не должен менять состояние приложения. 2. Держите защиту CSRF в Yii включенной. Как избежать нежелательного доступа к файлам -------------------------------------------- По умолчанию, webroot сервера указывает на каталог `web`, где лежит `index.php`. В случае использования виртуального хостинга, это может быть недостижимо, в конечном итоге весь код, конфиги и логи могут оказаться в webroot сервера. Если это так, то нужно запретить доступ ко всему, кроме директории `web`. Если на вашем хостинге такое невозможно, рассмотрите возможность смены хостинга. Как избежать отладочной информации и утилит в продуктиве -------------------------------------------------------- В режиме отладки, Yii отображает довольно подробные ошибки, которые полезны во время разработки. Дело в том, что подробные ошибки удобны для нападающего, так как могут раскрыть структуру базы данных, параметров конфигурации и части вашего кода. Никогда не запускайте продуктивное приложение с `YII_DEBUG` установленным в `true` в вашем `index.php`. Вы никогда не должны включать Gii на продуктиве. Он может быть использован для получения информации о структуре базы данных, кода и может позволить заменить файлы, генерируемые Gii автоматически. Также следует избегать включения на продуктиве панели отладки, если только в этом нет острой необходимости. Она раскрывает всё приложение и детали конфигурации. Если вам все таки нужно запустить панель отладки на продуктиве, проверьте дважды что доступ ограничен только вашими IP-адресами.