В предыдущих статьях мы подробно рассмотрели основные проблемы безопасности, с которыми может столкнуться разработчик при создании своих приложений на Python. В этой статье мы продолжим рассмотрение данной темы и поговорим о работе с фреймворками, написанными на Python и теми проблемами в области безопасности, с которыми может столкнуться использующий их разработчик.
Фреймворк Flask
Flask является одним из самых распространенных фреймворков, написанных на Python. По сути, это упрощенная платформа Python для веб-приложений, которая обеспечивает основные возможности маршрутизации URL-адресов и визуализации страниц. Flask также называют микроплатформой, так как она не предоставляет напрямую такие функции, как проверка форм, абстракция базы данных, проверка подлинности и т. д. Эти функции предоставляются специальными пакетами Python, называемыми расширениями Flask.
Основным файлом, отвечающим за отображение веб контента является файл app/routes.py. Вот один из простейших вариантов его содержимого:
from app import app
@app.route('/')
@app.route('/index')
def index():
return "Hello, World!"
Первые две строки со знаком @ это декораторы, которые изменяют функцию, следующую за ним. То есть, декоратор @app.route создает связь между URL-адресом, заданным как аргумент, и функцией. Так, у нас два декоратора, которые связывают URL-адреса / и /index с этой функцией. Это означает, что, если веб-браузер запрашивает один из этих двух URL-адресов, Flask будет вызывать эту функцию и передавать возвращаемое значение обратно в браузер в качестве ответа.
В результате, после запуска Flask, при обращении к / или /index в браузере будет выведена строка Hello, World!
Когда что-то пошло не так…
Однако, как и любой другой фреймворк, Flask не лишен уязвимостей и далее мы рассмотрим некоторые из них. Начнем со старой доброй уязвимости из OWASP TOP 10 – Cross Site Scripting, XSS. Для начала рассмотрим следующий пример кода.
From flask import Flask, render_template, request
from app import app
from app.forms import LoginForm
# ...
@app.route('/login', methods=['GET', 'POST'])
def login():
parameter = request.args.get('parameter','')
return render_template("template.html", data=parameter)
Как видно, здесь у нас присутствует render_template, который отображает в файле template.html значение data. При этом, само значение data мы получаем из значения parameter, переданного пользователем в адресной строке браузера. Файл template.html при этом имеет следующий вид:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Flask template</title>
</head>
<body>
{{ data }}
</body>
</html>
Значение data у нас отображается внутри тега <body>. Казалось бы, все должно быть хорошо. Передадим в адресной строке значение 12345.
Все успешно отобразилось. Но вернемся к межсайтовому скриптингу. XSS это такой тип атак, которые позволяют злоумышленнику внедрять вредоносный код через веб-сайт в браузеры других пользователей. При этом у нас не происходит взлома как такового на стороне сервера. Мы просто сохраняем код скрипта, например в гостевой книге на данном сайте, а в браузерах всех других посетителей при открытии данной записи выполняется сохраненный злоумышленниками код на Javascript. В качестве примера, рассмотрим следующий код:
<script>alert("XSS_vulnerable!")</script>
В результате выполнения данного скрипта в браузере отображается окно с сообщением XSS_Vulnerable! В реальности используемые в XSS скрипты могут ввести, например, на фишинговые страницы.
Теперь попробуем передать такой скрипт нашему Flask и посмотрим на его реакцию.
Как видно, наш Flask код уязвим для межсайтового скриптинга, что не очень хорошо.
Лечение
Для того, чтобы излечить наш код от такой древней уязвимости можно воспользоваться специальными библиотеками, например Escape. Данная библиотека не позволяет выполнить код в тех данных, которые передал пользователь, даже если он синтаксически полностью корректен.
Немного изменим наш код, добавив в него использование Escape:
from flask import Flask, render_template, request
from flask import escape
from app import app
from app.forms import LoginForm
# ...
@app.route('/login', methods=['GET', 'POST'])
def login():
parameter = escape(request.args.get('parameter',''))
return render_template("templates.html", data=parameter)
И посмотрим результат:
Теперь наш скрипт стал обычной текстовой строкой.
Перенаправляем не туда
Еще одним полезным свойством Flask является возможность перенаправления пользователя на веб страницу, переданную ему в качестве параметра. Рассмотрим следующий код:
from flask import Flask, render_template, request, Response
from app import app
from app.forms import LoginForm
# ...
@app.route('/url/<url>')
def change_location(url):
response = Response()
headers = response.headers
headers["location"] = url
return response.headers["location"]
Здесь у нас осуществляется перенаправление пользователя на страницу, переданную в url. Однако, мы вряд ли хотим, чтобы с нашего сайта пользователи могли переходить на различные сомнительные ресурсы. Соответственно, нам было неплохо проверять передаваемые значения на соответствие некоторому списку разрешенных ресурсов. Посмотрим, как это можно было бы сделать.
from flask import Flask, render_template, request, Response
from app import app
from app.forms import LoginForm
# ...
valid_locations = ['ya.ru', 'mail.ru',’rbc.ru’]
@app.route('/url/<url>')
def change_location(url):
response = Response()
headers = response.headers
sanitizedLocation = getSanitizedLocation(url)
headers["location"] = "http://" + sanitizedLocation
return response.headers["location"]
def getSanitizedLocation(location):
return location if (location in valid_locations) else "check_url"
Здесь в списке valid_locations у нас указаны веб сайты, на которые можно осуществлять перенаправление. Для всех прочих ресурсов мы вместо перенаправления будем получать сообщения check_url. Также в случае, если пользователь укажет некорректное или несуществующее имя сайта, будет выведено данное сообщение. Таким образом, мы можем ограничить перенаправление на сомнительные ресурсы.
Про отладку
Как и в других системах программирования и фреймворках, в Flask крайне не рекомендуется включать режим отладки в продуктовой среде. Использование данного режиме может быть оправдано при разработке и отладке кода, так этот режим позволяет своевременно выявлять и устранять ошибки в коде.
Однако, недопустимо, когда в случае ошибки пользователю выдаются сообщения, подобные приведенным ниже:
Необходимо, чтобы в случае сбоев, пользователю выводилась стандартная заглушка, уведомляющая о некорректной работе приложения, а не полный лог с указанием путей к библиотекам и строк, содержащих ошибки.
Заключение
Фреймворк Flask является мощным и полезным инструментом, предназначенным для быстрого создания веб ресурсов на Python. Но, как любой другой язык он содержит некоторые уязвимости, о которых необходимо помнить при разработке.
Напоследок хочу порекомендовать бесплатный вебинар от OTUS в рамках которого поговорим о том:
что такое IDE и какие IDE существуют;
как настроить самую полнофункциональную IDE для Python
PyCharm Community Edition;
версии Python, почему их так много;
как разрабатывать несколько проектов на одной машине (что нужно учитывать, зачем нужны виртуальные среды (venv) для проектов) и что это такое;
как запустить приложение в Docker-контейнере (зачем это делать, как это можно сделать, что нужно учесть).
В результате вы сможете организовать изолированную среду для разработки и запуска приложения с помощью venv и docker, узнаете почему существуют различные версии Python, и как организовать свое рабочее место, чтобы разрабатывать несколько различных проектов, использующих разные версии Python и сторонних пакетов, одновременно, и без боли.
Зарегистрироваться на бесплатный вебинар