Django网络安全跨站点请求伪造保护,CSRF如何正确使用

Posted 我辈李想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django网络安全跨站点请求伪造保护,CSRF如何正确使用相关的知识,希望对你有一定的参考价值。

Django网络安全

【Django网络安全】跨站点请求伪造保护,CSRF如何正确使用


文章目录


前言

CSRF(Cross-site request forgery),中文名跨站点请求伪造。当恶意网站包含一个链接、一个表单按钮或一些javascript,使用登录用户在浏览器中的凭据,打算恶意访问您的网站并执行某些操作时,就会发生这种攻击。还包括一种相关的攻击类型“登录CSRF”,即攻击站点诱使用户的浏览器使用他人的凭据登录站点。
XSS和CSRF正好相反,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。直接的说就是我们需要保护POST、PUT和DELETE请求。
网友借个图


一、CSRF攻击场景

对于用户的的所有操作,除了get查询,其他请求都需要保护,包括POST、PUT和DELETE。

发送邮件
修改账户信息
资金转账
盗取用户隐私数据
网站被上传网马
作为其他攻击方式的辅助攻击(比如xss)
传播CSRF蠕虫(见下文中的YouTube CSRF漏洞)
等等

二、CSRF攻击的防御手段

1.验证 HTTP Referer 字段

这种方式理论上可行,实际情况是Referer可以被伪造。

2.请求地址添加token并验证

django的验证方式是csrftoken验证。实现的主要是长cookie中csrftoken和表单中的csrfmiddlewaretoken是否为同一加密编码来源来实现csrf防御。csrftoken和csrfmiddlewaretoken这两个参数在接下来是我们关注的重点。

三、Django的CSRF防御解析

django的csrf防御是通过中间价来执行,默认是开启状态。位置是django项目的settings文件。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',  # csrf防御
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'utils.middleware.LogMiddleware.OpLog',  # 访问记录
]

1.CSRF防护的过程

CsrfViewMiddleware中间件使用的情况下,通过中间件的process_request方法获取请求cookie中的csrftoken,通过process_view获取post、put、delete请求cookie中的csrftoken和表单中的csrfmiddlewaretoken并进行校验(不通过就是403),通过process_response设置cookie的csrftoken参数。

2.cookie中的csrftoken

这里我都是用xadmin后端来验证的,也可以使用admin后端。首先看下get请求中的cookie中csrftoken参数。


我们再来看下post请求cookie中的csrftoken参数和表单中的csrfmiddlewaretoken参数。


3.session中的csrftoken

在第1部分中我们看到请求的cookie中是存在csrftoken参数的,而且这个参数时长是365天的长cookie。csrftoken其实是每次用户登录时生成的csrftoken,如果用户不注销或登出(django的logout方法),这个参数不会改变,并且每次重新登录时刷新csrftoken。
我们知道cookie时保留在客户端浏览器上的,浏览器时无法长久保留cookie的,可能应为客户端种种问题而丢失(浏览器崩溃、重装、更换浏览器等)。这里就衍生出了将csrftoken保存至sessions,通过服务器来保存csrftoken,并通过后端来验证csrftoken。
具体操作是需要在django的settings中修改配置,使cookie保存至sessions。

CSRF_USE_SESSIONS=True # 在用户会话中而不是在cookie中存储CSRF令牌,实际意义不大。

4.html中的csrftoken

在第1部分中我们看到了表单中的csrfmiddlewaretoken参数,在django的使用中,我们会在表单中使用csrftoken

<form method="post">% csrf_token % </form>

表单中的csrf_token 实际上就是csrfmiddlewaretoken。

5.装饰器中的csrf函数

我们经常看到很多教程在遇到403报错时,会在django后端使用csrf_exempt装饰器。一般都是在def方法中使用@csrf_exempt,在class类方法中使用@method_decorator(csrf_exempt, name=‘dispatch’)。
csrf_exempt的真正原理是改变csrf_exempt状态为True,在CsrfViewMiddleware中间件的process_view方法中直接跳过csrftoken和csrfmiddlewaretoken的加密校验。

四、前后端不分离场景的正确防御

在django前后端不分离项目中,django通过render方法实现了在表单中生成CsrfViewMiddleware参数。

1.django模板中form表单提交

submit提交

	<form method="post">% csrf_token % </form>

2.django模板中ajax提交

在前端一共有三种方式通过验证,示例如下:

方式一:

function getCookie(name) 
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') 
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) 
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) 
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            
        
    
    return cookieValue;

const csrftoken = getCookie('csrftoken');

方式二:

# 也可以通过JavaScript Cookie获得。
const csrftoken = Cookies.get('csrftoken');

方式三:

% csrf_token %   # 在HTML中包含CSRF令牌
<script>
	const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
</script>

具体的ajax请求处理如下:

<script>
		const request = new Request(
		    /* URL */,
		    headers: 'X-CSRFToken': csrftoken
		);
		fetch(request, 
		    method: 'POST',
		    mode: 'same-origin'  // Do not send CSRF token to another domain.
		).then(function(response) 
		    // ...
		);	
</script>

综上所述,Cookies中获取的csrftoken写入到表单的csrfmiddlewaretoken肯定时能校验通过的。前边我们说到,cookie中可以存储csrftoken,sessions中也可以,针对不同的配置,使用的方式时不同的。主要是CSRF_USE_SESSIONS 和 CSRF_COOKIE_HTTPONLY参数。

当CSRF_USE_SESSIONS或CSRF_COOKIE_HTTPONLY 为默认值,即Flase时,三种方式均可。
当CSRF_USE_SESSIONS或CSRF_COOKIE_HTTPONLY 为True时,只能使用方式三。

五、前后端分离场景的正确防御

我使用的前后端分离是基于django+vue的,django后端使用drf和jwt,并且通过django-cors-headers处理跨域问题。在涉及到网络安全的时候,处理csrf防御问题衍生出了本博客。前后端分离的csrf的防护需要后端提供csrfmiddlewaretoken参数供前端调用,并在使用post、put和delete请求时添加到headers中。基于前后端分离项目,有时需要考虑请求的幂等性,可以考虑将csrfmiddlewaretoken设置为幂等性的验证token。

1.django提供接口

urls.py文件

urlpatterns = [
    path('get_csrf_token/', get_csrf_token),  # 获取csrf_token
]

views.py文件

from django.middleware.csrf import get_token

def get_csrf_token(request):
    csrftoken= get_token(request)
    return JsonResponse('csrftoken': csrftoken)

2.现在所有函数都应该去除csrf装饰器

3.vue获取csrftoken添加至请求


总结

处理csrf的过程中,使用csrf装饰器的都是干扰项,整理初这个文章很不容易,希望所有小伙伴不要被困扰,如有问题欢迎指正。

CSRF

1.漏洞简介

    CSRF(Cross-Site Request Forgery,跨站点伪造请求)是一种网络攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作,具有很大的危害性。

2.原理

    CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

3.检测

    通常手工检测一般 先抓一个正常请求的数据包,使用burpsuite发送到中继后去掉Referer 字段后 发送数据包,如果提交有效,一般会有CSRF存在。我们也可以使用burp生成 csrf poc ,把poc带啊吗放在自己的云服务器上,使用已登录的用户访问进行验证。当然,我们也可以使用一些CSRF检测工具辅助我们检测该漏洞。常用的有CSRFTester,CSRF Request Builder这些。当然,还需要我们手工检测。

4.防御

4.1验证http referrer 

    在HTTP协议中,referer字段记录了HTTP请求的来源,可以以此来判断来源地址是不是站内地址。

防御的重点是:前端JS无法修改referer字段

  绕过方法:

    (1)Referrer中包含关键字就通过。

      我们可以 domain.com.hacksite.com、hacksite.com.domain.com

    (2)Referer为空允许通过

      Data协议、https跳转到http

4.2Token 

    CSRF攻击根本在于伪造用户的请求,二请求的所有的用于验证信息都在cookie中,攻击者可以在不知道受害者信息,而直接利用受害者cookie去通过安全验证。我们需要让服务器随机生成token发给客户端,客户端请求时带上token,服务器验证token的合法性。

防御重点是:由于同源策略的限制,攻击者无法使用JS获取其他域的token。

绕过方法:

    同域下存在xss漏洞时,使用js读取页面中的token,然后可以构造 csrf poc 达到csrf 攻击的目的。某运维平台验证码处存在xss,然后可以通过csrf 搞事,哈哈。

4.3在HTTP头自定义属性并验证

    自定义属性的方法也是使用token并进行验证,把token放到HTTP头中自定义的属性里。通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过Referer泄露到其他网站。

4.4 其他

    在执行关键操作时(如修改密码,修改绑定邮箱手机等),除了要做CSRF的token防护之外,还应该要求重新输入当前账号密码,并加入验证码。

    关键操作只接受post请求,只接受https请求

5.新姿势

5.1JSON CSRF

    以json格式传输数据,没有添加token,无referer限制。应用服务器程序会验证Content-type 是否为application/json

    我们还可以使用新姿势:

      制作flash文件

      制作一个跨域XML 文件

      制作一个具有307状态码的PHP文件

原理: flash的header存在一个黑名单,黑名单中的header不允许设置,如referer ,但是Content-Tyep 不再黑名单中(不过在新的谷歌浏览器已经不行了..)。

5.2JSONP 接口劫持

    用户登陆后,通过JSONP的方式获取数据,如JSONP接口未作防护(如没有验证referer头),就会造成JSONP劫持攻击,被定向钓鱼。

6.Burp suite 生成CSRF PoC

技术分享图片

参考

https://www.cnblogs.com/blacksunny/p/7940287.html

http://blog.knownsec.com/2015/03/jsonp_security_technic/

 

未来工作

强迫症,需要配图才行。

最新CSRF JSON 实战,当然是defcon走一波,哈哈

 

以上是关于Django网络安全跨站点请求伪造保护,CSRF如何正确使用的主要内容,如果未能解决你的问题,请参考以下文章

使用会话令牌或随机数进行跨站点请求伪造保护 (CSRF)?

CSRF 跨站点请求伪造

[不常用] - CSRF(跨站点请求伪造)

《白帽子讲WEB安全》学习笔记之第4章 跨站点请求伪造(CSRF)

浅谈跨站请求伪造(CSRF)

白帽子讲Web安全 第四章 跨站点请求伪造(CSRF)