【发布时间】:2012-01-16 05:23:24
【问题描述】:
我在 Heroku(Cedar 堆栈)上有 python/django 应用程序,并希望仅通过 https 访问它。我启用了“ssl piggyback”选项,并且可以通过 https 连接到它。
但是禁用 http 访问或重定向到 https 的最佳方法是什么?
【问题讨论】:
标签: python django ssl https heroku
我在 Heroku(Cedar 堆栈)上有 python/django 应用程序,并希望仅通过 https 访问它。我启用了“ssl piggyback”选项,并且可以通过 https 连接到它。
但是禁用 http 访问或重定向到 https 的最佳方法是什么?
【问题讨论】:
标签: python django ssl https heroku
将@CraigKerstiens 和@allanlei 的答案结合到我测试过的东西中,并验证可以工作。 Heroku 在请求是 ssl 时将 HTTP_X_FORWARDED_PROTO 设置为 https,我们可以使用它来检查:
from django.conf import settings
from django.http import HttpResponseRedirect
class SSLMiddleware(object):
def process_request(self, request):
if not any([settings.DEBUG, request.is_secure(), request.META.get("HTTP_X_FORWARDED_PROTO", "") == 'https']):
url = request.build_absolute_uri(request.get_full_path())
secure_url = url.replace("http://", "https://")
return HttpResponseRedirect(secure_url)
【讨论】:
Django 1.8 将核心支持非 HTTPS 重定向(集成自 django-secure):
SECURE_SSL_REDIRECT = True # [1]
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
为了处理SECURE_SSL_REDIRECT,您必须使用SecurityMiddleware:
MIDDLEWARE = [
...
'django.middleware.security.SecurityMiddleware',
]
[1]https://docs.djangoproject.com/en/1.8/ref/settings/#secure-ssl-redirect
【讨论】:
不确定@CraigKerstiens 的回答是否考虑到request.is_secure() 总是返回False 如果在Heroku 的反向代理之后而不是“固定”。如果我没记错的话,这会导致 HTTP 重定向循环。
如果您使用 gunicorn 运行 Django,另一种方法是将以下内容添加到 gunicorn 的配置中
secure_scheme_headers = {
'X-FORWARDED-PROTO': 'https'
}
在你的 Procfile 中运行一些类似的东西
web: python manage.py run_gunicorn -b 0.0.0.0:$PORT -c config/gunicorn.conf
通过设置 gunicorn 的 secure-scheme-header,request.is_secure() 将在 https 请求上正确返回 True。见Gunicorn Config。
现在@CraigKerstiens 的中间件可以正常工作,包括在您的应用中对request.is_secure() 的任何调用。
注意:Django 也有相同的配置设置调用SECURE_PROXY_SSL_HEADER,但在开发版本中。
【讨论】:
您的应用程序使用什么框架?如果您使用的是 Django,您可以简单地使用一些类似于以下的中间件:
import re
from django.conf import settings
from django.core import urlresolvers
from django.http import HttpResponse, HttpResponseRedirect
class SSLMiddleware(object):
def process_request(self, request):
if not any([settings.DEBUG, request.is_secure()]):
url = request.build_absolute_uri(request.get_full_path())
secure_url = url.replace("http://", "https://")
return HttpResponseRedirect(secure_url)
【讨论】:
2020 年更新:
如果您使用的是 Flask,我会推荐以下内容:
@app.before_request
def before_request():
if 'DYNO' in os.environ:
if request.url.startswith('http://'):
url = request.url.replace('http://', 'https://', 1)
code = 301
return redirect(url, code=code)
以上在 Heroku 上运行良好,并允许您在本地开发中使用 http 和 heroku local。
Flask-SSLify 不再维护,也不再受到 Flask 社区的官方支持。
2014 年原始答案:
如果您使用的是 Flask,则效果很好:
(github在这里:https://github.com/kennethreitz/flask-sslify)
from flask_sslify import SSLify
if 'DYNO' in os.environ: # only trigger SSLify if the app is running
on Heroku
sslify = SSLify(app)
【讨论】:
对于 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。
【讨论】: