Ticket #14261: clickjacking_patch_with_docs.diff
File clickjacking_patch_with_docs.diff, 18.8 KB (added by , 14 years ago) |
---|
-
tests/regressiontests/middleware/tests.py
### Eclipse Workspace Patch 1.0 #P django-the-trunk
2 2 3 3 from django.conf import settings 4 4 from django.http import HttpRequest 5 from django.http import HttpResponse 6 from django.middleware.clickjacking import XFrameOptionsMiddleware 5 7 from django.middleware.common import CommonMiddleware 6 8 from django.middleware.http import ConditionalGetMiddleware 7 9 from django.test import TestCase … … 334 336 self.resp['Last-Modified'] = 'Sat, 12 Feb 2011 17:41:44 GMT' 335 337 self.resp = ConditionalGetMiddleware().process_response(self.req, self.resp) 336 338 self.assertEqual(self.resp.status_code, 200) 339 340 class XFrameOptionsMiddlewareTest(TestCase): 341 """ 342 Tests for the X-Frame-Options clickjacking prevention middleware. 343 """ 344 def tearDown(self): 345 if hasattr(settings, 'X_FRAME_OPTIONS'): 346 delattr(settings, 'X_FRAME_OPTIONS') 347 348 def test_same_origin(self): 349 """ 350 Tests that the X_FRAME_OPTIONS setting can be set to SAMEORIGIN to 351 have the middleware use that value for the HTTP header. 352 """ 353 settings.X_FRAME_OPTIONS = 'SAMEORIGIN' 354 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 355 HttpResponse()) 356 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 357 358 settings.X_FRAME_OPTIONS = 'sameorigin' 359 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 360 HttpResponse()) 361 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 362 363 def test_deny(self): 364 """ 365 Tests that the X_FRAME_OPTIONS setting can be set to DENY to 366 have the middleware use that value for the HTTP header. 367 """ 368 settings.X_FRAME_OPTIONS = 'DENY' 369 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 370 HttpResponse()) 371 self.assertEqual(r['X-FRAME-OPTIONS'], 'DENY') 372 373 settings.X_FRAME_OPTIONS = 'deny' 374 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 375 HttpResponse()) 376 self.assertEqual(r['X-FRAME-OPTIONS'], 'DENY') 377 378 def test_defaults_sameorigin(self): 379 """ 380 Tests that if the X_FRAME_OPTIONS setting is not set then it defaults 381 to SAMEORIGIN. 382 """ 383 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 384 HttpResponse()) 385 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 386 387 def test_dont_set_if_set(self): 388 """ 389 Tests that if the X-FRAME-OPTIONS header is already set then the 390 middleware does not attempt to override it. 391 """ 392 settings.X_FRAME_OPTIONS = 'DENY' 393 response = HttpResponse() 394 response['X-FRAME-OPTIONS'] = 'SAMEORIGIN' 395 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 396 response) 397 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 398 399 settings.X_FRAME_OPTIONS = 'SAMEORIGIN' 400 response = HttpResponse() 401 response['X-FRAME-OPTIONS'] = 'DENY' 402 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 403 response) 404 self.assertEqual(r['X-FRAME-OPTIONS'], 'DENY') 405 406 def test_response_exempt(self): 407 """ 408 Tests that if the response has a xframe_options_exempt attribute set 409 to False then it still sets the header, but if it's set to True then 410 it does not. 411 """ 412 settings.X_FRAME_OPTIONS = 'SAMEORIGIN' 413 response = HttpResponse() 414 response.xframe_options_exempt = False 415 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 416 response) 417 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 418 419 response = HttpResponse() 420 response.xframe_options_exempt = True 421 r = XFrameOptionsMiddleware().process_response(HttpRequest(), 422 response) 423 self.assertEqual(r.get('X-FRAME-OPTIONS', None), None) 424 425 def test_is_extendable(self): 426 """ 427 Tests that the XFrameOptionsMiddleware method that determines the 428 X-FRAME-OPTIONS header value can be overridden based on something in 429 the request or response. 430 """ 431 class OtherXFrameOptionsMiddleware(XFrameOptionsMiddleware): 432 # This is just an example for testing purposes... 433 def get_xframe_options_value(self, request, response): 434 if getattr(request, 'sameorigin', False): 435 return 'SAMEORIGIN' 436 if getattr(response, 'sameorigin', False): 437 return 'SAMEORIGIN' 438 return 'DENY' 439 440 settings.X_FRAME_OPTIONS = 'DENY' 441 response = HttpResponse() 442 response.sameorigin = True 443 r = OtherXFrameOptionsMiddleware().process_response(HttpRequest(), 444 response) 445 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 446 447 request = HttpRequest() 448 request.sameorigin = True 449 r = OtherXFrameOptionsMiddleware().process_response(request, 450 HttpResponse()) 451 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 452 453 settings.X_FRAME_OPTIONS = 'SAMEORIGIN' 454 r = OtherXFrameOptionsMiddleware().process_response(HttpRequest(), 455 HttpResponse()) 456 self.assertEqual(r['X-FRAME-OPTIONS'], 'DENY') -
docs/ref/contrib/index.txt
24 24 25 25 admin/index 26 26 auth 27 clickjacking 27 28 comments/index 28 29 contenttypes 29 30 csrf -
docs/index.txt
167 167 * :doc:`Admin site <ref/contrib/admin/index>` | :doc:`Admin actions <ref/contrib/admin/actions>` | :doc:`Admin documentation generator<ref/contrib/admin/admindocs>` 168 168 * :doc:`Authentication <topics/auth>` 169 169 * :doc:`Cache system <topics/cache>` 170 * :doc:`C onditional content processing <topics/conditional-view-processing>`170 * :doc:`Clickjacking protection <ref/contrib/clickjacking>` 171 171 * :doc:`Comments <ref/contrib/comments/index>` | :doc:`Moderation <ref/contrib/comments/moderation>` | :doc:`Custom comments <ref/contrib/comments/custom>` 172 * :doc:`Conditional content processing <topics/conditional-view-processing>` 172 173 * :doc:`Content types <ref/contrib/contenttypes>` 173 174 * :doc:`Cross Site Request Forgery protection <ref/contrib/csrf>` 174 175 * :doc:`Databrowse <ref/contrib/databrowse>` -
tests/regressiontests/decorators/tests.py
13 13 from django.views.decorators.http import require_http_methods, require_GET, require_POST 14 14 from django.views.decorators.vary import vary_on_headers, vary_on_cookie 15 15 from django.views.decorators.cache import cache_page, never_cache, cache_control 16 from django.views.decorators.clickjacking import xframe_options_deny, xframe_options_sameorigin, xframe_options_exempt 17 from django.middleware.clickjacking import XFrameOptionsMiddleware 16 18 17 19 18 20 def fully_decorated(request): … … 183 185 184 186 self.assertEqual(Test.method.__doc__, 'A method') 185 187 self.assertEqual(Test.method.im_func.__name__, 'method') 188 189 class XFrameOptionsDecoratorsTests(TestCase): 190 """ 191 Tests for the X-FRAME-OPTIONS decorators. 192 """ 193 def test_deny_decorator(self): 194 """ 195 Ensures @xframe_options_deny properly sets the X-FRAME-OPTIONS header. 196 """ 197 @xframe_options_deny 198 def a_view(request): 199 return HttpResponse() 200 r = a_view(HttpRequest()) 201 self.assertEqual(r['X-FRAME-OPTIONS'], 'DENY') 202 203 def test_sameorigin_decorator(self): 204 """ 205 Ensures @xframe_options_sameorigin properly sets the X-FRAME-OPTIONS 206 header. 207 """ 208 @xframe_options_sameorigin 209 def a_view(request): 210 return HttpResponse() 211 r = a_view(HttpRequest()) 212 self.assertEqual(r['X-FRAME-OPTIONS'], 'SAMEORIGIN') 213 214 def test_exempt_decorator(self): 215 """ 216 Ensures @xframe_options_exempt properly instructs the 217 XFrameOptionsMiddleware to NOT set the header. 218 """ 219 @xframe_options_exempt 220 def a_view(request): 221 return HttpResponse() 222 req = HttpRequest() 223 resp = a_view(req) 224 self.assertEqual(resp.get('X-FRAME-OPTIONS', None), None) 225 self.assertTrue(resp.xframe_options_exempt) 226 227 # Since the real purpose of the exempt decorator is to suppress 228 # the middleware's functionality, let's make sure it actually works... 229 r = XFrameOptionsMiddleware().process_response(req, resp) 230 self.assertEqual(r.get('X-FRAME-OPTIONS', None), None) -
django/views/decorators/clickjacking.py
1 from django.utils.decorators import available_attrs 2 3 try: 4 from functools import wraps 5 except ImportError: 6 from django.utils.functional import wraps # Python 2.4 fallback. 7 8 def xframe_options_deny(view_func): 9 """ 10 Modifies a view function so its response has the X-FRAME-OPTIONS HTTP 11 header set to 'DENY' as long as the response doesn't already have that 12 header set. 13 14 e.g. 15 16 @xframe_options_deny 17 def some_view(request): 18 ... 19 20 """ 21 def wrapped_view(*args, **kwargs): 22 resp = view_func(*args, **kwargs) 23 if resp.get('X-FRAME-OPTIONS', None) is None: 24 resp['X-FRAME-OPTIONS'] = 'DENY' 25 return resp 26 return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) 27 28 def xframe_options_sameorigin(view_func): 29 """ 30 Modifies a view function so its response has the X-FRAME-OPTIONS HTTP 31 header set to 'SAMEORIGIN' as long as the response doesn't already have 32 that header set. 33 34 e.g. 35 36 @xframe_options_sameorigin 37 def some_view(request): 38 ... 39 40 """ 41 def wrapped_view(*args, **kwargs): 42 resp = view_func(*args, **kwargs) 43 if resp.get('X-FRAME-OPTIONS', None) is None: 44 resp['X-FRAME-OPTIONS'] = 'SAMEORIGIN' 45 return resp 46 return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) 47 48 def xframe_options_exempt(view_func): 49 """ 50 Modifies a view function by setting a response variable that instructs 51 XFrameOptionsMiddleware to NOT set the X-FRAME-OPTIONS HTTP header. 52 53 e.g. 54 55 @xframe_options_exempt 56 def some_view(request): 57 ... 58 59 """ 60 def wrapped_view(*args, **kwargs): 61 resp = view_func(*args, **kwargs) 62 resp.xframe_options_exempt = True 63 return resp 64 return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) -
django/middleware/clickjacking.py
1 """ 2 Clickjacking Protection Middleware. 3 4 This module provides a middleware that implements protection against a 5 malicious site loading resources from your site in a hidden frame. 6 """ 7 8 from django.conf import settings 9 10 class XFrameOptionsMiddleware(object): 11 """ 12 Middleware that sets the X-Frame-Options HTTP header in HTTP responses. 13 14 Does not set the header if it's already set or if the response contains 15 a xframe_options_exempt value set to True. 16 17 By default, sets the X-Frame-Options header to 'SAMEORIGIN', meaning the 18 response can only be loaded on a frame within the same site. To prevent 19 the response from being loaded in a frame in any site, set X_FRAME_OPTIONS 20 in your project's Django settings to 'DENY'. 21 22 Note: older browsers will quietly ignore this header, thus other 23 clickjacking protection techniques should be used if protection in those 24 browsers is required. 25 26 http://en.wikipedia.org/wiki/Clickjacking#Server_and_client 27 """ 28 def process_response(self, request, response): 29 # Don't set it if it's already in the response 30 if response.get('X-FRAME-OPTIONS', None) is not None: 31 return response 32 33 # Don't set it if they used @xframe_options_exempt 34 if getattr(response, 'xframe_options_exempt', False): 35 return response 36 37 response['X-FRAME-OPTIONS'] = self.get_xframe_options_value(request, 38 response) 39 return response 40 41 def get_xframe_options_value(self, request, response): 42 """ 43 Gets the value to set for the X_FRAME_OPTIONS header. 44 45 By default uses the value from the X_FRAME_OPTIONS Django settings. If 46 not found in settings, defaults to 'SAMEORIGIN'. 47 48 This method can be overridden if needing to flex based on the request 49 or response. 50 """ 51 return getattr(settings, 'X_FRAME_OPTIONS', 'SAMEORIGIN').upper() -
docs/ref/contrib/clickjacking.txt
1 ======================== 2 Clickjacking Protection 3 ======================== 4 5 .. module:: django.middleware.clickjacking 6 :synopsis: Protects against Clickjacking 7 8 The clickjacking middleware and decorators provide easy-to-use protection against `clickjacking`_. This type of attack occurs when a malicious site tricks a user into clicking on a concealed element of another site which they have loaded in a hidden frame or iframe. 9 10 .. _clickjacking: http://en.wikipedia.org/wiki/Clickjacking 11 12 An example of clickjacking 13 ========================== 14 Say an online store has a page where a logged in user can click "Buy Now" to purchase an item. A user has chosen to stay logged into the store all the time for convenience. An attacker site might try to load the store's page in a hidden iframe and overlay the "Buy Now" button with an "I Like Ponies" button. If the user visits the attacker site and clicks "I Like Ponies" he will inadvertently click on the online store's "Buy Now" button and unknowningly purchase the item. 15 16 Preventing clickjacking 17 ======================= 18 Modern browsers honor the `x-frame-options`_ HTTP header that indicates weather or not a resource is allowed to load within a frame or iframe. If the response contains the header with a value of SAMEORIGIN then the browser will only load the resource in a frame if the request originated from the same site. If the header is set to DENY then the browser will block the resource from loading in a frame no matter which site made the request. 19 20 .. _x-frame-options: https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header 21 22 Django provides a few simple ways to include this header in responses from your site: 23 24 1. A simple middleware that sets the header in all responses. 25 2. A set of view decorators that can be used to override the middleware or to only set the header for certain views. 26 27 How to use it 28 ============= 29 Setting x-frame-options for all responses 30 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31 To set the same x-frame-options value for all responses in your site, add ``'django.middleware.clickjacking.XFrameOptionsMiddleware'`` to :setting:`MIDDLEWARE_CLASSES`:: 32 33 MIDDLEWARE_CLASSES = ( 34 ... 35 'django.middleware.clickjacking.XFrameOptionsMiddleware', 36 ... 37 ) 38 39 By default, the middleware will set the x-frame-options header to SAMEORIGIN for every outgoing ``HttpResponse``. If you want DENY instead, set the :setting:`X_FRAME_OPTIONS` setting:: 40 41 X_FRAME_OPTIONS = 'DENY' 42 43 When using the middleware there may be some views where you do **not** want the x-frame-options header set. For those cases, you can use a view decorator that tells the middleware to not set the header:: 44 45 from django.http import HttpResponse 46 from django.views.decorators.clickjacking import xframe_options_exempt 47 48 @xframe_options_exempt 49 def ok_to_load_in_a_frame(request) 50 return HttpResponse("This page is safe to load in a frame on any site.") 51 52 53 Setting x-frame-options per view 54 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 55 To set the x-frame-options header on a per view basis, Django provides these decorators:: 56 57 from django.http import HttpResponse 58 from django.views.decorators.clickjacking import xframe_options_deny 59 from django.views.decorators.clickjacking import xframe_options_sameorigin 60 61 @xframe_options_deny 62 def view_one(request) 63 return HttpResponse("I won't display in any frame!") 64 65 @xframe_options_sameorigin 66 def view_two(request) 67 return HttpResponse("Display in a frame if it's from the same origin as me.") 68 69 Note that you can use the decorators in conjunction with the middleware. Use of a decorator overrides the middleware. 70 71 Limitations 72 =========== 73 The `x-frame-options` header will only protect against clickjacking in a modern browser. Older browsers will quietly ignore the header and need `other clickjacking prevention techniques`_. 74 75 Browsers that support x-frame-options 76 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 77 78 * Internet Explorer 8+ 79 * Firefox 3.6.9+ 80 * Opera 10.5+ 81 * Safari 4+ 82 * Chrome 4.1+ 83 84 See also 85 ~~~~~~~~ 86 A `complete list`_ of browsers supporting x-frame-options. 87 88 .. _complete list: https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header#Browser_compatibility 89 .. _other clickjacking prevention techniques: http://en.wikipedia.org/wiki/Clickjacking#Prevention 90 No newline at end of file