几种常见的攻击类型
GET类型的CSRF
这种类型的CSRF一般是由于程序员安全意识不强造成的。GET类型的CSRF利用非常简单,只需要一个HTTP请求,所以,一般会这样利用:
<img src=http://wooyun.org/csrf?xx=11 />
在访问含有这个img的页面后,成功向http://wooyun.org/csrf?xx=11
发出了一次HTTP请求。
所以,如果将该网址替换为存在GET型CSRF的地址,就能完成攻击了。
POST类型的CSRF
这种类型的CSRF危害没有GET型的大,利用起来通常使用的是一个自动提交的表单,如:
-
<form action=http://wooyun.org/csrf.php method=POST>
-
<input type="text" name="xx" value="11" />
-
</form>
-
<script> document.forms[0].submit(); </script>
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。
2. token验证
- 在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有
token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。 - token需要足够随机
- 敏感的操作应该使用POST,而不是GET,以form表单的形式提交,可以避免token泄露。
- Token 应该被保存起来(放到 local / session stograge 或者 cookies)
- Tokens 除了像 cookie 一样有有效期,而且你可以有更多的操作方法。一旦 token 过期,只需要重新获取一个。你可以使用一个接口去刷新 token;你甚至可以把 token 原来的发布时间也保存起来,并且强制在两星期后重新登录什么的;如果你需要撤回 tokens(当 token 的生存期比较长的时候这很有必要)那么你需要一个 token 的生成管理器去作检查。
- Local / session storage 不会跨域工作,请使用一个标记 cookie
- 有需要的话,要加密并且签名 token
- 将 JSON Web Tokens 应用到 OAuth 2
使用token:
1.引入csrf
-
from flask_wtf.csrf import CsrfProtect
-
csrf = CsrfProtect()
-
-
app = Flask(__name__)
-
-
csrf.init_app(app)
-
app.config[ 'SECRET_KEY']= 'myblog'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2.在站内页面上head中,增加token
<meta name="csrf-token" content="{{ csrf_token() }}">
- 1
3.配置angular提交表头
-
app.config( function ($httpProvider) {
-
$httpProvider.defaults.headers.common[ 'X-CSRF-Token'] = $( 'meta[name=csrf-token]').attr( 'content');
-
});
- 1
- 2
- 3
4.再次测试跨域post
后台输出:
前台输出:
在表单中增加一个随机的数字或字母验证码,通过强制用户和应用进行交互,来有效地遏制CSRF攻击。
4. 在请求地址中添加 token 并验证
CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,
该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己
的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。
可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,
如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。 这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,
然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数
的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。
而对于 POST 请求来说,要在 form 的最后加上 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>,
这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求
都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,
对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,
这种方法就没有作用,还需要程序员在编码时手动添加 token。 该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,
黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,
并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的
,就在后面添加 token,如果是通向外网则不加。不过,即使这个 csrftoken 不以参数的形式附加在请求之中,
黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器
Referer 功能的原因。
5. 在 HTTP 头中自定义属性并验证
这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,
而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个
HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest
请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。 然而这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,
并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,
从而进行前进,后退,刷新,收藏等操作,给用户带来不便。另外,对于没有进行 CSRF 防护的遗留系统来说,
要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。