Ticket #14261: clickjacking_patch_with_docs.diff

File clickjacking_patch_with_docs.diff, 18.8 KB (added by rniemeyer, 14 years ago)

Renamed patch to .diff i h8 eclipse

  • tests/regressiontests/middleware/tests.py

    ### Eclipse Workspace Patch 1.0
    #P django-the-trunk
     
    22
    33from django.conf import settings
    44from django.http import HttpRequest
     5from django.http import HttpResponse
     6from django.middleware.clickjacking import XFrameOptionsMiddleware
    57from django.middleware.common import CommonMiddleware
    68from django.middleware.http import ConditionalGetMiddleware
    79from django.test import TestCase
     
    334336        self.resp['Last-Modified'] = 'Sat, 12 Feb 2011 17:41:44 GMT'
    335337        self.resp = ConditionalGetMiddleware().process_response(self.req, self.resp)
    336338        self.assertEqual(self.resp.status_code, 200)
     339
     340class 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

     
    2424
    2525   admin/index
    2626   auth
     27   clickjacking
    2728   comments/index
    2829   contenttypes
    2930   csrf
  • docs/index.txt

     
    167167    * :doc:`Admin site <ref/contrib/admin/index>` | :doc:`Admin actions <ref/contrib/admin/actions>` | :doc:`Admin documentation generator<ref/contrib/admin/admindocs>`
    168168    * :doc:`Authentication <topics/auth>`
    169169    * :doc:`Cache system <topics/cache>`
    170     * :doc:`Conditional content processing <topics/conditional-view-processing>`
     170    * :doc:`Clickjacking protection <ref/contrib/clickjacking>`
    171171    * :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>`
    172173    * :doc:`Content types <ref/contrib/contenttypes>`
    173174    * :doc:`Cross Site Request Forgery protection <ref/contrib/csrf>`
    174175    * :doc:`Databrowse <ref/contrib/databrowse>`
  • tests/regressiontests/decorators/tests.py

     
    1313from django.views.decorators.http import require_http_methods, require_GET, require_POST
    1414from django.views.decorators.vary import vary_on_headers, vary_on_cookie
    1515from django.views.decorators.cache import cache_page, never_cache, cache_control
     16from django.views.decorators.clickjacking import xframe_options_deny, xframe_options_sameorigin, xframe_options_exempt
     17from django.middleware.clickjacking import XFrameOptionsMiddleware
    1618
    1719
    1820def fully_decorated(request):
     
    183185
    184186        self.assertEqual(Test.method.__doc__, 'A method')
    185187        self.assertEqual(Test.method.im_func.__name__, 'method')
     188
     189class 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

     
     1from django.utils.decorators import available_attrs
     2
     3try:
     4    from functools import wraps
     5except ImportError:
     6    from django.utils.functional import wraps  # Python 2.4 fallback.
     7
     8def 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
     28def 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
     48def 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"""
     2Clickjacking Protection Middleware.
     3
     4This module provides a middleware that implements protection against a
     5malicious site loading resources from your site in a hidden frame.
     6"""
     7
     8from django.conf import settings
     9
     10class 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========================
     2Clickjacking Protection
     3========================
     4
     5.. module:: django.middleware.clickjacking
     6   :synopsis: Protects against Clickjacking
     7
     8The 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
     12An example of clickjacking
     13==========================
     14Say 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
     16Preventing clickjacking
     17=======================
     18Modern 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
     22Django provides a few simple ways to include this header in responses from your site:
     23
     241. A simple middleware that sets the header in all responses.
     252. A set of view decorators that can be used to override the middleware or to only set the header for certain views.
     26
     27How to use it
     28=============   
     29Setting x-frame-options for all responses
     30~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     31To 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   
     39By 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
     43When 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
     53Setting x-frame-options per view
     54~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     55To 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       
     69Note that you can use the decorators in conjunction with the middleware.  Use of a decorator overrides the middleware. 
     70                                                                                             
     71Limitations
     72===========
     73The `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
     75Browsers 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
     84See also
     85~~~~~~~~
     86A `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
Back to Top