序
上一篇文章中,我們討論了關(guān)于“瀏覽器記住用戶名和密碼”的問題,至于這篇文章嘛,我想談?wù)勱P(guān)于“會話標(biāo)識”的漏洞。而且,這篇文章也是“Web安全實戰(zhàn)”系列的最后一篇。為什么要拿到最后來說呢,其實,是之前一直困擾我的問題,這個問題曾一頓讓我抓狂,n(n=3~5)多天一直沒有解決,當(dāng)時也是搜遍了各大網(wǎng)站,各大論壇,均未找到合適的解決方案。其中的過程就不再廢話了,轉(zhuǎn)到正題。
問題
先介紹一下問題是如何發(fā)現(xiàn)的,當(dāng)然,這個不是我發(fā)現(xiàn)的,是我們測試部的童鞋發(fā)現(xiàn)的漏洞,然后她轉(zhuǎn)交給我,讓我去解決這個問題。剛開始也是一點思路都沒有,然后就是一頓狂搜,后來慢慢發(fā)現(xiàn),其實這個問題產(chǎn)生的根源,就是應(yīng)用服務(wù)器(如 Tomcat)的 JSessionId 沒有更新。只要使得舊的 Session 過期,重新生成新的 Session
即可。當(dāng)然,這是理論上的解決方案,很想當(dāng)然的。
曲折過程
有了思路之后,再想解決問題就容易了很多。于是,我就把范圍固定在了如何讓 Session 過期,當(dāng)然,這樣的解決方案網(wǎng)上有一籮筐,這里就不多說了,我拿到項目中試了試,結(jié)果你肯定能想到 —— 拋異常,異常原因是:...Session already invalidated 。這說明在程序中的某處,需要用到 Session 中的數(shù)據(jù),但是此時
Session 已經(jīng)過期了,無法取到數(shù)據(jù)。
于是,我開始找是哪里用到了 Session ,遺憾的是,我沒能找到。心想,找不到 Session 我就換個思路吧,那就讓 Session 過期之前,重新生成一個新的 Session ,把舊的 Session中的數(shù)據(jù)拷貝到新產(chǎn)生的 Session 中。這樣就可以避免 Session 過期的問題了(理論上是這樣)。
到這里,如果是一般的情況,就可以解決了,不過嘛,如果你的項目中還加入了安全框架 Shiro ,那么在 Session 的處理上,會有一些麻煩,因為 Shiro 也會有自己的 Session ,而且在認證的時候,Shiro 會有一些處理 Session 的操作,這就是導(dǎo)致 ...Session already invalidated 的原因所在。后來試了幾種把舊
Session 數(shù)據(jù)轉(zhuǎn)新 Session 數(shù)據(jù)的方案也不好使。
最終方案
一個偶然的機會,在網(wǎng)上找到了一篇《會話標(biāo)識未更新》的文章,這篇文章就是介紹的在使用 Shiro 的情況下,如何解決這個漏洞,不過,情況不同的是,他使用的是應(yīng)用服務(wù)器是 jBoss,而我們用的則是 Tomcat,本著試一試的態(tài)度,按照他給的思路,把自己的代碼做了一下整理,把對
Session 操作的處理類封裝成了一個 Filter,通過這個 Filter 對 Session 進行處理,下面請看相關(guān)代碼。
代碼如下
首先,增加一個新類,NewSessionFilter。
<span style="font-family:Comic Sans MS;">package com.test.web.common; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.shiro.SecurityUtils; import org.slf4j.LoggerFactory; public class NewSessionFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(NewSessionFilter.class); public static final String NEW_SESSION_INDICATOR = "com.cacss.sc.web.common.NewSessionFilter"; public static void newSession(){ HttpSession session = (HttpSession) SecurityUtils.getSubject().getSession(true); session.setAttribute(NEW_SESSION_INDICATOR, true); System.out.println("NewSessionFilter destory"); public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("NewSessionFilter doFilter"); if (request instanceof HttpServletRequest) { HttpServletRequest httpRequest = (HttpServletRequest) request; String url = httpRequest.getRequestURI(); if (httpRequest.getSession() != null) { System.out.println("NewSessionFilter doFilter httpRequest.getSession().getId()"+ httpRequest.getSession().getId()); //--------復(fù)制 session到臨時變量 HttpSession session = httpRequest.getSession(); HashMap old = new HashMap(); Enumeration keys = (Enumeration) session.getAttributeNames(); while (keys.hasMoreElements()){ String key = (String) keys.nextElement(); if (!NEW_SESSION_INDICATOR.equals(key)){ old.put(key, session.getAttribute(key)); session.removeAttribute(key); if (httpRequest.getMethod().equals("POST") && httpRequest.getSession() != null && !httpRequest.getSession().isNew() && httpRequest.getRequestURI().endsWith(url)){ session=httpRequest.getSession(true); logger.debug("new Session:" + session.getId()); //-----------------復(fù)制session for (Iterator it = old.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Entry) it.next(); session.setAttribute((String) entry.getKey(), entry.getValue()); chain.doFilter(request, response); System.out.println("NewSessionFilter doFilter end"); public void init(FilterConfig filterConfig) throws ServletException { System.out.println("NewSessionFilter init"); System.out.println("NewSessionFilter init end");
然后,在 web.xml 中配置 Filter。
<span style="font-family:Comic Sans MS;"><filter> <filter-name>NewSessionFilter</filter-name> <filter-class>com.cacss.sc.web.common.NewSessionFilter</filter-class> <filter-name>NewSessionFilter</filter-name> <url-pattern>/login</url-pattern>
這樣處理完之后,再啟動應(yīng)用服務(wù)器,登錄前后的 JSessionId 就已經(jīng)不一樣了,也就是說,會話標(biāo)識未更新的問題也就解決了。
結(jié)束語
這個問題困擾了我 n 多天,直到看到這個解決思路之后,通過跟測試部的童鞋協(xié)商,測試通過之后,才算是真正的解決了。這也算是這個系列的最后一篇了,寫到現(xiàn)在已經(jīng)把大部分的 Web 安全方面的漏洞都提到過了,而且也都給出了一些解決方案。
通過這近一個多月的漏洞修復(fù),我在 Web 安全方面真的是惡補了一番,也接觸了很多安全方面的技術(shù),這對于我以后的開發(fā)、設(shè)計都是很有好處的,在開發(fā)、設(shè)計的時候,就會考慮到會不會產(chǎn)生安全漏洞,怎樣做會避免這樣的問題。這樣,就不會在測試的時候出現(xiàn)很多不必要的漏洞了。
|