Безопасная разработка в Flask
Тема безопасной разработки актуальна для различных языков программирования и фреймворков, использующих данные языки. Ранее мы уже говорили о безопасной разработке на Python, а сегодня рассмотрим безопасную разработку в Flask — облегченном фреймворке, написанном на Python для разработки веб‑приложений WSGI. Flask был разработан для быстрого и простого начала работы с возможностью масштабирования до сложных приложений.
По сути, Flask представляет собой набор библиотек и модулей, которые позволяют разработчикам веб‑приложений писать приложения, не сильно беспокоясь о деталях низкого уровня, таких как протокол, управление потоками и так далее. При этом, в нем есть много интересных функций, таких как маршрутизация URL‑адресов, механизм шаблонов и т. д.
Установка
Знакомство с Flask начнем с установки фреймворка. Для этого установим его с помощью pip:
pip3 install Flask
Далее, внутри папки проекта создадим файл, например, app.py, который будет основным файлом вашего веб‑приложения. Использование символа /
в @app.route
указывает на то, что данный контент будет выводиться при обращении к странице по умолчанию.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
Далее просто выполним команду:
flask run
Наш фреймворк запущен, теперь можно обращаться к веб серверу по порту 5000. Этот простой пример показывает, как легко можно начать работу с Flask, хотя на самом деле в нем можно разрабатывать достаточно сложные веб-приложения. Далее мы поговорим о том, что должен сделать разработчик, чтобы не допустить наиболее распространенных уязвимостей в приложениях Flask.
Не стареющая классика XSS
Межсайтовый скриптинг по прежнему актуален. Напомним, что это концепция внедрения произвольного HTML (а вместе с ним и JavaScript) в контекст веб-сайта. Чтобы избежать XSS, разработчики должны правильно экранировать текст, чтобы он не включал произвольные HTML-теги. Например, пользователь может ввести что-то вроде:
И при отображении этого текста должны быть экранированы спецсимволы для того, чтобы это не выполнялось как код, а просто выводилось как обычная текстовая строка.
Для борьбы с XSS Flask настраивает язык шаблонов Jinja2 на автоматическое экранирование всех значений, если явно не указано иное. И в целом наш код, построенный на основе шаблонов, не будет уязвим к XSS;, но есть и другие места, где вам следует быть осторожным.
Прежде всего, это генерация HTML без помощи Jinja2 и вызов flask.markup в данных, отправленных пользователями. Здесь вам потребуется самостоятельно выполнять экранирование пользовательского ввода на Python. Так, в примере ниже показано использование библиотеки bleach:
import bleach
…
clean_html = bleach.clean(html)
print(clean_html)
Функция bleach.clean
очистит содержимое html от потенциально опасной HTML разметки. Кроме этого, всевозможные манипуляции пользователей с файлами тоже могут быть небезопасными. Так, при отправке HTML из загруженных пользователями файлов, используйте заголовок Content-Disposition: attachment
, чтобы предотвратить обработку кода из них.
Кроме того, некоторые браузеры используют угадывание типа контента на основе первых нескольких байт, что тоже может привести к выполнению HTML.
Еще один аспект, который также важен — это атрибуты без кавычек. Хотя Jinja2 может защитить вас от проблем с XSS, экранируя HTML, он не может вас защитить от атак XSS с помощью внедрения атрибутов. Чтобы противостоять этому возможному вектору атаки, обязательно заключайте атрибуты в двойные или одинарные кавычки при использовании в них выражений Jinja:
Зачем это нужно? Потому что, если мы это не сделаем, злоумышленник может легко внедрить пользовательские обработчики JavaScript. Например, злоумышленник мог бы внедрить этот фрагмент HTML+JavaScript:
onmouseover=alert(document.cookie)
Когда пользователь наведет курсор мыши на вводимые данные, значения cookie будут представлены пользователю в окне предупреждения. Естественно, вместо вывода cookie, умелый злоумышленник может также выполнить любой другой код JavaScript. В сочетании с внедрением CSS злоумышленник может даже заставить элемент заполнить всю страницу, так что пользователю достаточно будет навести курсор мыши в любом месте страницы, чтобы запустить атаку.
Также, есть еще один класс проблем с XSS, от которых экранирование Jinja не защищает. Атрибут href
тега ..
может содержать javascript: URI
, который браузер будет выполнять при нажатии, если он не защищен должным образом.
click here
click here
Здесь лучшим средством защиты будет использование заголовков Content Security Policy (CSP). CSP позволяет веб-разработчикам указывать браузерам, из каких источников разрешено загружать ресурсы, такие как скрипты, стили, изображения, шрифты и другие элементы. Одним из рекомендуемых решений для добавления CSP в Flask является использование расширения Talisman.
Установить его проще всего с помощью pip:
pip3 install talisman
Далее для использования этого расширения совместно с Flask необходимо в файле app.py, перед routes указать представленный ниже блок кода, в котором определить откуда нам можно загружать контент для выполнения различных задач.
talisman = Talisman(
app,
content_security_policy={
'default-src': [
'\'self\'',
],
'script-src': [
'\'self\'',
'\'unsafe-inline\'',
'https://code.jquery.com',
],
'style-src': [
'\'self\'',
'\'unsafe-inline\'',
'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css',
'https://todoapp5.ddns.net/static/styles.css',
'https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css',
],
},
force_https=True,
session_cookie_secure=True,
frame_options='DENY',
)
Как видно из представленного кода, мы определяем разрешение для любого контента default-src
, затем идут скрипты и стили. В завершении мы говорим Flask использовать только HTTPS для соединений, сессионные куки мы также передаем только через HTTPS, и запрещаем встраивание содержимого приложения в .
Подделка межсайтовых запросов (CSRF)
Еще одной серьезной проблемой является CSRF. CSRF — вид атак на посетителей веб-сайтов, использующий недостатки протокола HTTP. Если жертва заходит на сайт, созданный злоумышленником, от её лица тайно отправляется запрос на другой сервер (например, на сервер платёжной системы), осуществляющий некую вредоносную операцию (например, перевод денег на счёт злоумышленника). Для осуществления данной атаки жертва должна быть аутентифицирована на том сервере, на который отправляется запрос, и этот запрос не должен требовать какого-либо подтверждения со стороны пользователя, которое не может быть проигнорировано или подделано атакующим скриптом.
Соответственно, если ваша аутентификационная информация хранится в файлах cookie, у вас есть неявное управление состоянием. Состояние «вход в систему» контролируется файлом cookie, и этот файл cookie отправляется при каждом запросе на страницу, и к сожалению, это также включает запросы, инициируемые сторонними сайтами.
Допустим, у вас есть определенный URL‑адрес, по которому при отправке POST‑запросов профиль пользователя будет удален (скажем, http://example.com/user/delete). Если злоумышленник теперь создаст страницу, которая отправит запрос post на эту страницу с помощью JavaScript, ему просто нужно будет обмануть пользователей, заставив их загрузить эту страницу, для того, чтобы их профили в конечном итоге были удалены.
Представьте, что у вас есть соцсеть с миллионами одновременных пользователей, и кто‑то будет рассылать ссылки на изображения маленьких котят. Когда пользователи будут заходить на эту страницу, их профили будут удаляться, пока они просматривают изображения пушистых кошек.
Предотвратить это можно, если для каждого запроса, который изменяет содержимое на сервере, вы будете либо использовать одноразовый токен и сохранять его в файле cookie, либо передавать его вместе с данными формы. После повторного получения данных на сервере вам нужно будет сравнить два токена и убедиться, что они равны. Однако, функционала для проверки форм, который мог выполнять все это автоматически во Flask пока нет.
Заключение
В этой небольшой статье мы постарались осветить основные моменты, связанные с безопасной разработкой веб-приложений в Flask на Python. Следование этим простым рекомендациям позволит существенно увеличить защищенность приложений в целом.
Все актуальные методы и инструменты безопасной разработки приложений можно освоить на онлайн-курсах OTUS: в каталоге можно посмотреть список всех программ, а в календаре — записаться на бесплатные открытые уроки.