CSRF攻击原理及预防手段
CSRF全程 Cross Site Request Forgery, 跨站域请求伪造.这种攻击方式相对于XSS,SQL注入等攻击方式比较晚被发现,今天就来讲解下这种攻击方式以及避免方式.
攻击过程
-
假设abc用户登录银行的网站进行操作, 同时也访问了攻击者预先设置好的网站.
-
abc点击了攻击者网站的某一个链接,这个链接是
http://www.bank.com/xxxx指向银行,银行服务器会根据这个链接携带的参数会进行转账操作. -
银行服务器在执行转账操作之前会进行SESSION验证是否登录, 但是由于abc已经登录了银行网站,攻击者的链接也是
www.bank.com.所以攻击的链接就会携带session id到银行服务器. -
由于session id是正确的,所以银行会判断操作是由本人发起的,执行转账操作.
演示
根据上面的说明,我们来模拟一下攻击的过程.
-
有
www.bank.com跟www.hacker.com.用户abc登录www.bank.com网站之后点击了www.hacker.com的点击抽大奖的诱骗链接 -
此链接会向
www.bank.com发起一个post请求.由于请求域名为www.bank.com,所以请求会携带www.bank.com的session id.
www.hacker.com的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post" action="http://www.bank.com/transfer.php"> <input type="hidden" name="from" value="abc"> <input type="hidden" name="money" value="10000"> <input type="hidden" name="to" value="hacker"> <input type="button" onclick="submit()" value="点击抽大奖"> </form> </body>
可以发现,www.hacker.com的网页中包含了一个向www.bank.com发起的post请求.并且表单都没隐藏了,只有一个诱骗用户点击的按钮.
预防
从上面的例子 可以看到csrf攻击.黑客不能拿到cookie,也没办法对服务器返回的内容进行解析.唯一能做的就是给服务器发送请求.通过发送请求改变服务器中的数据.上面的例子中攻击者诱导用户点击链接进行转账操作,使得银行数据库中受害者的金额发生了改变.
了解了csrf攻击的原理和目标,提出了两种防御手段
referer 验证
根据HTTP协议,在http请求头中包含一个referer的字段,这个字段记录了该http请求的原地址.通常情况下,执行转账操作的post请求www.bank.com/transfer.php应该是点击www.bank.com网页的按钮来触发的操作,这个时候转账请求的referer应该是www.bank.com.而如果黑客要进行csrf攻击,只能在自己的网站www.hacker.com上伪造请求.伪造请求的referer是www.hacker.com.所以我们通过对比post请求的referer是不是www.bank.com就可以判断请求是否合法.
这种方式验证比较简单,网站开发者只要在post请求之前检查referer就可以,但是由于referer是由浏览器提供的.虽然http协议有要求不能篡改referer的值.但是一个网站的安全性绝对不能交由其他人员来保证.
token 验证
从上面的样式可以发现,攻击者伪造了转账的表单,那么网站可以在表单中加入了一个随机的token来验证.token随着其他请求数据一起被提交到服务器.服务器通过验证token的值来判断post请求是否合法.由于攻击者没有办法获取到页面信息,所以它没有办法知道token的值.那么伪造的表单中就没有该token值.服务器就可以判断出这个请求是伪造的。如下所示:
前端:
let gtk = getGToken(); requestOpts.headers = { 'gtk': gtk }; function getGToken() { let hash = 1234; let token = feCookie.get(config.cache.cookie.token.key); if (!token) { token = ''; } for (let i = 0; i < token.length; i++) { hash += (hash << 5) + token.charCodeAt(i); } return hash & 0x7fffffff; }
后台:增加一个过滤器,对token进行比对校验
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String reqUrl = getRequestPath(httpRequest); if(isIgnore(reqUrl)) { log.info("ignore g_tk url:{}", reqUrl); chain.doFilter(request, response); return; } //获取请求头中的gtk String click_gtk = httpRequest.getHeader("gtk"); // 获取服务器的gtk Cookie cookie = CookiesUtil.getCookieByName(httpRequest, Constant.TOKEN_COOKIE_KEY); if(cookie == null) { log.warn("sesstion token is empty"); relogin(httpRequest, httpResponse); }else{ String server_gtk = getGToken(cookie.getValue()).toString(); if (!server_gtk.equals(click_gtk)) { log.warn("g_tk error, click_gtk:{},server_gtk:{}",click_gtk,server_gtk); relogin(httpRequest, httpResponse); }else{ // 验证通过 chain.doFilter(request, response); } } }
posted on 2017-08-27 22:15 bijian1013 阅读(307) 评论(0) 收藏 举报
浙公网安备 33010602011771号