Соображения по безопасности
Веб-приложения подвержены множеству потенциальных проблем с безопасностью, и сложно добиться идеальной безопасности или даже понять, что значит «идеальная» безопасность в целом. Flask пытается решить некоторые из этих проблем по умолчанию, но есть и другие аспекты, о которых вам нужно позаботиться самостоятельно. Многие из этих решений представляют собой компромиссы и зависят от конкретных потребностей и модели угроз каждого приложения. Многие хостинговые платформы могут обрабатывать определенные типы проблем без необходимости обработки их Flask-приложением.
Использование ресурсов
Распространенной категорией атак является «Отказ в обслуживании» (DoS или DDoS). Это очень обширная категория, и различные варианты нацелены на разные уровни развернутого приложения. В общем, что-то делается для увеличения времени обработки или использования памяти для обработки каждого запроса до такой степени, что ресурсов становится недостаточно для обработки законных запросов.
Flask предоставляет несколько параметров конфигурации для обработки использования ресурсов. Их также можно задавать для отдельных запросов, чтобы настроить только этот запрос. Документация по каждому из них содержит более подробные сведения.
-
MAX_CONTENT_LENGTHилиRequest.max_content_lengthуправляет объёмом данных, которые будут считываться из запроса. По умолчанию он не установлен, хотя он всё равно будет блокировать действительно неограниченные потоки, если WSGI-сервер не указывает поддержку. -
MAX_FORM_MEMORY_SIZEилиRequest.max_form_memory_sizeуправляет размером любого не-файловогоmultipart/form-dataполя. По умолчанию он установлен в 500 КБ. -
MAX_FORM_PARTSилиRequest.max_form_partsуправляет количествомmultipart/form-dataполей, которые могут быть обработаны. По умолчанию он установлен в 1000. В сочетании с настройкамиmax_form_memory_size, это означает, что форма будет занимать не более 500 МБ памяти.
Независимо от этих настроек, следует также проверить доступные параметры от вашей операционной системы, развертывания в контейнере (Docker и т. д.), WSGI-сервера, HTTP-сервера и хостинговой платформы. Они, как правило, предоставляют способы установки лимитов ресурсов процесса, таймаутов и других проверок независимо от того, как настроена Flask.
Межсайтовый скриптинг (XSS)
Межсайтовый скриптинг – это концепция внедрения произвольного HTML (и вместе с ним JavaScript) в контекст веб-сайта. Для решения этой проблемы разработчики должны правильно экранировать текст, чтобы он не содержал произвольных HTML-тегов. Более подробную информацию можно найти в статье Википедии о Межсайтовом скриптинге.
Flask настраивает Jinja2 для автоматической экранизации всех значений, если явно не указано обратное. Это должно исключить все проблемы XSS, возникающие в шаблонах, но всё равно нужно быть осторожным в других местах:
- генерация HTML без использования Jinja2
- вызов
Markupна данные, отправленные пользователями - отправка HTML из загруженных файлов, никогда этого не делайте, используйте заголовок
Content-Disposition: attachmentдля предотвращения этой проблемы. - отправка текстовых файлов из загруженных файлов. Некоторые браузеры используют угадывание типа содержимого, основываясь на первых байтах, поэтому пользователи могут обмануть браузер, чтобы он выполнил HTML.
Ещё одним важным аспектом являются невыделенные атрибуты. Хотя Jinja2 может защитить вас от проблем XSS путём экранирования HTML, есть одна проблема, от которой он не защищает: XSS посредством инъекции атрибутов. Чтобы противостоять этому возможному вектору атаки, обязательно заключайте атрибуты в двойные или одинарные кавычки при использовании выражений Jinja в них:
<input value="{{ value }}">
Зачем это нужно? Потому что, если этого не делать, злоумышленник может легко ввести пользовательские обработчики JavaScript. Например, злоумышленник может ввести этот фрагмент HTML+JavaScript:
onmouseover=alert(document.cookie)
Когда пользователь затем перемещает курсор над полем ввода, куки будет отображено в окне предупреждения. Но вместо того, чтобы отобразить куки пользователю, опытный злоумышленник может также выполнить любой другой JavaScript-код. В сочетании с инъекциями CSS злоумышленник может даже заставить элемент заполнить всю страницу, так что пользователю достаточно навести курсор на любую часть страницы, чтобы инициировать атаку.
Есть один класс проблем XSS, от которых экранирование Jinja не защищает. Атрибут href тега a может содержать URI javascript:, который браузер выполнит при нажатии, если он не защищён должным образом.
<a href="{{ value }}">click here</a>
<a href="javascript:alert('unsafe');">click here</a>
Чтобы предотвратить это, вам нужно задать заголовок ответа Политика безопасности содержимого (CSP).
Межсайтовая подделка запросов (CSRF)
Ещё одной большой проблемой является CSRF. Это очень сложная тема, и я не буду подробно её описывать здесь, а лишь упомяну, что это такое и как теоретически можно предотвратить её.
Если ваша информация об авторизации хранится в куках, у вас есть неявное управление состоянием. Состояние «быть авторизованным» контролируется кукой, и эта кука отправляется с каждым запросом на страницу. К сожалению, это включает запросы, инициированные сторонними сайтами. Если вы об этом не позаботитесь, некоторые люди могут обмануть пользователей вашего приложения с помощью социальной инженерии, чтобы те делали глупые вещи, не осознавая этого.
Допустим, у вас есть определённый URL, который при отправке запросов POST удалит профиль пользователя (например, http://example.com/user/delete). Если теперь злоумышленник создаёт страницу, которая отправляет POST-запрос на эту страницу с помощью JavaScript, ему нужно просто обмануть некоторых пользователей, чтобы они загрузили эту страницу, и их профили будут удалены.
Представьте себе Facebook с миллионами одновременных пользователей, и кто-то отправляет ссылки на изображения маленьких котят. Когда пользователи переходят по этим ссылкам, их профили удаляются, пока они смотрят на изображения пушистых кошек.
Как предотвратить это? В основном для каждого запроса, который изменяет содержимое на сервере, нужно либо использовать временный токен и хранить его в куках, и также передавать его вместе с данными формы. После получения данных на сервере снова нужно сравнить два токена и убедиться, что они совпадают.
Почему Flask не делает это за вас? Идеальное место для этого – фреймворк для валидации форм, которого в Flask нет.
Безопасность JSON
В Flask 0.10 и ниже jsonify() не сериализовал массивы верхнего уровня в JSON. Это было связано с уязвимостью в ECMAScript 4.
ECMAScript 5 закрыл эту уязвимость, поэтому уязвимы только очень старые браузеры. У всех этих браузеров есть другие, более серьёзные уязвимости, поэтому это поведение было изменено, и jsonify() теперь поддерживает сериализацию массивов.
Заголовки безопасности
Браузеры распознают различные заголовки ответа для управления безопасностью. Мы рекомендуем просмотреть каждый из следующих заголовков для использования в вашем приложении. Расширение Flask-Talisman может быть использовано для управления HTTPS и заголовками безопасности за вас.
HTTP Strict Transport Security (HSTS)
Уведомляет браузер о преобразовании всех HTTP-запросов в HTTPS, предотвращая атаки «человек посередине» (MITM).
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
Политика безопасности контента (CSP)
Указывает браузеру, откуда он может загружать различные типы ресурсов. Этот заголовок следует использовать по возможности, но для определения правильной политики для вашего сайта требуется работа. Очень строгая политика будет:
response.headers['Content-Security-Policy'] = "default-src 'self'"
X-Content-Type-Options
Принуждает браузер соблюдать тип содержимого ответа вместо попытки его определения, что может быть использовано для создания атаки межсайтового скриптинга (XSS).
response.headers['X-Content-Type-Options'] = 'nosniff'
X-Frame-Options
Препятствует внешним сайтам от встраивания вашего сайта в iframe. Это предотвращает класс атак, где щелчки во внешней рамке могут быть невидимо переведены в щелчки по элементам вашей страницы. Это также известно как «кликджекинг».
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
HTTP Public Key Pinning (HPKP)
Это сообщает браузеру об аутентификации с сервером только с помощью определенного ключа сертификата, чтобы предотвратить атаки MITM.
Предупреждение
Будьте осторожны при включении этого параметра, так как его очень сложно отменить, если вы неправильно настроили или обновили свой ключ.
Копирование/вставка в терминал
Скрытые символы, такие как символ удаления (\b, ^H), могут вызывать визуализацию текста в HTML иначе, чем при интерпретации, если вставлен в терминал.
Например, import y\bose\bm\bi\bt\be\b отображается как import yosemite в HTML, но удаления применяются при вставке в терминал, и он становится import os.
Если вы ожидаете, что пользователи будут копировать и вставлять недоверенный код с вашего сайта, например, из комментариев, опубликованных пользователями в техническом блоге, рассмотрите применение дополнительной фильтрации, например, замену всех \b символов.
body = body.replace("\b", "")
Большинство современных терминалов будут предупреждать о скрытых символах и удалять их при вставке, поэтому это не строго необходимо. Также можно создавать опасные команды другими способами, которые нельзя отфильтровать. В зависимости от использования вашего сайта, может быть полезно показать предупреждение о копировании кода в целом.
© 2010 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/stable/web-security/