[Перевод] Запустите программу Flask по протоколу HTTPS

сервер Nginx Безопасность Flask

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

Люди всегда задают мне этот вопрос, особенно как развернуть сервер Flask по протоколу HTTPS. В этой статье я расскажу о нескольких сценариях добавления шифрования в приложение Flask, начиная от очень простого, который можно реализовать менее чем за пять секунд, до надежного, такого как мой веб-сайт, который может получить рейтинг A+ (Данные SSL-аналитики для моего сайта).

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/8/27/1657abb15d157b0a~tplv-t2oaga2asx-image.image

Как работает HTTPS?

Функции шифрования и безопасности HTTP реализованы черезБезопасность транспортного уровня (TLS)реализован протокол. По сути, TLS определяет стандартный способ защиты любого сетевого канала связи. Поскольку я не эксперт по безопасности, если я попытаюсь дать вам подробное описание протокола TLS, я не думаю, что смогу сделать это хорошо, поэтому я просто хочу рассказать вам то, что нас больше интересует, зная что мы настроили сервер Flask в целях безопасности и шифрования.

Общая идея заключается в том, что когда клиент устанавливает соединение с сервером и запрашивает зашифрованное соединение, сервер отвечает своим сертификатом SSL. Сертификат теперь служит идентификатором сервера, поскольку он включает информацию об имени сервера и имени домена. Чтобы убедиться, что информация, предоставленная сервером, верна, сертификат криптографически подписан центром сертификации или ЦС. Если клиент знает ЦС и доверяет ему, он может подтвердить, что подпись сертификата действительно исходит от этого объекта, и клиент может быть уверен, что сервер, к которому он подключается, является законным.

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

Как вы можете догадаться из приведенного выше резюме, для реализации шифрования TLS нам нужны два элемента: сертификат сервера, включая открытый ключ, подписанный ЦС, и закрытый ключ для использования с открытым ключом, содержащимся в сертификате.

Самый простой способ

Колба (точнее на самом делеWerkzeug), поддерживает использование мгновенных сертификатов, что полезно для быстрого обслуживания приложений через HTTPS без вмешательства в системные сертификаты. Все, что вам нужно сделать, это поставитьssl_context ='adhoc'добавлено в программуapp.run()Вызов. К сожалению, интерфейс командной строки Flask не работает с этой опцией. В качестве примера, вот приложение Flask «Hello, World» из официальной документации с добавленным шифрованием TLS:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(ssl_context='adhoc')

Чтобы использовать временные сертификаты с Flask, вам необходимо установить соответствующую зависимость в виртуальной среде:

$ pip install pyopenssl

Когда вы запустите скрипт, вы заметите, что Flask указывает, что он работает.https://сервер:

$ python hello.py
 * Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)

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

Самоподписанный сертификат

Так называемый самозаверяющий сертификат — это сертификат, который генерирует подпись с использованием закрытого ключа, связанного с тем же сертификатом. Выше я упомянул, что клиент должен «знать и доверять» ЦС, подписавшему сертификат, потому что это доверительное отношение позволяет клиенту проверить сертификат сервера. Веб-браузеры и другие HTTP-клиенты обычно предварительно настроены со списком известных и доверенных ЦС, но очевидно, что если вы используете самозаверяющий сертификат, ЦС не будет известен, и проверка завершится ошибкой. Именно это произошло с временным сертификатом, который мы использовали в предыдущем разделе. Если веб-браузер не может проверить сертификат сервера, он позволит вам вручную продолжить работу и получить доступ к соответствующему веб-сайту, но это гарантирует, что вы сами должны понимать, на какой риск вы идете.

Но в чем именно заключается риск? Используя сервер Flask из предыдущего раздела, мы, очевидно, доверяем себе, поэтому никакого риска для нас нет. Основная проблема заключается в том, что пользователи получают это предупреждение при подключении к сайту, о котором они не знают или не контролируют. В этих случаях у пользователя нет возможности узнать, является ли сервер доверенным, поскольку любой может сгенерировать сертификат для любого домена, как вы увидите далее.

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

Мы можем легко генерировать самозаверяющие сертификаты из командной строки. Просто нужно установитьopenssl:

openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365

Эта команда находится вcert.pemЗапишите новый сертификат в соответствующий закрытый ключ по адресуkey.pem, действует 365 дней. Когда вы запустите эту команду, вам будет задано несколько вопросов. Ниже вы можете увидеть, как я ответил на них, чтобы сгенерировать сертификат для моего собственного частного сервера:

Generating a 4096 bit RSA private key
......................++
.............++
writing new private key to 'key.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Vimiix
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:vimiix
Email Address []:contact@vimiix.com

Теперь мы можем использовать этот новый самозаверяющий сертификат в нашем приложении Flask, добавивapp.run()серединаssl_contextВ качестве параметра задан кортеж с именами файлов сертификата и файлов закрытого ключа:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(ssl_context=('cert.pem', 'key.pem'))

Браузер продолжит предупреждать об этом сертификате, но если вы его проверите, то увидите информацию, введенную вами при его создании:

Веб-сервер для производства

Конечно, мы все знаем, что сервер разработки Flask предназначен только для разработки и тестирования. Итак, как нам установить SSL-сертификат на рабочем сервере?

если вы используетеgunicorn, вы можете сделать это с помощью аргументов командной строки:

$ gunicorn --certfile cert.pem --keyfile key.pem -b 0.0.0.0:8000 hello:app

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

server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    # ...
}

Еще один важный вопрос, который следует учитывать, — как заставить nginx обрабатывать клиентов, подключающихся через обычный HTTP. На мой взгляд, лучшее решение — использовать HTTPS для ответа на незашифрованные запросы путем перенаправления на тот же URL-адрес. Для приложения Flask вы можете использоватьFlask-SSLifyрасширение для достижения. Для использования nginx мы можем включить в конфигурацию еще один серверный модуль:

server {
    listen 80;
    server_name example.com;
    location / { # 全部转发到https的相同url下
        return 301 https://$host$request_uri;
    }
}

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

Используйте «настоящие» сертификаты

Теперь мы рассмотрели все варианты самозаверяющих сертификатов, но во всех этих случаях общим ограничением является то, что веб-браузеры по-прежнему не будут доверять этим сертификатам, если вы не примете их вручную, поэтому лучше всего использовать сертификаты на рабочих серверах. лучшим выбором для сайта будет получение сертификата от центра сертификации, который хорошо известен и которому автоматически доверяют все веб-браузеры. Когда вы запрашиваете сертификат у ЦС, этот орган подтвердит, что вы имеете контроль над своим сервером и доменом, но то, как выполняется проверка, зависит от ЦС. Если ваш сервер проверен, ЦС выдаст вашему серверу сертификат, подписанный этим ЦС, и предоставит его вам для установки. Сертификаты обычно действительны в течение определенного периода времени, обычно не более одного года. Большинство ЦС взимают плату за эти сертификаты, но некоторые ЦС предоставляют их бесплатно. Самые популярные бесплатные центры сертификации:Let's Encrypt.

Получить сертификат от Let's Encrypt довольно просто, поскольку весь процесс автоматизирован. Предполагая, что вы используете сервер на базе Ubuntu, вы должны сначала установить на сервер его инструмент certbot с открытым исходным кодом:

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install certbot

Теперь вы готовы запросить предварительную обработку сертификата. Существует несколько способов использования certbot для аутентификации вашего веб-сайта. Обычно проще всего реализовать метод «webroot». Используя этот метод, certbot добавляет некоторые файлы в качестве статических файлов в каталог, предоставляемый веб-сервером, а затем пытается получить доступ к этим файлам через HTTP, используя доменное имя, для которого вы хотите создать сертификат. Если этот тест прошел успешно, certbot узнает, что сервер, на котором он работает, связан с правильным доменным именем, а сертификат удовлетворяется и выдается. Команда для запроса сертификата с использованием этого метода выглядит следующим образом.

$ sudo certbot certonly --webroot -w /var/www/example -d example.com

В этом примере мы пытаемсяexample.comСертификат генерации доменного имени, доменное имя использует/var/www/exampleкаталог в качестве корневого каталога статического файла. Если certbot сможет проверить доменное имя, он запишет файл сертификата как/etc/letsencrypt/live/example.com/fullchain.pem, напишите закрытый ключ как/etc/letsencrypt/live/example.com/privkey.pem, эти сертификаты действительны в течение 90 дней.

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

Если вы используете nginx в качестве обратного прокси-сервера, вы можете создать сопоставление в своей конфигурации, чтобы предоставить certbot частный каталог, в который можно записывать файлы проверки. В приведенном ниже примере я расширяю блок HTTP-сервера, показанный в предыдущем разделе, для отправки всех запросов, связанных с Let’s Encrypt, в определенный каталог по вашему выбору:

server {
    listen 80;
    server_name example.com;
    location ~ /.well-known {
        root /path/to/letsencrypt/verification/directory;
    }
    location / {
        return 301 https://$host$request_uri;
    }
}

Certbot также используется, когда необходимо обновить сертификат. Для этого вам просто нужно выполнить следующую команду:

$ sudo certbot renew

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

Получите сертификат SSL A+

Если вы используете сертификат от Let's Encrypt или другого известного ЦС и используете на этом сервере недавно обновленную операционную систему, вероятно, у вас уже есть что-то близкое к серверу с высоким рейтингом с точки зрения безопасности SSL. ты можешь пойти вQualys SSL Labsвеб-сайт и получать отчеты, чтобы увидеть, как ваш сервер ранжируется.

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

Одной из областей, в которой относительно легко внести улучшения, является то, как генерировать коэффициенты, используемые во время обмена криптографическими ключами, которые часто имеют довольно слабые значения по умолчанию. особенно,Diffie-HellmanДля генерации коэффициентов требуется значительное количество времени, поэтому по умолчанию сервер использует меньшие числа для экономии времени. Но мы можем заранее сгенерировать сильные коэффициенты и сохранить их в файле, а затем nginx сможет их использовать. Используя инструмент openssl, вы можете запустить следующие команды:

openssl dhparam -out /path/to/dhparam.pem 2048

Если вам нужны более сильные коэффициенты, вы можете изменить их на 2048 или больше, например 4096. Выполнение этой команды займет некоторое время, особенно если ваш сервер имеет небольшое количество ЦП, но когда это будет сделано, у вас будет сильный коэффициентdhparam.pemфайл, который вы можете подключить к серверному модулю ssl nginx:

	ssl_dhparam /path/to/dhparam.pem;

Далее вам может потребоваться настроить метод шифрования, который сервер разрешает для зашифрованной связи. Вот зашифрованный список на моем сервере:

	ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

В этом списке отключенные шифры начинаются с!как префикс. Отчет SSL сообщит вам, есть ли устаревшие шифры. Вы должны следить за новыми уязвимостями, которые время от времени требуют изменения этого списка шифрования.

Ниже вы можете найти мою текущую конфигурацию nginx SSL, которая включает указанные выше настройки, а также некоторые настройки, которые я добавил для устранения предупреждений в отчете SSL:

server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    ssl_dhparam /path/to/dhparam.pem;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_stapling on;
    ssl_stapling_verify on;
    add_header Strict-Transport-Security max-age=15768000;
    # ...
}

В начале статьи вы можете увидеть результаты оценок, которые я получил для своего сайта. Если вы достигнете отметки 100% во всех категориях, вам придется добавить дополнительные ограничения в конфигурацию, но это также ограничит количество клиентов, которые могут подключиться к вашему сайту. Как правило, старые браузеры и HTTP-клиенты используют более слабые методы шифрования, но если эти методы шифрования отключены, эти клиенты не смогут подключиться. (Примечание переводчика: выбор безопасности и совместимости, как поставщику услуг, должен учитывать это и соответствующим образом уменьшать лимит обслуживания, чтобы быть совместимым со старыми методами запроса, что также является компромиссом.) Следовательно, вам в основном нужно идти на компромисс. а также необходимо регулярно просматривать отчеты о безопасности и обновлять исправления с течением времени.

К сожалению, из-за сложности этих последних улучшений SSL вы должны использовать веб-сервер профессионального уровня, поэтому, если вы не хотите использовать nginx, количество вариантов найти веб-сервер, поддерживающий эти настройки, в настоящее время очень мало. Я знаю, что Apache его поддерживает, но кроме этого я никого не знаю.

оригинал:Running Your Flask Application Over HTTPS --- miguelgrinberg

--- EOF ---