mirror of
https://github.com/kennethreitz/flask-sslify.git
synced 2026-06-05 23:00:19 +00:00
4f9d0b83d6
In some configurations, extension object is constructed while there is no current application. Without this fix, this extension doesn't support such setups.
80 lines
2.6 KiB
Python
80 lines
2.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from flask import request, redirect, current_app
|
|
|
|
YEAR_IN_SECS = 31536000
|
|
|
|
|
|
class SSLify(object):
|
|
"""Secures your Flask App."""
|
|
|
|
def __init__(self, app=None, age=YEAR_IN_SECS, subdomains=False, permanent=False, skips=None):
|
|
self.app = app or current_app
|
|
self.hsts_age = age
|
|
|
|
self.hsts_include_subdomains = subdomains
|
|
self.permanent = permanent
|
|
self.skip_list = skips
|
|
|
|
if app is not None:
|
|
self.init_app(app)
|
|
|
|
def init_app(self, app):
|
|
"""Configures the specified Flask app to enforce SSL."""
|
|
app.config.setdefault('SSLIFY_SUBDOMAINS', False)
|
|
app.config.setdefault('SSLIFY_PERMANENT', False)
|
|
app.config.setdefault('SSLIFY_SKIPS', None)
|
|
|
|
self.hsts_include_subdomains = self.hsts_include_subdomains or app.config['SSLIFY_SUBDOMAINS']
|
|
self.permanent = self.permanent or self.app.config['SSLIFY_PERMANENT']
|
|
self.skip_list = self.skip_list or self.app.config['SSLIFY_SKIPS']
|
|
|
|
app.before_request(self.redirect_to_ssl)
|
|
app.after_request(self.set_hsts_header)
|
|
|
|
@property
|
|
def hsts_header(self):
|
|
"""Returns the proper HSTS policy."""
|
|
hsts_policy = 'max-age={0}'.format(self.hsts_age)
|
|
|
|
if self.hsts_include_subdomains:
|
|
hsts_policy += '; includeSubDomains'
|
|
|
|
return hsts_policy
|
|
|
|
@property
|
|
def skip(self):
|
|
"""Checks the skip list."""
|
|
# Should we skip?
|
|
if self.skip_list and isinstance(self.skip_list, list):
|
|
for skip in self.skip_list:
|
|
if request.path.startswith('/{0}'.format(skip)):
|
|
return True
|
|
return False
|
|
|
|
def redirect_to_ssl(self):
|
|
"""Redirect incoming requests to HTTPS."""
|
|
# Should we redirect?
|
|
criteria = [
|
|
request.is_secure,
|
|
current_app.debug,
|
|
current_app.testing,
|
|
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
|
|
|
|
def set_hsts_header(self, response):
|
|
"""Adds HSTS header to each response."""
|
|
# Should we add STS header?
|
|
if request.is_secure and not self.skip:
|
|
response.headers.setdefault('Strict-Transport-Security', self.hsts_header)
|
|
return response
|