0x01 Token的使用
可以有效防止csrf漏洞和http请求批量提交或http重放,像爆破,刷评论,刷短信,请求重放,请求并发都可以利用token以及反射型xss和url调用之类的都可以用token做防御。
esapi中token的使用过程
a.在用户刚登录的时候,产生一个新的不可预知的CSRF Token,并且把此Token存放在用户的session中。
b.在任何一个需要保护的表单中,增加一个隐藏的字段来存放这个Token;对于需要保护的URL,增加一个参数来存放此Token。
c.提交此请求的时候,在服务器端检查提交的Token与用户session中的Token是否一致,如果一致,继续处理请求,否则返回一个错误信息给用户。
d.在用户退出或者session过期的时候,用户信息(包括CSRF Token)从session中移除并且销毁session。
看一下addCSRFToken函数,其实这个函数的作用就相当于加了个ctoken的参数,即&ctoken=xxxxx
public String addCSRFToken(String href) {故Get型请求增加token的写法是
User user = ESAPI.authenticator().getCurrentUser();
if (user.isAnonymous()) {
return href;
}
// if there are already parameters append with &, otherwise append with ?
String token = CSRF_TOKEN_NAME + "=" + user.getCSRFToken();
return href.indexOf( '?') != -1 ? href + "&" + token : href + "?" + token;
}
String Href = "/test.php?action=update&id=100";
<a href='<%=ESAPI.httpUtilities().addCSRFToken(Href)%>'>update</a>
如果是form表单使用相同,不需要在写成form表单自动这么麻烦,同样可以达到防止刷接口和csrf的效果。
String url = "/account/login/";
<form action="<%=ESAPI.httpUtilities().addCSRFToken(url)%>" method="POST">
<input type="text" name="user"/>
<input type="text" name="pass"/>
<input type="hidden" value="<%=token %>" name="ctoken"/>
<input type="submit" value="submit"/>
</form>
看一下它的验证方法,检测从客户端得到的ctoken和用户的csrftoken是相同。
public void verifyCSRFToken(HttpServletRequest request) throws IntrusionException {
User user = ESAPI.authenticator().getCurrentUser();
// check if user authenticated with this request - no CSRF protection required
if( request.getAttribute(user.getCSRFToken()) != null ) {
return;
}
String token = request.getParameter(CSRF_TOKEN_NAME);
if ( !user.getCSRFToken().equals( token ) ) {
throw new IntrusionException("Authentication failed", "Possibly forged HTTP request without proper CSRF token detected");
}
}
下面是表单中加入token的常用方法,实现起来也算简单方便。
<%
long token=System.currentTimeMillis();
session.setAttribute("token",token);
%>
<form action="/account/login/" method="POST">
<input type="text" name="user"/>
<input type="text" name="pass"/>
<input type="hidden" value="<%=token %>" name="ctoken"/>
<input type="submit" value="submit"/>
</form>
处理接收的token并进行验证
public String login(HttpServletRequest request, HttpServletResponse response){
String username= request.getParameter("user");
String password = request.getParameter("pass");
String token = request.getParameter("ctoken");
HttpSession session=request.getSession();
String tokenInSession = ""+session.getAttribute("token");
if (tokenInSession!=null && token!=null && token.equals(tokenInSession)) {
session.removeAttribute("token");
return "login";
}
return "error";
}
0x02安全随机数的生成
随机数的作用
1.可以防止平行越权漏洞,我们可以执行越权的原因是我们可以猜测这个操作的唯一关键字,像订单号变成唯一且随机的,我们就无法查看他人的订单了。
2.静态敏感文件的保存,当我们把省份证照片存储到网站目录上时,如果是时间戳生成的,可以预测的,我们就能爆破他人的身份证照片了。
3.生成安全的cookie,防止cookie可以被破解。
下面我们就看看esapi中随机数的使用和getRandomString函数:
获取特定字符串中长度为length的随机字符串
ESAPI.randomizer().getRandomString(length,characterSet);
public String getRandomString(int length, char[] characterSet) {
StringBuilder sb = new StringBuilder();
for (int loop = 0; loop < length; loop++) {
int index = secureRandom.nextInt(characterSet.length);
sb.append(characterSet[index]);
}
String nonce = sb.toString();
return nonce;
}
获取两个整数中的整数
ESAPI.randomizer().getRandomInteger(int min,int max)
public int getRandomInteger(int min, int max) {
return secureRandom.nextInt(max - min) + min;
}
获取长整型随机值
ESAPI.randomizer().getRandomLong()
public long getRandomLong() {
return secureRandom.nextLong();
}
使用的时候可以用时间戳加随机数(最好大于8位)的形式来生成唯一的随机数。