diff options
Diffstat (limited to 'django')
| -rw-r--r-- | django/conf/global_settings.py | 3 | ||||
| -rw-r--r-- | django/conf/project_template/settings.py | 2 | ||||
| -rw-r--r-- | django/middleware/clickjacking.py | 51 | ||||
| -rw-r--r-- | django/views/decorators/clickjacking.py | 64 |
4 files changed, 120 insertions, 0 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index c98cab763d..288dc9a1d9 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -406,6 +406,9 @@ URL_VALIDATOR_USER_AGENT = "Django/%s (http://www.djangoproject.com)" % get_vers DEFAULT_TABLESPACE = '' DEFAULT_INDEX_TABLESPACE = '' +# Default X-Frame-Options header value +X_FRAME_OPTIONS = 'SAMEORIGIN' + ############## # MIDDLEWARE # ############## diff --git a/django/conf/project_template/settings.py b/django/conf/project_template/settings.py index b74408ace8..e719dec5db 100644 --- a/django/conf/project_template/settings.py +++ b/django/conf/project_template/settings.py @@ -98,6 +98,8 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', + # Uncomment the next line for simple clickjacking protection: + # 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) ROOT_URLCONF = '{{ project_name }}.urls' diff --git a/django/middleware/clickjacking.py b/django/middleware/clickjacking.py new file mode 100644 index 0000000000..81763ef41f --- /dev/null +++ b/django/middleware/clickjacking.py @@ -0,0 +1,51 @@ +""" +Clickjacking Protection Middleware. + +This module provides a middleware that implements protection against a +malicious site loading resources from your site in a hidden frame. +""" + +from django.conf import settings + +class XFrameOptionsMiddleware(object): + """ + Middleware that sets the X-Frame-Options HTTP header in HTTP responses. + + Does not set the header if it's already set or if the response contains + a xframe_options_exempt value set to True. + + By default, sets the X-Frame-Options header to 'SAMEORIGIN', meaning the + response can only be loaded on a frame within the same site. To prevent the + response from being loaded in a frame in any site, set X_FRAME_OPTIONS in + your project's Django settings to 'DENY'. + + Note: older browsers will quietly ignore this header, thus other + clickjacking protection techniques should be used if protection in those + browsers is required. + + http://en.wikipedia.org/wiki/Clickjacking#Server_and_client + """ + def process_response(self, request, response): + # Don't set it if it's already in the response + if response.get('X-Frame-Options', None) is not None: + return response + + # Don't set it if they used @xframe_options_exempt + if getattr(response, 'xframe_options_exempt', False): + return response + + response['X-Frame-Options'] = self.get_xframe_options_value(request, + response) + return response + + def get_xframe_options_value(self, request, response): + """ + Gets the value to set for the X_FRAME_OPTIONS header. + + By default this uses the value from the X_FRAME_OPTIONS Django + settings. If not found in settings, defaults to 'SAMEORIGIN'. + + This method can be overridden if needed, allowing it to vary based on + the request or response. + """ + return getattr(settings, 'X_FRAME_OPTIONS', 'SAMEORIGIN').upper() diff --git a/django/views/decorators/clickjacking.py b/django/views/decorators/clickjacking.py new file mode 100644 index 0000000000..fcd78871dd --- /dev/null +++ b/django/views/decorators/clickjacking.py @@ -0,0 +1,64 @@ +from functools import wraps + +from django.utils.decorators import available_attrs + + +def xframe_options_deny(view_func): + """ + Modifies a view function so its response has the X-Frame-Options HTTP + header set to 'DENY' as long as the response doesn't already have that + header set. + + e.g. + + @xframe_options_deny + def some_view(request): + ... + + """ + def wrapped_view(*args, **kwargs): + resp = view_func(*args, **kwargs) + if resp.get('X-Frame-Options', None) is None: + resp['X-Frame-Options'] = 'DENY' + return resp + return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) + + +def xframe_options_sameorigin(view_func): + """ + Modifies a view function so its response has the X-Frame-Options HTTP + header set to 'SAMEORIGIN' as long as the response doesn't already have + that header set. + + e.g. + + @xframe_options_sameorigin + def some_view(request): + ... + + """ + def wrapped_view(*args, **kwargs): + resp = view_func(*args, **kwargs) + if resp.get('X-Frame-Options', None) is None: + resp['X-Frame-Options'] = 'SAMEORIGIN' + return resp + return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) + + +def xframe_options_exempt(view_func): + """ + Modifies a view function by setting a response variable that instructs + XFrameOptionsMiddleware to NOT set the X-Frame-Options HTTP header. + + e.g. + + @xframe_options_exempt + def some_view(request): + ... + + """ + def wrapped_view(*args, **kwargs): + resp = view_func(*args, **kwargs) + resp.xframe_options_exempt = True + return resp + return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) |
