翻译
Running Your Flask Application Over HTTPS
介绍
在开发 FLASK 利用过程中,通常会运行开发 web 服务器,它提供了一个根本的、但功能齐全的 WSGI HTTP 服务器。然而当部署应用程序到生产环境中,须要思考的事件之一是,是否应该要求客户端应用加密连贯以减少安全性。
那么应该如何在 HTTPS 上运行 FLASK 应用程序呢?在这篇文章中,我将介绍几个为 Flask 应用程序增加加密性能的选项,从一个只须要 5 秒钟就能够实现的非常简单的解决方案,到一个强壮的 A + 评级的解决方案。
HTTPS 是如何工作的?
HTTP 的加密和平安性能是通过传输层平安 (TLS) 协定实现的。总的来说,TLS 定义了一种规范的形式来保障网络通道的平安。
其根本思维是,当客户端与服务器建设连贯并申请加密连贯时,服务器将应用其 SSL 证书 进行响应。该证书充当服务器的标识,因为它包含 服务器名称和域 。为了确保服务器提供的信息是正确的,证书由证书颁发机构(CA) 进行加密签名。如果客户端理解并信赖 CA,它能够确认证书签名的确来自此实体,并且通过此客户端,客户端能够确定其连贯的服务器是非法的。
在客户端验证证书之后,它将创立一个加密密钥以用于与服务器的通信。为了确保此密钥平安地发送到服务器,它应用服务器证书中蕴含的公钥对其进行加密。服务器领有与证书中的公钥一起应用的私钥,因而它是惟一可能解密的一方。从服务器接管到加密密钥开始,所有流量都应用只有客户端和服务器晓得的密钥进行加密。
为了实现 TLS 加密,咱们须要两个条目: 服务器证书,其中包含由 CA 签名的公共密钥; 与证书中蕴含的公共密钥一起的私钥。
最简略的办法
Flask (更具体地说是 Werkzeug)反对应用动静证书(on-the-fly certificates),这对于通过 HTTPS 疾速为应用程序提供服务而无需证书时十分有用。
要在 Flask 上应用长期证书,你须要在虚拟环境中装置一个附加依赖项:
$ pip install pyopenssl
而后将 ssl_context =’adhoc’ 增加到 app.run()调用中:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(ssl_context='adhoc')
如果您应用的是 Flask 1.x 发行版,则还能够通过 Flask CLI 应用此选项:
$ flask run --cert=adhoc
当运行这个脚本(或者 flask run),你会留神到 Flask 表明它运行的是 https://server:
$ python hello.py
* Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)
然而存在的问题是,浏览器不喜爱这种类型的证书,所以它们会显示一个可怕的正告,您须要在拜访应用程序之前解除这个正告。一旦你容许浏览器连贯,你将会有一个加密的连贯,就像你从一个无效证书的服务器那里失去的一样,应用这些长期证书能够很不便进行测试,但不适用于任何理论用处。
自签名证书
所谓的自签名证书是应用与该证书关联的私钥生成签名的证书。我在下面提到,客户端须要“理解并信赖”签订证书的 CA,因为这种信赖关系使客户端能够验证服务器证书。Web 浏览器和其余 HTTP 客户端事后配置了已知和受信赖的 CA 列表,然而显然,如果应用自签名证书,则 CA 将不会被通晓,并且验证将失败。这正是咱们在上一节中应用的长期证书所产生的。如果 web 浏览器无奈验证服务器证书,它容许持续进行操作并拜访有问题的网站,但它将揭示你这样做须要承当的危险。
然而真的有危险吗?应用上一部分的 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]:US
State or Province Name (full name) [Some-State]:Oregon
Locality Name (eg, city) []:Portland
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Miguel Grinberg Blog
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:
当初,咱们能够在 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 1.x 或更高版本,则能够在 flask run 命令中增加 –cert 和 –key 选项:
$ flask run --cert=cert.pem --key=key.pem
浏览器依然会告警,然而如果你查看这个证书,你会看到你创立它时输出的信息
应用生产 Web 服务器
咱们都晓得 Flask 开发服务器只实用于开发和测试。那么,咱们如何在生产服务器上安装 SSL 证书呢?
如果你应用 gunicorn,你能够应用命令行参数:
$ gunicorn --certfile cert.pem --keyfile key.pem -b 0.0.0.0:8000 hello:app
如果你应用 nginx 作为反向代理,那么你能够应用 nginx 配置证书,而后 nginx 能够“终止”加密连贯,这意味着它将承受来自内部的加密连贯,但随后应用惯例的非加密连贯与 Flask 后端通信。这是一个十分有用的设置,因为它使应用程序不用解决证书和加密。Nginx 的配置项如下:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# ...
}
你须要思考的另一个重要问题是如何解决通过惯例 HTTP 连贯的客户端。在我看来,最好的解决方案是通过重定向到雷同的 URL 但应用 HTTPS 来响应未加密的申请。对于一个 Flask 应用程序,你能够通过 Flask-SSLify 扩大来实现。应用 nginx,你能够在配置中蕴含另一个服务器块:
server {
listen 80;
server_name example.com;
location / {return 301 https://$host$request_uri;}
}
应用「真正」证书
咱们当初曾经钻研了自签名证书的所有选项,然而在所有这些状况下,局限性依然存在,除非你通知 web 浏览器,否则它们不会信赖这些证书。
因而对于生产站点来说,服务器证书的最佳抉择是从这些家喻户晓并被所有 web 浏览器主动信赖的 CA 之一取得它们。
当你向 CA 申请证书时,该实体将验证您是否在服务器和域的管制范畴内,然而验证的形式取决于 CA。如果服务器通过此验证,那么 CA 将为其颁发一个带有本人签名的证书,并将其交给你装置。证书有效期通常不超过一年。大多数 CA 会对这些证书免费,但也有一些收费提供证书。最受欢迎的收费 CA 叫做 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 工具申请证书。Certbot 可通过多种形式来验证您的站点。
通常,“webroot”办法是最容易实现的。应用这种办法,certbot 将一些文件增加到 web 服务器以动态文件模式公开的目录中,而后尝试应用要为其生成证书的域通过 HTTP 拜访这些文件。如果这个测试胜利,certbot 晓得运行它的服务器与正确的域相关联,并与之匹配,并颁发证书。应用此办法申请证书的命令如下:
$ sudo certbot certonly --webroot -w /var/www/example -d example.com
在此示例中,咱们尝试为 example.com 域生成证书,该证书应用 /var/www/example 中的目录作为动态文件根。可怜的是,基于 Flask 的网站没有动态文件根目录,至多应用默认配置时,应用 /static 前缀拜访应用程序中的所有动态文件,因而须要进行更多布局。
在此示例中,咱们尝试为 example.com 域生成证书,该证书应用 /var/www/example 中的目录作为动态文件根。可怜的是,基于 Flask 的网站没有动态文件根目录(至多应用默认配置时),须要应用 /static 前缀拜访应用程序中的所有动态文件。
Certbot 对动态根目录执行的操作是增加一个 .well-known 子目录,并在其中存储一些文件。而后它应用 HTTP 客户端以 [http://example.com/.well-known/...](http://example.com/.well-known/...)
的模式检索这些文件。如果能够检索这些文件,则表明你的服务器齐全管制了域名。对于 Flask 和其余没有动态文件根目录的应用程序,有必要定义一个根目录。
如果将 nginx 用作反向代理,则能够利用可在配置中创立的弱小映射为 certbot 提供一个公有目录,在该目录中能够写入其验证文件。在以下示例中,我扩大了上一节中显示的 HTTP 服务器块,以将所有与加密相干的申请(始终以 /.well-known / … 结尾)发送到您抉择的特定目录:
如果应用 nginx 作为反向代理,那么能够在配置中创立的弱小映射来为 certbot 提供一个公有目录,在这个目录中它能够写入其验证文件
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 certonly --webroot -w /path/to/letsencrypt/verification/directory -d example.com
如果 certbot 可能验证域名,它将把证书文件写成 /etc/letsencrypt/live/ example.com/fullchain.pem,私钥写成 /etc/letsencrypt/live/ example.com/privkey.pem,有效期为 90 天。
要应用这个新取得的证书,能够输出下面提到的两个文件名来代替咱们之前应用的自签名文件,这应该实用于下面形容的任何配置。当然,你也须要让你的应用程序通过你注册的域名可用,因为这是浏览器承受证书为无效的惟一形式。
当你须要更新证书时也能够应用 Certbot:
$ sudo certbot renew
如果零碎中有任何证书行将过期,下面的命令将更新它们,并在雷同的地位留下新的证书。如果你心愿获取更新后的证书,你可能须要重新启动你的 web 服务器。
取得 SSL A+ 等级
如果您在生产站点上应用 Let’s Encrypt 或其余已知 CA 的证书,并在此服务器上运行最新保护的操作系统,那么你很可能领有一个在 SSL 安全性方面有最高评分的服务器。您能够返回 Qualys SSL Labs 实验室网站,取得一个报告。
报告会指出你须要改良的中央,然而一般来说,我心愿你会被告知服务器公开的加密通信选项太宽,或者太弱,使你容易受到已知破绽的影响。
易于改良的中央之一是如何生成在加密密钥替换过程中应用的系数,这些系数通常具备相当弱的默认值。特地是,Diffie-Hellman 系数须要破费大量工夫能力生成,因而默认状况下,服务器应用较小的数字来节省时间。然而咱们能够事后生成强系数并将其存储在文件中,而后 nginx 就能够应用它们了。应用 openssl 工具,你能够运行以下命令:
openssl dhparam -out /path/to/dhparam.pem 2048
如果你想要更强的系数,你能够把下面的 2048 改成 4096。这个命令须要一些工夫来运行,特地是如果你的服务器没有很多的 CPU 能量,然而当它运行的时候,你会有一个带有强系数的 dhparam.pem 文件,你能够插入 nginx 的 ssl 服务器 block
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';
在此列表中,应用!前缀禁用 ciphers。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.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;
# ...
}
你能够看到我的网站取得的 SSL 平安报告。如果你在所有类别中的得分都未达到 100%,则必须对配置增加其余限度,但这将限度能够连贯到你的站点的客户端的数量。通常,较旧的浏览器和 HTTP 客户端应用的 ciphers 不被认为是最强的,然而如果禁用 ciphers,则这些客户端将无奈连贯。因而,你基本上须要斗争,并且还须要定期检查平安报告并随着状况的变动进行更新。
可怜的是,对于最近这些 SSL 改良的复杂程度,你将须要应用专业级的 Web 服务器,因而,如果你不想应用 nginx,则须要找到一个反对这些设置的服务器,而且这个列表十分小。我晓得 Apache 能够,然而除此之外,我不晓得还有别的。