前言
以前在很多p2p網(wǎng)站中削祈,都有新手領(lǐng)取紅包的活動烛卧。這樣的紅包鏈接或多或少都有很多的漏洞九串,就是表單可以重復(fù)提交生棍。這樣的話,對那些p2p網(wǎng)站或者其他類似的網(wǎng)站造成很大的損失蛤虐。Fiddler大家都不陌生吧党饮,就是一個(gè)抓包軟件。我們先攔截url請求驳庭,Shift+R刑顺,填入壓力測試的次數(shù),然后釋放饲常,就會造成很多次的url訪問請求蹲堂,這樣的結(jié)果很容易造成表單重復(fù)提交。那么我們的今天主題就是如何使用Session和Token防止表單重復(fù)提交
表單重復(fù)提交例子
在我們寫網(wǎng)站的時(shí)候贝淤,肯定寫過留言板的功能柒竞,但是肯定對重復(fù)提交留言的惡性行為沒有進(jìn)行一些安全措施。
我們利用Fiddler這一款抓包軟件播聪,可以進(jìn)行壓力測試朽基,模擬多次url請求。就會造成以下情況离陶,留言板被惡性刷屏稼虎。
如何進(jìn)行防止表單重復(fù)提交
其實(shí)很簡單的,我們只需要生成一個(gè)唯一的token,分別放進(jìn)客戶端的表單里和服務(wù)器的session中進(jìn)行了枕磁。當(dāng)我們發(fā)起請求時(shí)渡蜻,只需要判斷session中的token(以下簡稱serverToken)和客戶端表單里的token(以下簡稱clientToken)是否相等术吝。 如果severToken==null,clientToken==null 還有
serverToken不等于clientToken计济,那么就說明表單被重復(fù)提交了。反之排苍,如果serverToken==clientToken沦寂,就說明表單沒有被重復(fù)提交,當(dāng)我們進(jìn)行了一系列需要的操作后淘衙,就可以清除session中的token了传藏。
具體代碼
- 生成唯一的Token
public static String makeToken(){
String token=(System.currentTimeMillis()+new Random().nextInt(999999999))+"";
try {
MessageDigest mDigest=MessageDigest.getInstance("md5");
byte[] md5=mDigest.digest(token.getBytes());
BASE64Encoder encoder=new BASE64Encoder();
return encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}} - Jsp代碼
<pre>
<%
String token=CommonUtils.makeToken();
request.getSession().setAttribute(token,token);
%>
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/PhotoServlet?method=uploadPhoto&token=<%=token%>" method="post">
上傳圖片:<input type="file" name="file"/>
描 述:<input type="text" name="desc"/>
<input type="submit" value="提交"/>
</form>
</pre> - Serlvet代碼
<pre>
public String uploadPhoto(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String result=null;
String clientToken=request.getParameter("token");
String serverToken=(String)request.getSession().getAttribute(clientToken);
if(!isRepeatCommit(serverToken, clientToken)){
result="請不要重復(fù)提交";
request.getSession().setAttribute("result",result);
return "/result.jsp";
}
request.getSession().removeAttribute(clientToken);
}
</pre>
<pre>
/**
*
* @param serverToken
* @param clientToken
* @return
* 如果沒有重復(fù)提交 返回true, 如果重復(fù)提交 返回fasle
*/
public boolean isRepeatCommit(String serverToken,String clientToken){
MyLog.i(TAG, "serverToken="+serverToken+",clientToken="+clientToken);
if(serverToken==null){
return false;
}
if(clientToken==null){
return false;
}
if(!clientToken.equals(serverToken)){
return false;
}
return true;
}
</pre>
效果圖
我自己寫了一個(gè)圖片上傳的demo,就來測試一下是否能防止重復(fù)提交吧
首先手動測試一下彤守,是否能防止重復(fù)提交
我們上傳成功后毯侦,再次刷新地址欄,會發(fā)現(xiàn)已經(jīng)防止了表單重復(fù)提交
手動測試完畢了具垫,心里肯定是美滋滋了侈离。那么我們用Fiddler來測試一下吧,我們來模擬一下壓力測試筝蚕。
我們從Fiddler看到返回結(jié)果卦碾,心里更是美滋滋了铺坞。已經(jīng)成功防止了表單重復(fù)提交不安全的行為了。
發(fā)現(xiàn)的問題
寫這邊簡文的時(shí)候洲胖,我也看到其他大牛寫的博客济榨。在底下的評論也發(fā)現(xiàn)了一個(gè)存在的問題。就是打開2個(gè)上傳圖片的網(wǎng)頁绿映,在第一個(gè)網(wǎng)頁上傳到了照片擒滑,提示"上傳成功"。然后在打開第二個(gè)網(wǎng)頁再上傳圖片绘梦,就會提示"請不要重復(fù)提交"橘忱。這是什么問題造成的呢?其實(shí)仔細(xì)想一下就會發(fā)現(xiàn)卸奉, 當(dāng)?shù)谝粋€(gè)網(wǎng)站上傳圖片成功后钝诚,就會清除Session中的token值,此時(shí)的serverToken=null了榄棵。就會造成第二個(gè)網(wǎng)站上傳圖片時(shí)候凝颇,serverToken=null和serverToken!=clientToken,那么肯定會提示"請不要重復(fù)提交"疹鳄。
解決方案
看到這里拧略,你們估計(jì)還有疑問?那么就想一下為什么我要生成唯一的Token瘪弓。我們這個(gè)Token是每一次用戶請求的標(biāo)識垫蛆。我們只需要serverToken在Session中的屬性值設(shè)置為唯一的Token即可。就不會發(fā)生剛才的情況腺怯。
<pre>
String token=CommonUtils.makeToken();
request.getSession().setAttribute(token,token);
%>
</pre>