跨站点请求伪造解决方案

AppScan

跨站点请求伪造

Token

近期通过APPScan扫描程序,发现了不少安全问题,通过大量查阅和尝试最终还是解决掉了,于是整理了一下方便查阅。

1.跨站点请求伪造

首先,什么是跨站点请求伪造?

跨站点请求伪造-CSRF(Cross Site Request Forgery):是一种网络攻击方式。

说的白话一点就是,别的站点伪造你的请求,最可怕的是你还没有察觉并且接收了。听起来确实比较危险,下面有个经典的实例,了解一下跨站点请求伪造到底是怎么是实现的,知己知彼。

受害者:Bob

黑客:Mal

银行:bank

bob在银行有一笔存款,可以通过请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 把钱转到bob2下。通常情况下,该请求到达网站后,服务器会验证请求是否来自一个合法的session,并且该session的用户Bob已登录。Mal在该银行也有账户,于是他伪造了一个地址 http://bank.example/withdraw?account=bob&amount=1000000&for=mal ,但是如果直接访问,服务器肯定会识别出当前登录用户是mal而不是Bob,不能接受请求。于是通过CSRF攻击方式,将此链接伪造在广告下,诱使Bob自己点这个链接,那么请求就会携带Bob浏览起的cookie一起发送到银行,而Bob同时又登录了银行或者刚刚登录不久session还没有过期,那服务器发现cookie中有Bob的登录信息,就接收了响应,攻击就成功了

2.现在主要的几种防御CSRF的策略:1. 验证Referer:

referer携带请求来源,从示例可以看出,受害者发送非法请求肯定不是在银行的界面,所以在服务器通过验证Referer是不是 bank.example 开始就可以了,这个方法简单粗暴。

最简单的实现就是加个Filter:

public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String referer=request.getHeader( "Referer" ); if ((referer!= null ) &&(referer.trim().startsWith( "bank.example" ))){ chain.doFilter(request, response); } else { request.getRequestDispatcher( "error.jsp" ).forward(request,response); } } 2. 在请求参数中添加token验证:

要抵御跨站点请求伪造就要设置一个黑客伪造不了的东西。我们可以在请求参数中加一个随机token,在服务器验证这个token,通过即销毁重设。下面说一下我的实现:

首先定义token为 key-value 结构,因为很多情况会从不同的地方访问同一个请求,如果是单一的数据结构,第一个请求生成token后还没来得及发送请求,第二个又请求生成token就会把第一个冲掉,从而导致连续的验证失败。所以,我们要通过请求源将token隔离起来。这里我将请求地址摘要后作为token的key,用GUID作为token的value,代码如下:

/** * 根据请求地址获取token-key */ public static String getTokenKey (HttpServletRequest request) { String key = null ; try { MessageDigest mDigest = MessageDigest.getInstance( "MD5" ); //摘要算法可以自己选择 byte [] result = mDigest.digest(request.getRequestURL().toString().getBytes()); key = StringUtil.bytes2hex(result); } catch (NoSuchAlgorithmException e) { LOGGER.error( "get token key failed" ,e); } return key } /** * 获取token-value并存储在session中 */ public static String getTokenValue (HttpServletRequest request) { String key = getTokenKey(request); Map tokenMap = null ; Object obj = request.getSession().getAttribute( "tokenMap" ); if (obj == null ){ tokenMap = new HashMap(); request.getSession().setAttribute( "tokenMap" , tokenMap); } else { tokenMap = (Map)obj; } if (tokenMap.containsKey(key)){ return tokenMap.get(key); } String value = GUID.generate(); //GUID实现可自行百度,其实弄个伪随机数也是可以的... tokenMap.put(key,value); return value; } /** * 验证token */ public static boolean verify (String key ,String value ,HttpServletRequest request) { boolean result = false ; if (StringUtil.isEmpty(key) || StringUtil.isEmpty(value)) { //key或value只要有一个不存在就验证不通过 return result; } if (request.getSession() != null ) { Map tokenMap = getTokenMap(request); if (value.equals(tokenMap.get(key))){ result = true ; tokenMap.remove(key); //成功一次就失效 } } return result; }

完成上边的工具方法后,需要在form中添加token,如下:

< form name = "frm" action = "/test/tokentest.htm" method = "POST" > < input type = "hidden" name = "token_key" value = "

你可能感兴趣的:(跨站点请求伪造解决方案)