summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
Diffstat (limited to 'django')
-rw-r--r--django/conf/global_settings.py3
-rw-r--r--django/conf/project_template/settings.py2
-rw-r--r--django/middleware/clickjacking.py51
-rw-r--r--django/views/decorators/clickjacking.py64
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)