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

												

django—csrf中间件校验流程的更多相关文章

  1. 30.Django CSRF 中间件

    CSRF 1.概述 CSRF(Cross Site Request Forgery)跨站点伪造请求,举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用 ...

  2. 7.Django CSRF 中间件

    CSRF 1.概述 CSRF(Cross Site Request Forgery)跨站点伪造请求,举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用 ...

  3. django csrf 中间件

    CSRF和中间件 CSRF使用 说明csrf存在cookie中 全局使用 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', ...

  4. Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

    目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax c ...

  5. django csrf 处理简介

    CSRF 是什么 CSRF 即跨站请求伪造,在用户不知情的情况下向有漏洞的网站发送请求.例如有正常网站A,恶意网站B, 用户若对A B 两个网站都有访问,B 可能伪造请求到 A,比如提交表单.至于具体 ...

  6. Django的csrf中间件

    csrf中间件 ​ csrf 跨站请求伪造 ​ 补充两个装饰器: ​ from django.views.decorators.csrf import csrf_exempt,csrf_protect ...

  7. csrf 跨站请求伪造相关以及django的中间件

    django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware来完成. 1.django中常用的中间件? - proces ...

  8. python 全栈开发,Day87(ajax登录示例,CSRF跨站请求伪造,Django的中间件,自定义分页)

    一.ajax登录示例 新建项目login_ajax 修改urls.py,增加路径 from app01 import views urlpatterns = [ path('admin/', admi ...

  9. Django 中CSRF中间件 'django.middleware.csrf.CsrfViewMiddleware',

    1.Django中CSRF中间件的工作原理及form表单提交需要添加{% csrf_token %}防止出现403错误 CSRF # 表示django全局发送post请求均需要字符串验证功能:防止跨站 ...

随机推荐

  1. bootsrap 样式笔记

    颜色 --blue: #007bff; --indigo: #6610f2; --purple: #6f42c1; --pink: #e83e8c; --red: #dc3545; --orange: ...

  2. turtle空间坐标系

    利用空间坐标改变行进 以海龟的角度来看,无论往那个方向运行,都叫正方向 turtle.fd向海龟的正前方运行,turtle.bk向反方向运行

  3. hystrix熔断器之线程池

    隔离 Hystrix有两种隔离方式:信号量和线程池. 线程池隔离:对每个command创建一个自己的线程池,执行调用.通过线程池隔离来保证不同调用不会相互干扰和每一个调用的并发限制. 信号量隔热:对每 ...

  4. 2020DASCTF八月浪漫七夕战

    安恒大学 注入点在邮箱注册那里,无法复现了,提一下 ezflask 源代码 #!/usr/bin/env python # -*- coding: utf-8 -*- from flask impor ...

  5. SSH 框架常见错误

    新手学习SSH框架的时候总会遇到很多问题,一碰到404 或者500错误就不知道怎么解决. 404错误是很常见的一个错误,如果没有用框架基本上只可能是没有这个路径或者文件,但是用了框架之后404的原因就 ...

  6. OSPF及实验

    OSPF:Open Shortest Path First,最短路径优先1)基本概念:标准的LS型协议--共享拓扑组播更新:224.0.0.5/6触发更新,存在周期更新  30minOSPF是跨层封装 ...

  7. 手把手教你springboot中导出数据到excel中

    手把手教你springboot中导出数据到excel中 问题来源: 前一段时间公司的项目有个导出数据的需求,要求能够实现全部导出也可以多选批量导出(虽然不是我负责的,我自己研究了研究),我们的项目是x ...

  8. Python练习题 016:猴子吃桃

    [Python练习题 016] 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个.第二天早上又将剩下的桃子吃掉一半,又多吃了一个.以后每天早上都吃了前一天剩下的一半零一个.到 ...

  9. osgEarth使用笔记4——加载矢量数据

    目录 1. 概述 2. 详论 2.1. 基本绘制 2.2. 矢量符号化 2.2.1. 可见性 2.2.2. 高度设置 2.2.3. 符号化 2.2.4. 显示标注 2.3. 其他 3. 结果 4. 问 ...

  10. 093 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 03 static关键字(下)

    093 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...