在 Flask+Heroku 上将 HTTP 重定向到 HTTPS

Posted

技术标签:

【中文标题】在 Flask+Heroku 上将 HTTP 重定向到 HTTPS【英文标题】:Redirect HTTP to HTTPS on Flask+Heroku 【发布时间】:2013-02-13 12:13:19 【问题描述】:

当我尝试将传入流量重定向到 https 时,我得到一个无限重定向循环。

@app.route('/checkout/')                                                                                                                                                                                        
def checkout():                                                                                                                                                                                                 
    checkout = "https://myapp.herokuapp.com/checkout/"                                                                                                                                              
    if checkout != request.url:                                                                                                                                                                             
        print checkout, request.url                                                                                                                                                                             
        return redirect(checkout)                                                                                                                                                                               
    return render_template('checkout.html', key=keys['publishable_key']) 

request.url 永远不会更改为 https 前缀。我想使用 heroku 的背驮式 ssl 来最小化成本。

【问题讨论】:

【参考方案1】:

在 Heroku 上,SSL (https) 在到达您的应用程序之前就被终止了,因此您的应用程序永远不会真正看到 SSL 流量。要检查是否使用 https 发出请求,您必须检查 x-forwarded-proto 标头。更多信息在这里:How to make python on Heroku https only?

更新:为了您的使用,您应该只检查 request.url 中的“myapp.herokuapp.com/checkout/”;并验证标头是“https”

【讨论】:

谢谢朋友。我已经看过 flask-sslify 包。除了我使用自定义域和heroku的子域之外,这将满足我的需求。我只想将 SSL 放在一页上。不过谢谢你的文档,我会通读的。 您应该只检查request.url 中的“myapp.herokuapp.com/checkout”并验证标头是否为“https” 谢谢,就是这样。 我已在答案中添加了评论 - 请考虑接受答案。 我会认真考虑 :) 如果您在回答中提到 flask-sslify,我会更加重视。【参考方案2】:

我能够将 flask-sslify 代码重新用于单个视图。只需要检查是否使用 SSL 发出请求并将正确的标头添加到响应中。 https://github.com/kennethreitz/flask-sslify

@app.route('/checkout/')                                                                                                                                                                                        
def checkout():                                                                                                                                                                                                 
    checkout = "https://myapp.herokuapp.com/checkout/"                                                                                                                                              
    if request.headers.get('X-Forwarded-Proto', 'http') == 'https':                                                                                                                                             
        resp = make_response(render_template('checkout.html', key=keys['publishable_key']))                                                                                                              
        return set_hsts_header(resp)                                                                                                                                                                            
    return redirect(checkout, code=302)                                                                                                                                                                         

def set_hsts_header(response):                                                                                                                                                                                  
    """Adds HSTS header to each response."""                                                                                                                                                                    
    response.headers.setdefault('Strict-Transport-Security', hsts_header)                                                                                                                                       
    return response                                                                                                                                                                                             

def hsts_header():                                                                                                                                                                                              
    """Returns the proper HSTS policy."""                                                                                                                                                                       
    hsts_policy = 'max-age=0'.format(31536000) #year in seconds                                                                                                                                               
    if self.hsts_include_subdomains:                                                                                                                                                                            
        hsts_policy += '; includeSubDomains'                                                                                                                                                                    
        return hsts_policy 

【讨论】:

【参考方案3】:

1) 执行“pip install flask-sslify”

(github在这里:https://github.com/kennethreitz/flask-sslify)

2) 包括以下几行:

from flask_sslify import SSLify
if 'DYNO' in os.environ: # only trigger SSLify if the app is running on Heroku
    sslify = SSLify(app)

【讨论】:

'DYNO' in os.environ 提示特别有用。【参考方案4】:

我尝试了 SSLify、url_for _scheme,并设置了 PREFERRED_URL_SCHEME;但是没有一个成功,至少在发布级别..(在本地工作得很好)然后我想;

@app.before_request
def beforeRequest():
    if not request.url.startswith('https'):
        return redirect(request.url.replace('http', 'https', 1))

这本质上是另一种无需任何配置或扩展即可完成的方法。

【讨论】:

这导致我“这个网页有一个重定向循环” @tdc 有趣,在有人编辑此答案之前,您是否尝试过我给出的答案?我没有试过这个,虽然它看起来应该是一样的;我知道原版肯定效果很好。 我没看过原版! SSLify 最终为我工作【参考方案5】:

您只需要检查X-Forwarded-Proto 标头。如果为 false,则重定向到等效的 https url。

这里是对运行在 heroku 上的烧瓶应用程序的所有调用强制执行 https 的代码:

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

【讨论】:

【参考方案6】:

你可以这样做:

@app.before_request
def before_request():
    if 'DYNO' in os.environ: # Only runs when on heroku
        if request.url.startswith('http://'):
            url = request.url.replace('http://', 'https://', 1)
            code = 301
            return redirect(url, code=code)

【讨论】:

【参考方案7】:

在我对another question 的回答中,我已经说明了最新的 Flask 建议。使用 Talisman 代替 SSLify。

对于 Flask,请使用 Talisman。 Flask、Heroku 和 SSLify 文档倾向于使用Talisman 而不是 SSLify,因为 后来不再维护。

来自SSLify:

不再维护扩展,更喜欢使用 Flask-Talisman 作为 Flask 安全指南鼓励这样做。

通过 pip 安装:

$ pip install flask-talisman

实例化扩展(示例):

from flask import Flask
from flask_talisman import Talisman

app = Flask(__name__)
if 'DYNO' in os.environ:
    Talisman(app)

Talisman 默认仅启用 CSP(内容安全策略) 允许加载来自同一域的资源。如果你想 禁用它并处理其影响:

Talisman(app, content_security_policy=None)

如果您不想禁用它,您已设置 content_security_policy 参数允许来自外部的资源 域,例如 CDN。为此,请参阅 documentation.

【讨论】:

以上是关于在 Flask+Heroku 上将 HTTP 重定向到 HTTPS的主要内容,如果未能解决你的问题,请参考以下文章

Flask-Login 和 Heroku 问题

Heroku:Python Flask 应用程序 - 自动从 https 重定向到 http

如何在 Mongoose 上将 MongoDB 连接到 Heroku?

Flask-SocketIO - WebSocket 在连接建立之前关闭。 [Heroku]

为啥我的 Flask 应用程序在 Heroku 上被检测为 node.js

Flask-Socketio 应用程序在 Heroku 上出现超时错误