当前位置: 首页 > 后端技术 > Python

[翻译]RunningYourFlaskApplicationOverHTTPS

时间:2023-03-26 11:01:20 Python

翻译RunningYourFlaskApplicationOverHTTPS简介在开发FLASK应用程序的过程中,通常会运行开发Web服务器,它提供了一个基本但功能齐全的WSGIHTTP服务器。但是在将应用程序部署到生产环境时,需要考虑的一件事是是否应要求客户端使用加密连接以增加安全性。那么如何通过HTTPS运行FLASK应用程序呢?在这篇博文中,我将介绍为您的Flask应用程序添加加密的几个选项,从只需5秒即可实施的非常简单的解决方案,到稳健的A+级解决方案。HTTPS是如何工作的?HTTP的加密和安全特性是通过传输层安全(TLS)协议实现的。总的来说,TLS定义了一种保护网络通道的标准方法。其基本思想是,当客户端与服务器建立连接并请求加密连接时,服务器以其SSL证书进行响应。该证书充当服务器的身份,因为它包含服务器名称和域。为确保服务器提供的信息正确,证书由证书颁发机构(CA)以加密方式签名。如果客户端知道并信任CA,则可以确认证书签名确实来自该实体,并且通过此,客户端可以确定它所连接的服务器是合法的。客户端验证证书后,创建一个加密密钥用于与服务器通信。为确保将此密钥安全地发送到服务器,它使用服务器证书中包含的公钥对其进行加密。服务器拥有与证书中的公钥一起使用的私钥,因此它是唯一能够解密它的一方。从服务器收到加密密钥的那一刻起,所有流量都使用只有客户端和服务器知道的密钥进行加密。为了实现TLS加密,我们需要两项:服务器证书,包含CA签发的公钥;以及证书中包含的私钥和公钥。Flask(更具体地说是Werkzeug)支持使用即时证书的最简单方式,这对于在没有证书的情况下通过HTTPS快速提供应用程序非常有用。要在Flask中使用临时证书,您需要在虚拟环境中安装一个额外的依赖项:$pipinstallpyopenssl然后将ssl_context='adhoc'添加到app.run()调用中:fromflaskimportFlaskapp=Flask(__name__)@app.route("/")defhello():返回“HelloWorld!”if__name__=="__main__":app.run(ssl_context='adhoc')如果您使用的是Flask1.x发行版,您还可以通过FlaskCLI使用此选项:$flaskrun--cert=adhoc运行时这个脚本(或flaskrun),你会注意到Flask说它正在运行https://server:$pythonhello.py*Runningonhttps://127.0.0.1:5000/(按CTRL+C退出)但是问题是浏览器不喜欢这种类型的证书,因此它们会显示一个可怕的警告,您需要在访问应用程序之前消除此警告。一旦您允许浏览器连接,您将拥有一个加密连接,就像您从具有有效证书的服务器获得一样,使用这些临时证书可以方便地进行测试,但不能用于任何实际用途。自签名证书所谓自签名证书,就是使用与证书关联的私钥生成签名的证书。上面我提到客户端需要“知道并信任”签署证书的CA,因为这种信任关系允许客户端验证服务器证书。Web浏览器和其他HTTP客户端预先配置了已知和受信任的CA列表,但显然,如果使用自签名证书,CA将不为人所知,验证将失败。这正是我们在上一节中使用的临时证书所发生的情况。如果Web浏览器无法验证服务器证书,它允许继续访问有问题的网站,但它会警告您这样做的风险。但真的有风险吗?使用上一节中的Flask服务器,你显然相信自己,所以你没有风险。问题是,当用户连接到他们不知道或无法控制的站点时,会出现此警告。在这种情况下,用户无法知道服务器是否真实,因为任何人都可以为任何域生成证书。虽然自签名证书有时很有用,但Flask中的临时证书并不是很好,因为每次服务器运行时,pyOpenSSL都会生成不同的证书。使用自签名证书时,最好在每次启动服务器时都使用同一个证书,因为这会将浏览器配置为信任它,并消除安全警告。可以通过命令行生成自签名证书,安装openssl即可:opensslreq-x509-newkeyrsa:4096-nodes-outcert.pem-keyoutkey.pem-days365这条命令写了一个新建证书并将其对应的私钥写入key.pem中,有效期为365天。当你运行这条命令时,你会被问到几个问题:Generatinga4096bitRSAprivatekey................................++..............++writingnewprivatekeyto'key.pem'-----你将被要求输入将被合并到你的证书请求中的信息。你将要输入的是所谓的专有名称或DN.字段比较多,但可以留空一些字段会有默认值,如果输入'.',该字段将留空。-----国家名称(2个字母代码)[AU]:USStateorProvinceName(fullname)[Some-State]:OregonLocalityName(eg,city)[]:PortlandOrganizationName(eg,company)[InternetWidgitsPtyLtd]:MiguelGrinbergBlogOrganizationalUnitName(eg,section))[]:CommonName(e.g.serverFQDNorYOURname)[]:localhostEmailAddress[]:我们现在可以在我们的Flask应用程序中使用这个新的自签名证书,方法是通过app.run()中设置的ssl_context参数到包含文件名的元组证书和私钥文件。fromflaskimportFlaskapp=Flask(__name__)@app.route("/")defhello():返回“HelloWorld!”if__name__=="__main__":app.run(ssl_context=('cert.pem','key.pem'))如果您使用的是Flask1.x或更高版本,您可以添加--cert和--flaskrun命令的关键选项:$flaskrun--cert=cert.pem--key=key.pem浏览器仍然会警告,但是如果你检查证书,你会看到你创建它时输入的信息使用生产web服务器我们都知道Flask开发服务器只适用于开发和测试。那么,我们如何在生产服务器上安装SSL证书呢?如果你使用gunicorn,可以使用命令行参数:$gunicorn--certfilecert.pem--keyfilekey.pem-b0.0.0.0:8000hello:app如果你使用nginx作为反向代理,那么你可以使用nginx配置证书,然后nginx可以“终止”加密连接,这意味着它将接受来自外部的加密连接,但随后使用常规未加密连接与Flask后端通信。这是一个非常有用的设置,因为它使应用程序不必处理证书和加密。Nginx的配置项如下:server{listen443ssl;服务器名称example.com;ssl_certificate/path/to/cert.pem;ssl_certificate_key/path/to/key.pem;#...}您需要考虑的另一件事最大的问题是如何处理通过常规HTTP连接的客户端。在我看来,最好的解决方案是通过重定向到相同的URL但使用HTTPS来响应未加密的请求。对于Flask应用程序,您可以使用Flask-SSLify扩展来执行此操作。使用nginx,您可以在配置中包含另一个服务器块:server{listen80;服务器名称example.com;location/{return301https://$host$request_uri;}}使用“真实”证书我们现在已经查看了自签名证书的所有选项已被删除,但在所有这些情况下,限制仍然存在,除非您告诉它们,否则Web浏览器不会信任这些证书。因此,对于生产站点,服务器证书的最佳选择是从这些CA之一获取它们,这些CA是众所周知的,并且被所有Web浏览器自动信任。当您从CA请求证书时,该实体将验证您是否在服务器和域的控制范围内,但是如何完成取决于CA。如果服务器通过此验证,CA将为其颁发一个自签名证书并交给您安装。证书的有效期通常不超过一年。大多数CA对这些证书收费,但有些CA免费提供证书。最流行的免费CA称为Let'sEncrypt。从Let'sEncrypt获取证书相当容易,因为整个过程是自动化的。假设你使用的是基于Ubuntu的服务器,你需要在你的服务器上安装他们的开源certbot工具:$sudoapt-getinstallsoftware-properties-commongetupdate$sudoapt-getinstallcertbot现在,您可以使用certbot工具来申请证书。Certbot可以通过多种方式验证您的站点。通常,“webroot”方法是最容易实现的。使用此方法,certbot将一些文件作为静态文件添加到Web服务器公开的目录,然后尝试使用为其生成证书的域通过HTTP访问这些文件。如果此测试成功,certbot知道它运行的服务器与正确的域相关联,匹配它并颁发证书。使用这种方式申请证书的命令如下:$sudocertbotcertonly--webroot-w/var/www/example-dexample.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{listen80;服务器名称example.com;location~/.well-known{root/path/to/letsencrypt/verification/directory;}location/{return301https://$host$request_uri;}}然后你可以把这个目录给certbot:$sudocertbotcertonly--webroot-w/path/to/letsencrypt/verification/directory-dexample.com如果certbot可以验证域名,它会写证书文件为/etc/letsencrypt/live/example.com/fullchain.pem,私钥为/etc/letsencrypt/live/example.com/privkey.pem,有效期为90天。要使用这个新获得的证书,可以输入上面提到的两个文件名来代替我们之前使用的自签名文件,这应该适用于上述任何配置。当然,您还需要通过您注册的域名使您的应用程序可用,因为这是浏览器接受证书有效的唯一方式。当您需要更新证书时,您也可以使用Certbot:$sudocertbotrenew如果系统中的任何证书即将过期,上述命令将更新它们并将新证书留在原处。如果您希望获得更新的证书,您可能需要重新启动您的Web服务器。获得SSLA+评级如果您在生产站点上使用来自Let'sEncrypt或其他已知CA的证书,并在此服务器上运行新维护的操作系统,则您可能拥有一台服务器。您可以访问QualysSSLLabs网站获取报告。该报告将指出需要改进的地方,但总的来说,我希望您会被告知服务器公开的加密通信选项太宽或太弱,使您容易受到已知漏洞的攻击。一个容易改进的地方是如何生成加密密钥交换过程中使用的系数,这些系数通常具有相当弱的默认值。特别是,Diffie-Hellman系数需要花费大量时间来生成,因此默认情况下服务器使用较小的数字来节省时间。但是我们可以预先生成强系数并将它们存储在文件中,然后nginx可以使用它们。使用openssl工具,可以运行如下命令:openssldhparam-out/path/to/dhparam.pem2048如果你想要更强的系数,可以将上面的2048改成4096。这个命令运行需要一些时间,尤其是如果您的服务器没有很多CPU能力,但当它有时,您将拥有一个具有强大系数的dhparam.pem文件,您可以将其插入nginx的ssl服务器blockssl_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+AESECDSA-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报告会告诉您是否有任何已弃用的密码。您必须不时回来查看是否发现了需要修改此列表的新漏洞。在下面你可以找到我当前的nginxSSL配置,包括上面的设置,以及我添加到SSL报告中的一些警告:server{listen443ssl;服务器名称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:!出口:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';ssl_protocolsTLSv1.2;ssl_session_timeout1d;ssl_session_cache共享:SSL:50m;ssl_stapling开启;ssl_stapling_verify开启;add_headerStrict-Transport-Securitymax-age=15768000;如果您没有在所有类别中获得100%的分数,您将不得不在配置中添加额外的限制,但这将限制可以连接到您站点的客户端数量。通常,较旧的浏览器和HTTP客户端使用被认为不是最强的密码,但如果禁用密码,这些客户端将无法连接。所以你基本上需要妥协,你还需要定期检查安全报告,并随着情况的变化进行更新。不幸的是,随着这些最近SSL改进的复杂程度,您将需要使用专业级的Web服务器,因此如果您不想使用nginx,则需要找到一个支持这些设置的服务器,而且列表很小。我知道Apache可以,但我不知道其他任何东西。