8 правил для разработки безопасных PHP-приложений






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

PHP Security


Проверка входящих данных

Конечно же, в первую очередь любое приложение должно уметь обходиться с «плохими» входящими данными. Здесь правило большого пальца гласит: никогда не доверяйте тому, что получаете от пользователя. Конечно же, пользователи ваших приложений — хорошие люди, но, как минимум, глупо надеяться на то, что среди них рано или поздно не появится злоумышленник, пожелающий проверить стойкость вашего кода.

Всегда проверяйте данные на стороне сервера, даже если подобные проверки выполняются в клиентском JavaScript-коде. Где гарантия того, что злоумышленник будет пользоваться браузером с включённым JavaScript, да и вообще браузером? Проверка на стороне клиента — это, несомненно, правильно и необходимо, но слепо доверять данным, пришедшим «с улицы», никогда не стоит.

Защита от XSS атак

Cross-site scripting (XSS) атака основана на внедрении клиентского кода в уязвимые страницы приложения. У самой же уязвимости ноги растут как правило из того же места: отсутствия проверки пользовательских данных.

Например, на сайте есть форма, позволяющая пользователям отправлять комментарии, которые потом выводятся на странице. Если какой-нибудь пользователь введёт в форме  JavaScript-код, который не будет отфильтрован и выведен «как есть», то этот код будет интерпретирован браузером. А тут уж простор для деятельности воистину гигантских масштабов: начиная от простого редиректа посетителей на нужные URL, заканчивая организацией целых DoS-атак такими редиректами.

Вывод: всегда фильтруйте пользовательский ввод функциями, подобными strip_tags(), а перед отдачей контента клиенту, пропускайте через htmlentities() или аналогичные.

Защита от CSRF атак

В случае Cross Site Request Forgery (CSRF) атаки злоумышленники используют уязвимости в коде и доверие сайта к пользователю для того, чтобы выполнять какие-либо действия без ведома самого пользователя. Такие атаки чаще всего успешно работают с приложениями, у которых неправильно спроектирована бизнес-логика обработки GET-запросов.

Согласно снандарту HTTP, GET-запросы являются идемпотентными, то есть, не изменяющими содержимое. Иными словами, приложение не должно изменять какие бы то ни были данные на стороне сервера в ответ на GET-запрос. Вопреки этому правилу, многие разработчики проектируют приложения с точностью да наоборот. Например, есть фрагмент серверной логики:

Теперь представим, что Боб, выполняющий атаку банковского сайта, где расположен счёт Элис, отправляет ей email или иным образом подсовывает следующую ссылку:

Если Элис в данный момент имеет незавершённую сессию на bank.com, и перейдёт по данной ссылке, то логика, приведённая выше, сработает, поскольку приложение считает, что GET-запрос выполняется от имени Элис.

Можно пойти ещё дальше, даже не заставляя Элис кликать по ссылкам. Достаточно будет лишь загрузить страницу со следующим кодом:

Естественно, никакой картинки браузер не выведет, однако GET-запрос к bank.com будет выполнен, а больше и не нужно.

Исходя из сказанного, можно сделать следующий очевидный вывод: ваше приложение должно быть спроектировано таким образом, чтобы только POST-запросы могли изменять данные, а то время как GET должны использоваться исключительно для получения данных. Ещё одним очевидным моментом является то, что вместо глобальной переменной $_REQUEST необходимо использовать $_POST и $_GET.

Также очень хорошей практикой считается добавление т. н. CSRF-токена ко всем формам, отправляемых клиентам. Всякий раз, когда приложение отправляет форму клиенту, в скрытом поле добавляется уникальный токен, связанный с отправленной формой и учётной записью пользователя. При получении данных в POST-запросе от клиента, токен анализируется и только, если он корректен, выполняется дальнейшая обработка данных.

Защита от SQL-инъекций

Когда выполняете запросы к БД, всегда пользуйтесь PDO или производными классами, а также параметризованными запросами. Например:

В приведённом примере прежде чем выполнить запрос, происходит чистка (escaping) параметров :name и :age от возможных вставок SQL-инструкций. Таким образом исключается возможность выполнения посторонних SQL-команд, которые могут прибыть снаружи в виде GET или POST параметров.

Защита файловой системы

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

Используя подобный подход вы просто-напросто предоставляете доступ к части файловой системы сервера, а в особо тяжёлых случаях конфигурации PHP — и ко всей ФС.

Защита сессионных данных

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

Если уж у вас совсем нет желания или времени придумать лучший способ, то хотя бы надёжно шифруйте сессионные данные или найдите более надёжное место для их хранения при помощи session_set_save_handler().  К тому же, начиная с PHP 5.4, вы можете сделать всё по-человечески, используя интерфейс SessionHandlerInterface.

Обработка ошибок и исключений

Хорошо, конечно, если вы настолько опытны, что можете предусмотреть все возможные ошибки и исключительные ситуации, которые могут возникнуть в вашем приложений. Хотя, говоря откровенно, так не бывает, если ваше приложение хоть чуть сложнее «Hello world». Ещё одним золотым правилом каждого разработчика должна стать привычка использовать разное окружение при разработке приложений и при их работе в продакшне.

Поскольку сообщения об ошибках, видимые клиентам, могут стать хорошей отправной точной для поиска уязвимостей злоумышленниками, хорошей практикой является отключение display_errors и display_start_up_errors, в то время как error_reporting и log_errors лучше оставить включёнными, чтобы иметь доступ к информации о потенциальных проблемах в вашем продукте.

Чтобы вмешаться в стандартный обработчик ошибок в PHP, можно пользоваться встроенной функцией set_error_handler(), хотя и с ограничениями: она не может перехватывать ошибки типов E_CORE_ERROR, E_STRICT и E_COMPILER_ERROR в том же файле, где объявлен перехватчик. Кроме того, сам перехватчик не может получить доступа к ошибкам, возникающим внутри него самого.

Отличной практикой обработки исключительных ситуаций является использование механизма исключений: создания иерархий исключений на базе класса Exception и корректной их обработки в блоках try..catch.

Защита подключаемых файлов

PHP-сценарии часто включают в себя другие сценарии, используя инструкции include и require. У некоторых разработчиков есть привычка заканчивать имена включаемых файлов суффиксом '.inc' или чем-то подобным, отличающимся от '.php'. Опасность здесь заключается в том, что веб-сервер может быть настроен таким образом, что не будет рассматривать .inc-файлы в качестве PHP-сценариев, а вместо этого отдавать их как text/plain.  Таким вот нехитрым образом кто угодно сможет получить доступ к исходным кодам вашего приложения а то и файлам конфигурации с логинами/паролями подключения к БД или ещё чего похуже. Чем это грозит в случае чего, думаю, объяснять не стоит.

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

Источник




8 правил для разработки безопасных PHP-приложений: 1 комментарий

  1. Кроме последнего пункта все применимо и к прочим языкам web-разработки...

Комментарии запрещены.