zoukankan      html  css  js  c++  java
  • django—csrf中间件校验流程

      CSRF(跨站请求伪造)是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。

      这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。

        例如,一个用户刚在某个网站登录过,认证信息记录在浏览器中,此时不小心点进了一个钓鱼网站,钓鱼网站仿照用户真正登录的网页布局,一旦用户执行涉及财产的操作,那么他填写完表单后发生给了真正的网站。其实真正的网站接收到的是钓鱼网站提前设置好的数据。那么这次攻击的目的就实现了。

      django为了防止此类事件,便内置了csrf校验的中间件。

    CSRF中间件流程  

      class CsrfViewMiddleware(MiddlewareMixin):
        
        def _accept(self, request):
          request.csrf_processing_done = True
          return None
        def _reject(self, request, reason):
          response = _get_failure_view()(request, reason=reason)
          log_response(
          'Forbidden (%s): %s', reason, request.path,
           response=response,
          request=request,
           logger=logger,
          )
          return response
        def _get_token(self, request)
        def _set_token(self, request, response)
        def process_request(self, request)
        def process_view(self, request, callback, callback_args, callback_kwargs)
        def process_response(self, request, response)

      从上述代码段中,不难看出CSRF中间件下共有7个方法。

      其中前四个属于私有方法,后三个是中间件的方法。

      1、process_request

        一个请求到达视图函数前首先要经过中间件的process_request方法,所以从这个方法入手。

        def process_request(self, request):
        csrf_token = self._get_token(request)
        if csrf_token is not None:
        # Use same token next time.
        request.META['CSRF_COOKIE'] = csrf_token

        该方法主要功能:

          首先,需要通过_get_token方法从request对象中获取csrf_token;

          然后,判断当获取到的csrf_token不为空时,将其赋值给META字典中的CSRF_COOKIE字段 。

        _get_token方法:

        def _get_token(self, request):
        if settings.CSRF_USE_SESSIONS:
        try:
        return request.session.get(CSRF_SESSION_KEY)
        except AttributeError:
        raise ImproperlyConfigured(
        'CSRF_USE_SESSIONS is enabled, but request.session is not '
        'set. SessionMiddleware must appear before CsrfViewMiddleware '
        'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
        )
        else:
        try:
        cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
        except KeyError:
        return None

        csrf_token = _sanitize_token(cookie_token)
        if csrf_token != cookie_token:
        # Cookie token needed to be replaced;
        # the cookie needs to be reset.
        request.csrf_cookie_needs_reset = True
        return csrf_token

          a、先判断csrf的值是用什么方式存储,以便获取该值(默认是COOKIE存储,具体可见django的全局设置内的变量)

            from django.conf import global_settings
            以下变量出现在global_settings文件中
            # Settings for CSRF cookie.
            CSRF_COOKIE_NAME = 'csrftoken'
            CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52
            CSRF_COOKIE_DOMAIN = None
            CSRF_COOKIE_PATH = '/'
            CSRF_COOKIE_SECURE = False
            CSRF_COOKIE_HTTPONLY = False
            CSRF_COOKIE_SAMESITE = 'Lax'
            CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
            CSRF_TRUSTED_ORIGINS = []
            CSRF_USE_SESSIONS = False

          b、从cookie中根据键的名称取出csrf_token的值

          c、然后将cookie中取出的csrf_token传入_sanitize_token函数进行验证其是否合格,满足值的长度为64即合格,将该值原样返回

            def _sanitize_token(token):
            # Allow only ASCII alphanumerics
            if re.search('[^a-zA-Z0-9]', token):
            return _get_new_csrf_token()
            elif len(token) == CSRF_TOKEN_LENGTH:
            return token
            elif len(token) == CSRF_SECRET_LENGTH:
            return _salt_cipher_secret(token)
            return _get_new_csrf_token()

          d、如果经过验证后得到的csrftoken和原先的值不一样时,在request对象上新增一个属性,表示需要重置csrftoken的值

          e、如果一切正常,则将获取到的csrftoken返回给process_request方法

      2、process_view

        该方法位于process_request之后,且视图函数之前,所以接下来由它来处理request对象

        def process_view(self, request, callback, callback_args, callback_kwargs):
        if getattr(request, 'csrf_processing_done', False):
        return None

         if getattr(callback, 'csrf_exempt', False):
         return None

        # Assume that anything not defined as 'safe' by RFC7231 needs protection
        if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
         #内部代码块执行校验相关操作
        return self._accept(request)

         a、根据反射机制判断 request对象中是否有csrf_processing_done属性,有的话就不作csrf验证

          同理,判断对应的视图函数是否有csrf_exempt属性,有的话就不作csrf验证

        b、如果本次请求的请求方式是

            GET、HEAD、OPTIONS、TRACE中的一个,则接收本次请求,不需要csrf验证

        c、如果本次请求的请求方式不再上述方式内,则进行csrf验证

          

          if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
          if getattr(request, '_dont_enforce_csrf_checks', False):
           return self._accept(request)
      
           if request.is_secure():
           #当请求协议为HTTPS时的一些操作

           csrf_token = request.META.get('CSRF_COOKIE')
          if csrf_token is None:
          return self._reject(request, REASON_NO_CSRF_COOKIE)

           # Check non-cookie token for match.
          request_csrf_token = ""
          if request.method == "POST":
          try:
          request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
           except IOError:
          pass

          if request_csrf_token == "":
           request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')

           request_csrf_token = _sanitize_token(request_csrf_token)
          if not _compare_salted_tokens(request_csrf_token, csrf_token):
          return self._reject(request, REASON_BAD_TOKEN)
            return self._accept(request)

          1、排除掉接收本次请求的情况。

          2、然后将csrftoken从META字典中取出(process_request方法内将csrftoken存入该字典,这个csrftoken来自于cookie)

          3、判断csrftoken是否存在字典中,没有则拒绝本次请求

          4、然后从POST提交的数据中获取csrftoken,或者从请求头中获取csrftoken

          5、步骤4中获取到的csrftoken经过验证后,与cookie中存的csrftoken对比,如果不相等则拒绝本次请求。

            反之,接收该请求

      3、总结:

        django的csrf校验,主要是:

          a、从cookie中获取csrftoken

          b、从POST发来的数据中获取csrftoken或者从请求头中获取key为“x-csrftoken”对应的csrftoken

          然后比较a和b获取到的值是否一致

    关于CSRF校验的装饰器

      from django.views.decorators.csrf import csrf_exempt, csrf_protect, ensure_csrf_cookie

      1、csrf_exempt:加上该装饰器的视图函数,对该视图发起的请求不需要进行csrf校验,因为它会给视图函数加上csrd_exempt属性

        wrapped_view.csrf_exempt = True

      2、csrf_protect:加上该装饰器的视图函数,必须进行csrf校验

      3、ensure_csrf_cookie:加上该装饰器的视图函数,对应的页面中一定有存储csrf的cookie信息

      补充:

        cookie中必须有csrftoken的键值对时,才能够进行csrftoken校验

        a、在页面中使用{% csrf_token%}生成隐藏的csrfmiddlewaretoken的input标签的同时,会设置cookie中的csrftoken键值对

        b、给需要的视图函数加上ensure_csrf_cookie装饰器

    
    
  • 相关阅读:
    割点和割边
    差分约束
    错题本(持续更新)
    高中语文小说赏析问题
    CSPS2019游记
    【USACO09FEB】改造路Revamping Trails
    【SDOI2013】森林
    Nozaki_Chiyo的代码盒
    【HAOI2015】树上染色
    kruskal重构树
  • 原文地址:https://www.cnblogs.com/yamx/p/13334283.html
Copyright © 2011-2022 走看看