python flask从http重定向到https

时间:2015-08-26 21:57:37

标签: python ssl flask

我有一个使用python3.4和烧瓶的网站构建...我已经生成了自己的自签名证书,我目前正通过localhost测试我的网站。

我正在使用python ssl模块以及此烧瓶扩展:https://github.com/kennethreitz/flask-sslify

context = ('my-cert.pem', 'my-key.pem')
app = Flask(__name__)
sslify = SSLify(app)

...

if __name__ == '__main__':
    app.debug = False
    app.run(
    host="127.0.0.1",
    port=int("5000"),
    ssl_context=context
)

但这似乎并不奏效。我查看了sslify源代码,这行似乎不起作用

def init_app(self, app):
    """Configures the configured Flask app to enforce SSL."""
    app.before_request(self.redirect_to_ssl)
    app.after_request(self.set_hsts_header)

具体是对redirect_to_ssl的函数调用(我在redirect_to_ssl函数下添加了自己的print语句,我的语句从未打印过)

def redirect_to_ssl(self):
    print("THIS IS WORKING")
    """Redirect incoming requests to HTTPS."""
    Should we redirect?
    criteria = [
        request.is_secure,
        current_app.debug,
        request.headers.get('X-Forwarded-Proto', 'http') == 'https'
    ]

    if not any(criteria) and not self.skip:
        if request.url.startswith('http://'):
            url = request.url.replace('http://', 'https://', 1)
            code = 302
            if self.permanent:
                code = 301
            r = redirect(url, code=code)
            return r

我对python很新。有什么想法吗?

13 个答案:

答案 0 :(得分:29)

对我来说,似乎你让它变得比它需要的更复杂。以下是我在views.py脚本中使用的代码,用于强制用户进行HTTPS连接:

@app.before_request
def before_request():
    if request.url.startswith('http://'):
        url = request.url.replace('http://', 'https://', 1)
        code = 301
        return redirect(url, code=code)

答案 1 :(得分:13)

根据docs,在pip install Flask-SSLify之后,您只需要插入以下代码:

from flask import Flask
from flask_sslify import SSLify

app = Flask(__name__)
sslify = SSLify(app)

我做到了,效果很好。我在讨论中遗漏了什么吗?

答案 2 :(得分:5)

标准解决方案是使用enforce_ssl装饰器包装请求,在检查应用程序配置中的一些标志(根据您的调试需求可以设置的标志)后,使用{{{{}修改请求的URL。 1}}。

写成here

根据@ kelly-keller-heikkila

的建议,您可以修改代码以使其与request.url一起使用

答案 3 :(得分:5)

Flask Security Guide建议使用Flask-Talisman

$ pip install flask-talisman

用法示例:

from flask import Flask
from flask_talisman import Talisman

app = Flask(__name__)
Talisman(app)

默认情况下(自述文件)它强制HTTPS

  

force_https,默认为True,强制所有未调试的连接都连接到https


我个人遇到了一些与CSP(内容安全策略)有关的错误,这些错误已由我禁用:

Talisman(app, content_security_policy=None)

但是使用此方法需要您自担风险:)

答案 4 :(得分:2)

我使用一个简单的额外应用程序,该应用程序在端口80上运行,并将人们重定向到https:

from flask import Flask,redirect

app = Flask(__name__)

@app.route('/')
def hello():
    return redirect("https://example.com", code=302)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

答案 5 :(得分:2)

这里是烧瓶解决方案,如果您在双脚上并且在负载均衡器后面。将其放在您的views.py

@app.before_request
def before_request():
    scheme = request.headers.get('X-Forwarded-Proto')
    if scheme and scheme == 'http' and request.url.startswith('http://'):
        url = request.url.replace('http://', 'https://', 1)
        code = 301
        return redirect(url, code=code)

答案 6 :(得分:1)

感谢Kelly Keller-Heikkila的回答和jaysqrd的评论,我最终在Flask应用程序中做到了这一点:

from flask import request, redirect
...

@app.before_request
def before_request():
    if not request.is_secure and app.env != "development":
        url = request.url.replace("http://", "https://", 1)
        code = 301
        return redirect(url, code=code)

我尝试了Rodolfo Alvarez建议的flask_sslify解决方案,但遇到了this issue并选择了上述解决方案。

使用app.env检查可以在不使用https的情况下运行单元测试和本地开发。

答案 7 :(得分:1)

我遇到了在负载均衡器后面的AWS Elastic Beanstalk中运行Flask应用程序的相同解决方案。以下AWS文档提供了两个步骤来配置http重定向的环境:https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/configuring-https-httpredirect.html完成上述两个步骤后,解决了我的问题。

要注意的一件事是,您必须在应用程序源包的根目录级别创建.ebextenions文件夹,并将配置文件添加到该.ebextensions文件夹。这里的自述文件:https://github.com/awsdocs/elastic-beanstalk-samples对此进行了更详细的说明。

答案 8 :(得分:1)

我已经成功使用的其他答案的替代方法:

from http import HTTPStatus
from typing import Optional

from flask import Response, redirect, request, url_for


def https_redirect() -> Optional[Response]:
    if request.scheme == 'http':
        return redirect(url_for(request.endpoint,
                                _scheme='https',
                                _external=True),
                        HTTPStatus.PERMANENT_REDIRECT)


# ..

if app.env == 'production':
    app.before_request(https_redirect)

# ..

答案 9 :(得分:0)

我正在使用在负载均衡器后面的cloud Foundry python应用程序(例如https://stackoverflow.com/users/5270172/kelly-keller-heikkila说)。 此解决方案通过添加(_external和_Scheme到url_for函数中)对我有所帮助。 https://github.com/pallets/flask/issues/773

答案 10 :(得分:0)

出于某种原因,来自具有VPC端点的私有AWS API网关的请求似乎不包含“ X-Forwarded-Proto”标头。这可能会破坏其他一些解决方案(要么不起作用,要么不断重定向到相同的URL)。以下中间件在大多数烧瓶生成的内部重定向上强制使用https:

class ForceHttpsRedirects:
    def __init__(self, app):
        self.app = app
    
    def __call__(self, environ, start_response):
        environ["wsgi.url_scheme"] = "https"
        return self.app(environ, start_response)

# Usage
app = flask.Flask(__name__)
app.wsgi_app = ForceHttpsRedirects(app.wsgi_app) # Add middleware to force all redirects to https

答案 11 :(得分:0)

我遇到了同样的问题,我的是一个蛮力解决方案,但它有效。 Heroku 过去建议使用 flask_sslify,现在不再维护。现在 Flask 中的正确方法应该是 flask-talisman,但我尝试过,它与 boostrap 模板的交互很差。 我尝试了 anulaibar 解决方案,但它并不总是对我有用。 以下是我想出的:

@app.before_request
def before_request():
    # If the request is sicure it should already be https, so no need to redirect
    if not request.is_secure:
        currentUrl = request.url
        if currentUrl.startswith('http://'):
            # http://example.com -> https://example.com
            # http://www.example.com -> https://www.example.com
            redirectUrl = currentUrl.replace('http://', 'https://', 1)
        elif currentUrl.startswith('www'):
            # Here we redirect the case in which the user access the site without typing any http or https
            # www.example.com -> https://www.example.com
            redirectUrl = currentUrl.replace('www', 'https://www', 1)
        else:
            # I do not now when this may happen, just for safety
            redirectUrl = 'https://www.example.com'
        code = 301
        return redirect(redirectUrl, code=code)

我在 Godaddy 中注册了域,该域也重定向到 https://www.example.com

答案 12 :(得分:0)

在应用引擎 flex 上,添加:

from werkzeug.middleware.proxy_fix import ProxyFix

def create_app(config=None):

    app = Flask(__name__)
    app.wsgi_app = ProxyFix(app.wsgi_app)

除了解决方案:

@app.before_request
def before_request():
    if not request.is_secure:
        url = request.url.replace('http://', 'https://', 1)
        code = 301
        return redirect(url, code=code)

否则它会导致无限重定向,因为 SSL 在代理后面被解开,但会在标头中注明。