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装饰器

    
    
  • 相关阅读:
    go函数
    Linux 查看磁盘容量、查找大文件、查找大目录
    五分钟理解一致性哈希算法(consistent hashing)
    使用Java实现三个线程交替打印0-74
    Python实现IOC控制反转
    Wannafly挑战赛5 A珂朵莉与宇宙 前缀和+枚举平方数
    Yandex Big Data Essentials Week1 Scaling Distributed File System
    Yandex Big Data Essentials Week1 Unix Command Line Interface Processes managing
    Yandex Big Data Essentials Week1 Unix Command Line Interface File Content exploration
    Yandex Big Data Essentials Week1 Unix Command Line Interface File System exploration
  • 原文地址:https://www.cnblogs.com/yamx/p/13334283.html
Copyright © 2011-2022 走看看