Безопасная разработка в 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

6fd839011e16c9928e8a51da2677a6e8.png

Наш фреймворк запущен, теперь можно обращаться к веб серверу по порту 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.

766df460bef2e6527cfee6d43704f4a5.png

Еще один аспект, который также важен — это атрибуты без кавычек. Хотя 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, и запрещаем встраивание содержимого приложения в