company_banner

Руководство по написанию защищённых PHP-приложений в 2018-м

https://paragonie.com/blog/2017/12/2018-guide-building-secure-php-software
  • Перевод
  • Tutorial

Приближается 2018 год, и технари — в частности веб-разработчики — должны отбросить многие старые методики и верования в сфере разработки защищённых PHP-приложений. Особенно это относится ко всем, кто не верит, что такие приложения вообще возможны.


Это руководство — дополнение к электронной книге PHP: The Right Way с сильным уклоном в безопасность, а не общие вопросы программирования на PHP (вроде стиля кода).


Содержание


  1. Версии PHP
  2. Управление зависимостями с помощью Composer
    • Рекомендуемые пакеты
  3. HTTPS и безопасность в браузере
    • Заголовки безопасности
    • Целостность подресурсов (Subresource Integrity)
    • Взаимосвязи документов
  4. Разработка защищённых PHP-приложений
    • Взаимодействие с базами данных
    • Загрузка файлов
    • Межсайтовый скриптинг (XSS)
    • Межсайтовая подделка запросов (CSRF)
    • XML-атаки (XXE, XPath-внедрения)
    • Десериализация и внедрение PHP-объектов
    • Хеширование паролей
    • Криптография общего назначения
  5. Особые случаи
    • Шифрование с возможностью поиска
    • Аутентификация на основе токенов без побочных каналов
    • Разработка защищённых API
    • Журналирование событий безопасности с помощью Chronicle
  6. Несколько слов от автора
  7. Источники

Версии PHP


Вкратце: ничего не поделаешь, но в 2018-м вы будете пользоваться PHP 7.2, а в начале 2019-го — планировать перейти на 7.3.


PHP 7.2 вышел 30 ноября 2017 г.


На момент написания статьи только PHP 7.1 и 7.2 активно поддерживаются разработчиками языка, а для PHP 5.6 и 7.0 ещё примерно год будут выходить патчи безопасности.


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


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


Управление зависимостями


Вкратце: используйте Composer.


Composer — это шедевральное решение по управлению зависимостями в PHP-экосистеме. В книге PHP: The Right Way целый раздел посвящён началу работы с Composer, очень рекомендуем его прочесть.


Если вы не пользуетесь Composer для управления зависимостями, то рано или поздно (надеюсь — поздно, но, скорее всего, рано) окажетесь в ситуации, когда одна из библиотек, от которой вы зависите, сильно устареет, а преступники начнут активно эксплуатировать уязвимости в старых версиях.


Важно: не забывайте обновлять свои зависимости по мере разработки ПО. К счастью, это можно сделать одной строкой:


composer update

Если вы делаете что-то особенное, требующее использования PHP-расширений (написанных на С), то вы не можете установить их с помощью Composer. Вам также потребуется PECL.


Рекомендуемые пакеты


Вне зависимости от того, что вы создаёте, наверняка эти зависимости будут вам полезны. Это в дополнение к тому, что рекомендует большинство PHP-разработчиков (PHPUnit, PHP-CS-Fixer и т. д.).


roave/security-advisories


Пакет Roave security-advisories использует репозиторий Friends of PHP, чтобы ваш проект не зависел от любых пакетов с известными уязвимостями.


composer require roave/security-advisories:dev-master

Или можете загрузить свой файл composer.lock в Sensio Labs в качестве стандартной процедуры автоматической оценки на уязвимости, чтобы получать предупреждения о любых устаревших пакетах.


vimeo/psalm


Psalm — инструмент статичного анализа, помогающий определять возможные баги в вашем коде. Хотя есть и другие хорошие инструменты (например, замечательные Phan и PHPStan), но если вам нужна поддержка PHP 5, то Psalm — один из лучших инструментов статичного анализа для PHP 5.4+.


Использовать Psalm просто:


# Version 1 doesn't exist yet, but it will one day:
composer require --dev vimeo/psalm:^0

# Only do this once:
vendor/bin/psalm --init

# Do this as often as you need:
vendor/bin/psalm

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


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


HTTPS и безопасность в браузере


Вкратце: HTTPS, который нужно тестировать, и заголовки безопасности.


В 2018-м сайтам уже будет непозволительно работать по незащищённому HTTP. К счастью, можно было бесплатно получить TLS-сертификаты и автоматически обновлять их благодаря протоколу ACME и сертификационной компании Let's Encrypt.


Интегрировать ACME в свой веб-сервер — пара пустяков.



Вы могли подумать: «Ладно, у меня есть TLS-сертификат. Теперь нужно потратить несколько часов на поиск конфигурации, чтобы сайт стал безопасным и быстрым».


Нет! Mozilla вам поможет. Для создания рекомендованных шифронаборов для своей аудитории можете использовать генератор конфигураций.


HTTPS (HTTP через TLS) совершенно безальтернативен, если хотите сделать свой сайт безопасным. Использование HTTPS моментально исключает несколько видов атак на ваших пользователей (внедрение контента «человек посередине», перехват данных, атаки повтором и манипуляции с сессиями ради подмены пользователя).


Заголовки безопасности


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


  • Content-Security-Policy
    • Заголовок обеспечивает тонкое управление разрешениями загрузки браузером внешних и внутренних ресурсов. То есть это дополнительный уровень защиты от межсайтового скриптинга.
    • Воспользуйтесь CSP-Builder для быстрого и лёгкого развёртывания/управления политиками безопасности содержимого (Content Security Policies).
    • Для более глубокого анализа для начала можете прочитать это: введение в заголовки политик безопасности содержимого.
  • Expect-CT
    • Заголовок добавляет уровень защиты от мошеннических/скомпрометированных сертификационных компаний. Заголовок принуждает публиковать доказательства недействительности сертификатов в публично проверяемой, нередактируемой структуре данных. Подробнее об Expect-CT: https://scotthelme.co.uk/a-new-security-header-expect-ct/.
    • Сначала настройте enforce, max-age=30, а когда удостоверитесь, что заголовок не мешает работе сервиса, увеличьте max-age.
  • Referrer-Policy
  • Strict-Transport-Security
    • Заголовок говорит браузеру передавать все будущие запросы к этому источнику по HTTPS, а не по небезопасному HTTP.
    • При первом развёртывании пропишите max-age=30, а потом увеличьте значение (например, до 31536000), когда будете уверены, что ничего не сломается.
  • X-Content-Type-Options
    • Путаница в MIME-типах может привести к непредсказуемым результатам, включая странные крайние ситуации с возникновением XSS-уязвимостей. Лучше всего использовать этот заголовок со стандартным Content-Type.
    • Задайте nosniff, если только вам не нужно поведение по умолчанию (например, для скачивания файлов).
  • X-Frame-Options
    • Заголовок предотвращает атаку типа «кликджекинг».
    • Задайте DENY (или SAMEORIGIN, но только если используете элементы <frame>)
  • X-XSS-Protection
    • Заголовок включает некоторые возможности браузера по защите от XSS, которые по умолчанию не включены.
    • Задайте 1; mode=block

Аналогично, если вы используете встроенные PHP-свойства управления сессиями (что рекомендуется), то, возможно, захотите вызвать session_start():


session_start([
    'cookie_httponly' => true,
    'cookie_secure' => true
]);

Тогда ваше приложение при отправке идентификационных кук будет использовать только безопасные HTTPS-флаги, что предотвратит успешные XSS-атаки с помощью кражи пользовательских кук, они будут отправляться только по HTTPS. Пару лет назад мы уже писали о безопасных PHP-сессиях.


Целостность подресурсов


Однажды в будущем вы станете работать над проектом, использующим CDN для выгрузки традиционных Javascript/CSS-фреймворков и библиотек в центральное расположение. Неудивительно, что специалисты по безопасности предсказали очевидную проблему: если много сайтов используют CDN для предоставления части своего содержимого, то взлом CDN и подмена данных позволит внедрять произвольный код на тысячи (если не миллионы) сайтов.


Поэтому придумали целостность подресурсов (subresource integrity).


Целостность подресурсов (SRI) позволяет закреплять хеш содержимого файла, которое вам должна предоставить CDN. Текущая реализация SRI позволяет использовать только криптографические хеш-функции, поэтому злоумышленники не смогут сгенерировать вредоносные версии контента, приводящие к таким же хешам, как у оригинальных файлов.


Реальный пример: Bootstrap v4-alpha использует SRI в примере кода их CDN.


<link
    rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
    integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
    crossorigin="anonymous"
/>
<script
    src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"
    integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn"
    crossorigin="anonymous"
></script>

Взаимосвязи документов


Веб-разработчики часто задают гиперссылкам атрибут target (например, target="_blank" для открытия ссылки в новом окне). Но если вы не передаёте также тэг rel="noopener", то позволите целевой странице получить контроль над исходной страницей.


Не делайте так


<a href="http://example.com" target="_blank">Click here</a>

Это позволит example.com получить контроль над текущей страницей.


Делайте так


<a href="https://example.com" target="_blank" rel="noopener noreferrer">Click here</a>

В новом окне открывается example.com, но не получает контроля над текущим окном.


Для дальнейшего изучения.


Разработка защищённых PHP-приложений


Если безопасность ПО для вас в новинку, можете начать с введения A Gentle Introduction to Application Security.


Многие специалисты по безопасности с самого начала обращают внимание разработчиков на ресурсы вроде OWASP Top 10. Но многие распространённые уязвимости можно считать особыми случаями одних и тех же высокоуровневых проблем (код/данные не разделены адекватно, ошибочная логика, небезопасная операционная среда, сломанные криптографические протоколы).


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


Взаимодействие с базами данных


Подробнее: Предотвращение SQL-внедрений в PHP-приложениях


Если вы сами пишете SQL-запросы, проверьте, что вы используете подготовленные выражения (prepared statements) и что любая предоставляемая сетью или файловой системой информация передаётся в виде параметров, а не конкатенируется в строку запроса. Также удостоверьтесь, что вы избегаете эмулированных подготовленных выражений.


Лучший выбор — EasyDB.


НЕ ДЕЛАЙТЕ ТАК:


/* Небезопасный код: */
$query = $pdo->query("SELECT * FROM users WHERE username = '" . $_GET['username'] . "'");

Делайте так:


/* Защищено от SQL-внедрений: */
$results = $easydb->row("SELECT * FROM users WHERE username = ?", $_GET['username']);

В базах данных есть и другие слои абстракций, предоставляющие эквивалентный уровень безопасности (EasyDB под капотом использует PDO, но любыми способами старается отключить эмулирование подготовленных выражений в пользу настоящих подготовленных выражений, чтобы предотвратить проблемы). Пока вводимые пользователями данные не могут влиять на структуру запросов (это относится и к хранимым процедурам) — вы в безопасности.


Загрузка файлов


Подробнее: Как безопасно разрешать пользователям загружать файлы


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


Загружаемые файлы должны иметь атрибуты «только для чтения» или «только для чтения или записи» и никогда не быть исполняемыми.


Если на вашем сайте корневая директория для документов /var/www/example.com, то не надо хранить загружаемые файлы в /var/www/example.com/uploaded_files.


Лучше хранить их в отдельной директории, к которой нет прямого доступа (например, /var/www/example.com-uploaded/), чтобы они случайно не выполнялись как серверные скрипты и не открывали дверь удалённому исполнению кода.


Более чистое решение — переместить свою корневую директорию для файлов на один уровень вниз (т. е. в /var/www/example.com/public).


Другая проблема загружаемых файлов связана с их безопасным скачиванием.


  • SVG-изображения при прямом обращении исполняют в пользовательском браузере JavaScript-код. Это несмотря на вводящий в заблуждение префикс image/ в MIME-типе.
  • Сниффинг MIME-типа может привести к атакам посредством путаницы типов, как было описано выше. См. X-Content-Type-Options.
  • Если вы не последовали предыдущему совету относительно безопасного хранения загруженных файлов, то злоумышленник, пытающийся загрузить файл .php или .phtml, может исполнить произвольный код, обратившись к файлу напрямую в браузере, и получить полный контроль над сервером. Будьте осторожны.

Межсайтовый скриптинг (XSS)


Подробнее: Всё, что вам нужно знать о предотвращении межсайтового скриптинга в PHP


В идеальном мире предотвратить XSS было бы так же легко, как и SQL-внедрение. У нас был бы простой в использовании API для отделения структуры документа от его содержимого.


К сожалению, в реальном мире большинство веб-разработчиков генерируют длинный HTML и отправляют его в HTTP-ответе. Это характерно не только для PHP, просто таковы реалии веб-разработки.


Закрытие XSS-уязвимостей — задача вполне решаемая. Однако содержимое раздела о браузерной безопасности неожиданно обретает большую важность. Вкратце:


  1. Всегда экранируйте на выходе, никогда на входе. Если в базе данных вы храните очищенные данные, потом где-то обнаруживается SQL-уязвимость, атакующий может полностью обойти вашу XSS-защиту, просто «загрязнив» зловредным кодом вроде бы чистые записи.
  2. Если у вашего фреймворка есть движок шаблонов, предлагающий автоматическую контекстную фильтрацию, то воспользуйтесь ею. Ваш фреймворк будет работать безопаснее.
  3. echo htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); — это безопасный и эффективный способ остановить все XSS-атаки на страницу, использующую UTF-8, но при этом весь HTML будет запрещён.
  4. Если вам требуется использовать Markdown вместо HTML, не выбирайте HTML.
  5. Если вам нужно разрешить какой-то HTML и вы не пользуетесь движком шаблонов (см. пункт 1), применяйте HTML Purifier. HTML Purifier не подходит для экранирования контекста HTML-атрибутов.

Подделка межсайтовых запросов (CSRF)


Подделка межсайтовых запросов — это разновидность атаки с подменой делегата: можно обмануть пользовательский браузер и заставить его выполнить вредоносный HTTP-запрос с повышенными пользовательскими привилегиями.


В целом эта проблема легко решается:


  1. Используйте HTTPS. Это необходимое условие.
  2. Добавьте базовую аутентификацию типа «вопрос-ответ».
    • Добавьте в каждую форму скрытый атрибут.
    • Генерируйте криптографически безопасные случайные значения (токены).
    • Проверяйте предоставляемые скрытые атрибуты форм и сверяйтесь с ожидаемыми значениями.

Мы написали библиотеку Anti-CSRF, которая идёт ещё дальше:


  • Каждый токен можно сделать одноразовым, чтобы предотвратить атаки повтором.
    • Токены хранятся на бэкенде.
    • Токены ротируются по мере востребованности, сначала идут более старые.
  • Каждый токен можно привязать к конкретному URI.
    • Если происходит утечка токена, он не может использоваться в другом контексте.
  • При желании токены могут быть ограничены конкретным IP-адресом.
  • С версии v2.1 токены можно использовать многократно (например, для AJAX-вызовов).

Если ваш фреймворк не заботится о CSRF-уязвимостях, то применяйте Anti-CSRF.


В скором будущем куки SameSite позволят прекращать CSRF-атаки с гораздо меньшими усилиями.


XML-атаки (XXE, XPath-внедрения)


Есть две основные уязвимости, проявляющиеся в приложениях, которые много обрабатывают XML:


  1. Внешние сущности XML (XXE).
  2. XPath-внедрения.

XXE-атаки, помимо прочего, могут использоваться как стартовая площадка для эксплойтов локального/удалённого внедрения файлов.


Ранние версии Google Docs были уязвимы к XXE-атакам, но они мало известны за пределами бизнес-приложений, обрабатывающих большие объёмы XML.


Главное, что нужно сделать для защиты от XXE-атак:


libxml_disable_entity_loader(true);

XPath-внедрение очень похоже на SQL-внедрение, только здесь речь идёт об XML-документах.


К счастью, в PHP-экосистеме редко возникают ситуации, когда вводимые пользователями данные передаются в XPath-запросе.


К сожалению, это также означает, что лучшее доступное решение (для заранее скомпилированных и параметризованных XPath-запросов) в PHP-экосистеме отсутствует.


Рекомендуем использовать белые списки разрешённых символов для любых данных, имеющих отношение к XPath-запросам.


<?php
declare(strict_types=1);

class SafeXPathEscaper
{
    /**
     * @param string $input
     * @return string
     */
    public static function allowAlphaNumeric(string $input): string
    {
        return \preg_replace('#[^A-Za-z0-9]#', '', $input);
    }

    /**
     * @param string $input
     * @return string
     */
    public static function allowNumeric(string $input): string
    {
        return \preg_replace('#[^0-9]#', '', $input);
    }
}

// Usage:
$selected = $xml->xpath(
    "/user/username/" . SafeXPathEscaper::allowAlphaNumeric(
        $_GET['username']
    )
);

Белые списки безопаснее чёрных.


Десериализация и внедрение PHP-объектов


Подробнее: Безопасная (де)сериализация в PHP


Если вы передаёте в unserialize() недоверенные данные, то напрашиваетесь на два варианта развития событий:


  1. Внедрение PHP-объекта, который можно использовать для запуска POP-цепочки и срабатывания других уязвимостей из неправильно используемых объектов.
  2. Повреждение памяти в самом интерпретаторе PHP.

Многие разработчики предпочитают использовать вместо этого JSON-сериализацию, что является заметным улучшением безопасности ПО. Но имейте в виду, что json_decode() уязвима для DDoS-атак посредством хеш-коллизий. К сожалению, полное решение проблемы хеш-DOS в PHP ещё предстоит найти.


Полностью защититься от этих атак поможет мигрирование с djb33 на Siphash с назначением 1 в качестве старшего бита для хеша строкового входного значения, 0 для целочисленного и с заранее запрошенным ключом, его предоставит CSPRNG.


К сожалению, создатели PHP не готовы частично пожертвовать производительностью, которой они добились в PHP 7, поэтому трудно убедить их отказаться от djb33 (очень быстрого, но небезопасного) в пользу SipHash (тоже быстрого, хотя и не как djb33, но куда более безопасного). Значительное снижение производительности может даже помешать разработке будущих версий, что не пойдёт на пользу безопасности.


Поэтому лучше поступать так:


  • Использовать JSON, это безопаснее unserialize().
  • Там, где возможно, аутентифицируйте входные данные, прежде чем десериализовать их.
    • Для данных, которые вы предоставляете конечным пользователям, выбирайте sodium_crypto_auth() и sodium_crypto_auth_verify() с секретным ключом, известным только серверу.
    • Для данных, предоставляемых третьей стороной, подписывайте JSON-сообщения с помощью sodium_crypto_sign(), а затем проверяйте с помощью sodium_crypto_sign_open() и стороннего публичного ключа.
    • Также можете использовать для записи отдельный API, если для передачи вам нужно шестнадцатеричное или base64-кодирование подписей.
  • Там, где нет возможности аутентифицировать JSON-строки, используйте строгое рейт лимиты и блокируйте IP-адреса для защиты от атаки повторением.

Хеширование паролей


Подробнее: Как в 2016-м безопасно хранить пользовательские пароли
Безопасное хранилище паролей раньше было темой активной дискуссии, но сегодня его просто реализовать, особенно в PHP:


$hash = \password_hash($password, PASSWORD_DEFAULT);

if (\password_verify($password, $hash)) {
    // Authenticated.
    if (\password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        // Rehash, update database.
    }
}

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


Что бы вы ни делали, не делайте так, как WordPress.


Если интересно: с PHP 5.5 по 7.2 алгоритмом по умолчанию является bcrypt. В будущем его могут заменить Argon2, победителем в Соревновании по хешированию паролей.


Если до этого вы не использовали API password_* и вам нужно мигрировать легаси-хеши, то сделайте это именно так. Многие компании, например Yahoo, поступили неправильно. Похоже, недавно причиной бага с iamroot у Apple стала некорректная реализация обновления легаси-хешей.


Криптография общего назначения


Мы много писали на эту тему:



Для криптографии на уровне приложения всегда выбирайте библиотеку Sodium (libsodium). Если вам нужно поддерживать PHP ниже 7.2 (вплоть до 5.2.4), можете использовать sodium_compat и притвориться, что пользователи тоже применяют 7.2.


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


Особые случаи


Вы получили представление, как в 2018-м нужно создавать защищённые PHP-приложения. Давайте теперь рассмотрим некоторые специфические случаи.


Шифрование с возможностью поиска


Подробнее: Building Searchable Encrypted Databases with PHP and SQL


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


  1. Проектируем такую архитектуру, чтобы компрометация базы данных не позволила атакующему получить доступ к ключам шифрования.
  2. Шифруем данные одним секретным ключом.
  3. Создаём многочисленные индексы (с собственными различными секретными ключами) на основе HMAC или безопасного KDF со статической солью (например, Argon2).
  4. По желанию: усекаем выходные данные шага 3, используем их в качестве Bloom-фильтра.
  5. Используем выходные данные шагов 3 или 4 в запросах SELECT.
  6. Расшифровываем результат.

На любом шаге вы можете идти на компромиссы в зависимости от того, что в вашем случае оправдано.


Аутентификация на основе токенов без побочных каналов


Подробнее: Сплит-токены: протоколы аутентификации на основе токенов без побочных каналов


Если говорить о базах данных (предыдущий раздел): вы знали, что запросы SELECT теоретически могут быть источником утечек информации о тайминге?


Простые меры защиты:


  1. Режьте свои токены аутентификации пополам.
  2. Одну половину используйте в запросах SELECT.
  3. Вторую половину проверяйте за фиксированное время (constant-time).
    • При желании можете хранить в БД вместо второй половины токена её хеш. Это имеет смысл для одноразовых токенов, например для сброса паролей или для «запоминания меня на этом компьютере».

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


Разработка защищённых API


Подробнее: С помощью Sapient закаливаем ваши PHP API


Мы написали SAPIENT, Secure API ENgineering Toolkit, чтобы упростить задачу межсерверного аутентификационного обмена сообщениями. Sapient позволяет шифровать и/или аутентифицировать сообщения с помощью шифрования на основе общего (shared) или публичного ключа в дополнение к средствам безопасности HTTPS.


Это позволяет с помощью Ed25519 аутентифицировать и отвечать на API-запросы или шифровать сообщения для целевого сервера, которые можно расшифровать лишь с помощью секретного ключа на принимающем сервере, даже несмотря на атаку «человек посередине» в сочетании с фальшивой/скомпрометированной сертификационной организацией.


Поскольку тело каждого HTTP-сообщения аутентифицируется с помощью безопасного шифрования, его можно использовать вместо протоколов, оперирующих токенами с проверкой состояния (например, Oauth). Но если говорить о самом шифровании, то прежде чем делать что-то нестандартное, всегда нужно быть уверенными в том, что выбранный вами алгоритм проанализирован специалистами.


Вся используемая в Sapient криптография предоставлена шифровальной библиотекой Sodium.


Дополнительно почитать:



Paragon Initiative Enterprises уже использует Sapient во многих своих продуктах (включая open source проекты) и продолжит расширять портфолио пользователей Sapient.


Безопасное журналирование событий с помощью Chronicle


Подробнее: Chronicle заставит задуматься, нужна ли вам технология блокчейна


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


Помимо более изощрённых способов применения такого журнала, Chronicle превосходно себя проявляет в SIEM, поскольку вы можете отправлять важные с точки зрения безопасности события в личный журнал, после чего они становятся неизменяемыми.


Если ваш Chronicle настроен на перекрёстную запись (cross-sign) суммарного хеша в другие экземпляры Chronicle и/или если есть другие экземпляры, сконфигурированные на репликацию содержимого вашего Chronicle, то атакующему будет крайне сложно подделать ваши журналы событий безопасности.


С помощью Chronicle вы получите надёжность блокчейна без распространённых проблем с приватностью, производительностью или масштабируемостью.


Для публикации данных в локальный Chronicle можно использовать любой API, совместимый с Sapient, но самое простое решение — Quill.


Несколько слов от автора


Проницательный читатель мог заметить, что мы много ссылаемся на собственные работы (статьи и open source проекты), но мы ссылаемся не только на свои работы.


Это неслучайно.


Наша компания с самого основания в начале 2015-го пишет библиотеки для обеспечения безопасности и участвует в повышении защищённости экосистемы PHP.


Мы много путешествуем, и наш инженер по безопасности (чьи недавние усилия по использованию более сильной криптографии в ядре PHP только что отразились в PHP 7.2), по его собственному признанию, не слишком силён в генерировании хайпа или интереса к своей работе. Наверняка вы не слышали и о половине инструментов или библиотек, созданных нами за эти годы.


Но мы не можем стать пионерами во всех направлениях, поэтому везде, где это возможно, связываемся с экспертами индустрии, которые, как нам кажется, больше ориентируются на общественное благо, чем на мелкий эгоизм. Поэтому большая часть раздела, посвящённого безопасности в браузере, снабжена ссылками на работы Скотта Хелме (Scott Helme) и компании. Он вложил много сил в то, чтобы эти новые возможности по обеспечению безопасности стали доступны и понятны разработчикам.


Конечно, это не исчерпывающее руководство. Существует почти столько же способов писать небезопасный код, сколько способов самого написания кода. Безопасность — это больше мышление, чем цель. Мы надеемся, что с помощью всего сказанного и приведённых ниже источников разработчики с сегодняшнего дня смогут создавать защищённые PHP-приложения.


Источники


Если вы уже изучили всё предложенное и хотите больше, почитайте курируемый нами список по изучению безопасности приложений.


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


Если вы работаете в компании, которая заинтересована в оценке соответствия требованиям (PCI-DSS, ISO 27001 и т. д.), можете нанять нас для аудита своего исходного кода. Мы работаем гораздо более дружественно к разработчикам, чем другие консультанты по безопасности.


Ниже — список источников от PHP-сообщества и сообщества по информационной безопасности.


  • PHP: The Right Way — бесплатное руководство по современной PHP-разработке.
  • Генератор SSL-конфигураций Mozilla.
  • Let's Encrypt — сертификационная компания, бесплатно предоставляющая TLS-сертификаты ради повышения безопасности интернета.
  • Qualys SSL Labs предоставляет простой тестовый набор для TLS-конфигураций. Очень многие используют его для отладки своих наборов шифров и решения проблем с сертификатами, и не просто так: инструмент работает отлично.
  • Security Headers умеет проверять, насколько хорош ваш сайт с точки зрения использования браузерных средств безопасности для защиты пользователей.
  • Report-URI — замечательный бесплатный ресурс, поддерживающий инициативы по внедрению заголовков безопасности. Вам даётся Report-URI, который вы можете передавать браузерам пользователей, а те будут жаловаться Report-URI, если что-то сломается или обнаружится вектор XSS-атаки. Report-URI собирает все эти ошибки и помогает лучше отлаживать и сортировать статистику.
  • The PHP Security Advent Calendar, созданный разработчиками RIPSTech.
  • Snuffleupagus — PHP-модуль для улучшения безопасности приложений (и духовный наследник практически заброшенного Suhosin).
  • PHP Delusions — сайт, посвящённый улучшению использования PHP. Многое высказано очень категорично, но из-за стремления автора к технической точности и ясности почитать стоит. Особенно тем, кто не очень хорошо разбирается во многих функциях PDO.
  • Have I Been Pwned? помогает пользователям узнать, оказались ли их данные среди ранее украденной информации.
Mail.Ru Group 991,97
Строим Интернет
Поделиться публикацией
Похожие публикации
Комментарии 38
  • 0
    Взаимодействие с базами данных
    Ээх, сейчас бы в 2017 году о SQL-инъекциях рассказывать
    • 0
      для некоторых это актуально
      • +1
        Предлагаете везде монструозные ORM использовать?

        *Не имею ничего против ORM*
        • +1
          Подготовленных выражений будет достаточно
          • 0
            Нет, я в целом о уязвимости. Ей уже так много лет что слабо верится в то что разработчик который хоть чуть-чуть волнуется о безопасности своего приложения не знает о инъекциях.
            • +2
              Если разработчик знает все необходимое, зачем он читает туториалы?
              • 0
                Разработчик — это не только и не столько компания, сколько конкретные студенты в ней работающие
                • 0
                  Я о конкретных людях. Вряд ли какая-то организация заставляет кого-то читать хабр по долгу службы (кроме модераторов, конечно).
          • +3
            Читаю всякие рассылки уязвимостей. Один-два раза в месяц приходит что-нибудь из wordpress. У них до сих пор встречаются sql-инъекции. Добро пожаловать в будущее.
            • 0
              Вы не поверите…
              Просматривая вопросы от новичков по PHP+MYSQL на RUstackoverflow — можно почти в каждом вопросе увидеть прямые вставки переменных в запрос. Что самое смешное, при этом используется mysqli или PDO (как-будто людям сказали, что расширение mysql небезопасно, но не сказали почему). Так что увы, но тема, похоже, вечная.
              • +1
                как-будто людям сказали, что расширение mysql небезопасно, но не сказали почему

                Людям сначала сказали, что оно deprecated, а теперь, что его вообще нет (самостоятельную сборку и прочие бубны опустим для ясности). 98,58551% (субъективно, по свежему опыту) портирования с mysql на mysqli вообще тупо в добавлении одной буквы заключается.

            • +3

              К загрузке файлов: если проверяете тип файла (чаще надо, чем не надо), не доверяйте ни $_FILES['userfile']['type'], ни, тем более, расширению в $_FILES['userfile']['name'], всегда используйте mime_content_type.

              • +1

                Да и это не 100% гарантия того что файл имеет нужный нам тип т.к под капотом используется libmagic, которая проверяет только сигнатуры в хедерах файла.
                Тем не менее как первый уровень валидации это хороший совет. И что интересно, в фреймворках(и на других ЯП) зачастую используется именно Content-Type приходящий с формой, а он вообще может быть какой угодно.

                • 0
                  Ну да, проверка по сигнатурам. Но в целях безопасности особо проверить больше нечем, если не писать/использовать полноценные анализаторы форматов без использования стандартных библиотек, для выявления явных несоответствий.
              • 0
                в 2018-м вы будете пользоваться PHP 7.2
                На этом пункте bitrix-разработчики начали плакать.
                • +1
                  они начали плакать на каждом из этих пунктов
                  • +4
                    Они начали плакать ещё при чтении заголовка.
                    • +1
                      Они начали плакать когда вышел bitrix.
                      • –1
                        Полагаю, они начали плакать сразу как родились.
                • +1
                  «Но имейте в виду, что json_decode() уязвима для DDoS-атак посредством хеш-коллизий.»
                  получается, что с json_decode с параметром assoc=true такой проблемы нет, верно?
                  мне гораздо удобней работать с массивом, чем с объектом
                  • 0
                    Дело вкуса, и как я помню массивы копируются, а объекты передаются по ссылке.
                    Поправьте, если я не прав
                    • 0

                      если только читать из массива то разницы не будет. Т. е. в php копия массива будет только когда потребуется его изменение.

                      • +1

                        copy-on-write Семантически они копируются всегда, передаются по значению, но физически копирование происходит при первой попытке изменения.

                      • 0

                        Почему нет, есть. Массив или объект — нет никакой разницы, объекты хранят значения свойств в той же хеш-таблице. А проблема то и кроется в разрешении коллизий hash таблицы. Зная алгоритм хеша можно подобрать ключи так, что все элементы будут хешироваться в одно и тоже значение, а значит попадут в один и тот же список. А так как при вставке проверяется наличие ключа в массиве, то придется пройтись по всем элементам списка сравнивая ключи. Когда мы десериализуем json — вставляем значения в хеш-таблицу одно за одним, и каждое последующее занимает на единицу времени больше, т.е. эдакая арифметическая прогрессия. Зависимость времени вставки от числа элементов получается квадратичной.

                        • 0
                          Примеры ключей отсюда
                          Если скормить вашему API огромный JSON вида
                          {"Rz2VG":1,"Rz34h":2,"Rz35G":3,"RAAcd":4...}

                          то после десериализации такой объект/массив будет работать в ~500 раз медленнее обычного (такого же размера), т.к. индексация полей/ключей не работает.
                        • +1
                          Очень содержательная статья, редко столько информации в таком темпе выдают на хабре. Спасибо!
                          • 0
                            Не знал, что в svg можно засунуть javascript.
                            • 0

                              Люблю, когда люди поучают использованию TLS не имея такового у себя на сайте.
                              https://www.phptherightway.com/
                              Но статья полезная вне сомнения.

                              • 0
                                • –2
                                  phpBB из самописной лапши в версиях 3.0.х в версиях 3.1. и выше обновляется до компонентов Symphony и прочего относительно современного фарша под управлением Composer. Результат- на 3.0 полторы уязвимости, для эксплуатации одной нужно быть админом и включить флеш, вторая позволяет несанкционированно изменить настройки личных сообщений. На 3.1 и выше- куча дырок и багфиксы к багфиксам.
                                  Это я к тому, что не всегда следование моде и работа со сторонними библиотеками оказывается лучше старого, проверенного лапшекода.
                                  • +1
                                    echo htmlentities($string); — это безопасный и эффективный способ остановить все XSS-атаки на страницу


                                    Не все. Если, например, на странице «О себе» есть параметр «Мой Сайт», а пользователь контролирует вставляемую ссылку, то нарушителю достаточно указать javascript:alert() для реализации XSS по клику.
                                    Тут нет двойных кавычек, треугольных скобок, так что функция htmlentities() ничего не отрежет.

                                    Итого: санитайзить надо с учетом контекста.
                                    • +1
                                      Хотя применение HTTPS на вашем сервере даёт много преимуществ по безопасности и производительности ...

                                      О каких таких преимуществах по производительности идёт речь?
                                      • 0
                                        HTTP/2 работает только поверх HTTPS.
                                        • 0

                                          Заблуждение в общем случае.

                                          • 0
                                            Просто факт. Я прекрасно знаю и про стандарт, и про поддержку в Edge, но всем плевать, пока её нету в хроме с Nginx. А её там принципиально не будет.
                                            • 0

                                              В nginx она как раз есть. listen 127.0.0.1 http2; (без ssl) обеспечивает поддержку cleartext HTTP2 (переменная $http2 принимает значение "h2c").

                                      • 0
                                        Шифрование с возможностью поиска
                                        Подробнее: Building Searchable Encrypted Databases with PHP and SQL

                                        На мой взгляд очень спорный подход. В начале идет речь, что использовать AES через openssl плохо, т.к. зашифрованный текст всегда одинаков для одинаковых исходных текстов.
                                        Но при этом для поиска нужен хеш (например, HMAC с другим ключом), который точно также будет выдавать одинаковые хеши. А если нужен поиск по подстрокам — хеши нужны для каждой искомой подстроки.
                                        Мне видится костыльным решение с шифрованием и поиском на стороне php, что логичнее пользоваться средствами шифрования, встроенными в БД для реализации поиска. Но в mysql это будет тот же AES с повторами шифротекстов :(

                                        Сколько пытаюсь изучить вопрос с хранением ключей, ни одна статья не раскрывает этот вопрос. Особенно когда БД и php на одном сервере — увел ключи и все костыли с шифрованием в топку. Как защитить 1 ключ, которым зашифрована вся БД?
                                        • 0

                                          Как минимум не хранить их на физических дисках этого сервера, получая их извне только в память на минимально необходимое время. Это защитит от атак, связанных с получением read доступа к физическим дискам.

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

                                        Самое читаемое