django中间件生成csrf_token

django中间件原理

class MiddlewareMixin:

	#django启动时就执行,与用户无关
    def __init__(self, get_response=None):
        self.get_response = get_response
        super().__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
		#request到达views之前执行,如果在process_request函数中有返回值一般就会直接返回的不会执行以后的中间件
        response = response or self.get_response(request)
        #后面的中间件和views执行完成后返回
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response

例子:csrf_token:csrf原理:https://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html
推荐一篇关与csrf中间件的文章 https://blog.csdn.net/qq_27952549/article/details/82392790
CSRF攻击的过程

1.用户C浏览并登陆信任的站点A
2.A验证通过,在用户C处产生A的Cookie
3.用户C在没有登陆的情况下访问攻击站点B
4.B要求访问第三方的站点A,发出一个请求
5.根据B在4的请求,浏览器携带2产生的cookie访问站点A
6.A不知道5中的请求是用户C发出的还是B发出的,由于浏览器会自动带上用户C的Cookie,所以A会个根据C的权限处理5的请求,这样B就达到了模拟用户登录的目的

如何防止CSRF的攻击?

1.在客户端向后端请求界面数据的时候,后端会往响应中的cookie中设置csrf_token的值
2.在Form表单中添加一个隐藏字段,值也是csrf_token
3.在用户提交的时候,会带上这两个值向后台发起请求
4.后端接收到请求以后,会做三件事:
从cookie中取出csrf_token
从表单数据中取出隐藏的csrf_token的值
将这两个值进行比对
5.如果比较后两个值一样,那么代表是正常的请求,如果没有取到或者比较不一致,代表不是正常的请求,不执行下一步操作

总结:就是说你虽然替他登录了,但是以后的访问还必须需要你的token才能进行一些修改数据的操作,不然还是不能执行。

token值更新速度快,值保存在前端,cookie中和要提交表单中,然后后端进行比较这两值。

生成cookie

def get_token(request):
    """
    Return the CSRF token required for a POST form. The token is an
    alphanumeric value. A new token is created if one is not already set.

    A side effect of calling this function is to make the csrf_protect
    decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
    header to the outgoing response.  For this reason, you may need to use this
    function lazily, as is done by the csrf context processor.
    """
	#如果request.META中没有CSRF_COOKIE,就会给添加一个CSRF_COOKIE字段
    if "CSRF_COOKIE" not in request.META:
        csrf_secret = _get_new_csrf_string()
        request.META["CSRF_COOKIE"] = _salt_cipher_secret(csrf_secret)
    else:
    	#如果有CSRF_COOKIE就解密
        csrf_secret = _unsalt_cipher_token(request.META["CSRF_COOKIE"])
    request.META["CSRF_COOKIE_USED"] = True
    return _salt_cipher_secret(csrf_secret)

给response设置cookie

    def _set_token(self, request, response):
        if settings.CSRF_USE_SESSIONS:
            request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE']
        else:
            response.set_cookie(
                settings.CSRF_COOKIE_NAME,
                request.META['CSRF_COOKIE'],
                max_age=settings.CSRF_COOKIE_AGE,
                domain=settings.CSRF_COOKIE_DOMAIN,
                path=settings.CSRF_COOKIE_PATH,
                secure=settings.CSRF_COOKIE_SECURE,
                httponly=settings.CSRF_COOKIE_HTTPONLY,
                samesite=settings.CSRF_COOKIE_SAMESITE,
            )
            # Set the Vary header since content varies with the CSRF cookie.
            patch_vary_headers(response, ('Cookie',))

在中间件执行process_response方法时,给request设置cookie

    def process_response(self, request, response):
        if not getattr(request, 'csrf_cookie_needs_reset', False):
            if getattr(response, 'csrf_cookie_set', False):
                return response

        if not request.META.get("CSRF_COOKIE_USED", False):
            return response

        # Set the CSRF cookie even if it's already set, so we renew
        # the expiry timer.
        self._set_token(request, response)
        response.csrf_cookie_set = True
        return response

使用中间件生成token

from django.middleware.csrf import get_token
from django.utils.deprecation import MiddlewareMixin

class Middleware(MiddlewareMixin):
	
	def process_request(self,request):
		get_token(request)


django中间件生成csrf_token_第1张图片

django关于csrf_token的问题:

我前面是把csrf_token的中间件给注释了
我们可以看到在中间件中有这样一个函数

class CsrfViewMiddleware(MiddlewareMixin):

	    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


		#在views.py之前进行
	    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
		
		    def process_view(self, request, callback, callback_args, callback_kwargs):
        if getattr(request, 'csrf_processing_done', False):
            return None

        # Wait until request.META["CSRF_COOKIE"] has been manipulated before
        # bailing out, so that get_token still works
        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'):
            if getattr(request, '_dont_enforce_csrf_checks', False):
                # Mechanism to turn off CSRF checks for test suite.
                # It comes after the creation of CSRF cookies, so that
                # everything else continues to work exactly the same
                # (e.g. cookies are sent, etc.), but before any
                # branches that call reject().
                return self._accept(request)

            if request.is_secure():
                # Suppose user visits http://example.com/
                # An active network attacker (man-in-the-middle, MITM) sends a
                # POST form that targets https://example.com/detonate-bomb/ and
                # submits it via JavaScript.
                #
                # The attacker will need to provide a CSRF cookie and token, but
                # that's no problem for a MITM and the session-independent
                # secret we're using. So the MITM can circumvent the CSRF
                # protection. This is true for any HTTP connection, but anyone
                # using HTTPS expects better! For this reason, for
                # https://example.com/ we need additional protection that treats
                # http://example.com/ as completely untrusted. Under HTTPS,
                # Barth et al. found that the Referer header is missing for
                # same-domain requests in only about 0.2% of cases or less, so
                # we can use strict Referer checking.
                referer = request.META.get('HTTP_REFERER')
                if referer is None:
                    return self._reject(request, REASON_NO_REFERER)

                referer = urlparse(referer)

                # Make sure we have a valid URL for Referer.
                if '' in (referer.scheme, referer.netloc):
                    return self._reject(request, REASON_MALFORMED_REFERER)

                # Ensure that our Referer is also secure.
                if referer.scheme != 'https':
                    return self._reject(request, REASON_INSECURE_REFERER)

                # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match
                # match on host:port. If not, obey the cookie rules (or those
                # for the session cookie, if CSRF_USE_SESSIONS).
                good_referer = (
                    settings.SESSION_COOKIE_DOMAIN
                    if settings.CSRF_USE_SESSIONS
                    else settings.CSRF_COOKIE_DOMAIN
                )
                if good_referer is not None:
                    server_port = request.get_port()
                    if server_port not in ('443', '80'):
                        good_referer = '%s:%s' % (good_referer, server_port)
                else:
                    try:
                        # request.get_host() includes the port.
                        good_referer = request.get_host()
                    except DisallowedHost:
                        pass

                # Create a list of all acceptable HTTP referers, including the
                # current host if it's permitted by ALLOWED_HOSTS.
                good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
                if good_referer is not None:
                    good_hosts.append(good_referer)

                if not any(is_same_domain(referer.netloc, host) for host in good_hosts):
                    reason = REASON_BAD_REFERER % referer.geturl()
                    return self._reject(request, reason)

            csrf_token = request.META.get('CSRF_COOKIE')
            if csrf_token is None:
                # No CSRF cookie. For POST requests, we insist on a CSRF cookie,
                # and in this way we can avoid all CSRF attacks, including login
                # CSRF.
                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:
                    # Handle a broken connection before we've completed reading
                    # the POST data. process_view shouldn't raise any
                    # exceptions, so we'll ignore and serve the user a 403
                    # (assuming they're still listening, which they probably
                    # aren't because of the error).
                    pass

            if request_csrf_token == "":
                # Fall back to X-CSRFToken, to make things easier for AJAX,
                # and possible for PUT/DELETE.
                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、我们看到在process_request函数中会拿到cookie中的csrf-token值,然后在process_view函数中判断post等修改数据请求中拿到另一个token值,得到前端传来的request_csrf_token的值,因此我们必须得到前端传来token,不然就会报错,生成token的方式有很多 比如配合form的{%csrf_token%} 也可以设置全局的token值
这里使用中间件生成token值,作用与所有视图和模板

2、django中的cookie中的csrf_token由CsrfViewMiddleware设置,如果尚未在请求中设置csrf令牌,则它将与调用django.middleware.csrf.get_token()的每个响应一起发送(该函数在内部用于检索csrf令牌)。
所有传出的POST表单中都存在一个名称为’csrfmiddlewaretoken’的隐藏表单字段。该字段的值再次是机密的值,带有一个掩码,该掩码既被添加到该密码,又被用于加扰。每次调用时都会重新生成掩码,get_token()以便在每个此类响应中都更改表单字段值。这部分是通过template标签完成的
为了防止突破口攻击,令牌不仅仅是秘密;在秘密前加上一个随机掩码,用于对其进行置乱。
出于安全原因,每次用户登录时都会更改机密值。

对于所有未使用HTTP GET,HEAD,OPTIONS或TRACE的传入请求,必须存在CSRF cookie,并且“ csrfmiddlewaretoken”字段必须存在且正确。如果不是,则用户将收到403错误。

为什么用户登录后可能会遇到CSRF验证失败?¶
出于安全原因,每次用户登录时都会旋转CSRF令牌。任何在登录之前生成表单的页面都将具有旧的无效CSRF令牌,需要重新加载。如果用户在登录后使用后退按钮,或者他们登录了其他浏览器选项卡,则可能会发生这种情况。

1.process_request方法中:
  从请求的cookie中获取csrftoken的值 ——》csrf_token ——》request.META['CSRF_COOKIE']

2.process_view方法中:
  1. 如果视图函数加上了csrf_exempt的装饰器        不做校验
  这就是为什么要在process_view中进行csrf_token的校验,而不是在process_request进行校验,process_request执行完之前还没有进行路由匹配,无法知道那个视图不需要进行csrf_token的校验。
  2. 如果请求方式是'GET', 'HEAD', 'OPTIONS', 'TRACE'   也不做校验  
  3. 其他的请求方式做校验
    request.META.get('CSRF_COOKIE') —— 》 csrf_token   # 拿到csrf_token   

    request_csrf_token = ""    # 取值有两种方法
     1.
    request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')  # 从request.POST中获取csrfmiddlewaretoken对应的值
     2.
    request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')  # 从请求头中获取X-csrftoken 的值 

    request_csrf_token 和 csrf_token 进行对比校验
      如果校验成功 正常走
      如果校验不成功 拒绝

fbv 可以使用csrf_exempt装饰器用来逃避csrf验证,
而cbv需要写在dispatch方法加装饰器才行。
django中间件生成csrf_token_第2张图片
或:
django中间件生成csrf_token_第3张图片

你可能感兴趣的:(python,django)