保護(hù)你的會(huì)話令牌
保護(hù)你的會(huì)話令牌
通常我們會(huì)采取以下的措施來(lái)保護(hù)會(huì)話。
1.采用強(qiáng)算法生成Session ID
正如我們前面用Web Scrab
分析的那樣,會(huì)話ID
必須具有隨機(jī)性和不可預(yù)測(cè)性。一般來(lái)說(shuō),會(huì)話ID
的長(zhǎng)度至少為128
位。下面我們就拿常見的應(yīng)用服務(wù)器Tomcat
來(lái)說(shuō)明如何配置會(huì)話ID
的長(zhǎng)度和生成算法。
首先我們找到{TOMCAT_HOME}\conf\context.xml,然后加入下面一段設(shè)置
<Manager sessionIdLength="20" ➊
secureRandomAlgorithm="SHA1PRNG" ➋
secureRandomClass="java.security.SecureRandom" ➌
/>
➊ 定義會(huì)話ID 的長(zhǎng)度,如果我們這里不聲明的話,默認(rèn)是16字節(jié)??赡苡凶x者會(huì)納悶,怎么平時(shí)我看到的會(huì)話ID都是很長(zhǎng)的呀?我們就拿這里的20個(gè)字節(jié)來(lái)講吧,我們?cè)跒g覽器發(fā)送請(qǐng)求時(shí)會(huì)發(fā)現(xiàn)這樣的會(huì)話ID:
JSESSIONID=90503B6BE403D4AB6164A311E167CF1F6F3F2BD0
仔細(xì)看會(huì)發(fā)現(xiàn)ID的長(zhǎng)度為40,因?yàn)檫@里顯示的是十六進(jìn)制,每?jī)蓚€(gè)字符代表一個(gè)字節(jié)。
➋ 定義隨機(jī)數(shù)算法,默認(rèn)的是SHA1PRNG,你也可以換成自己的算法。
➌ 定義隨機(jī)數(shù)類,默認(rèn)的是java.security.SecureRandom,我們也可以繼承這個(gè)類來(lái)實(shí)現(xiàn)自己的算法。
有一點(diǎn)要注意,就是我們?cè)趯?shí)現(xiàn)自己的隨機(jī)數(shù)算法時(shí),一定要保證生成的Session ID不能有重復(fù),這里我們參考一下Tomcat實(shí)現(xiàn)的機(jī)制。
/**
* Generate and return a new session identifier.
*/
protected () {StringgenerateSessionId
String result = null;
do {
if (result != null) {
duplicates++;
}
result = sessionIdGenerator.generateSessionId();
} while (sessions.containsKey(result));
return result;
}
由此可見,Tomcat是不會(huì)產(chǎn)生兩個(gè)相同的會(huì)話ID的。
2.軟硬兼施,會(huì)話過(guò)期
會(huì)話過(guò)期是應(yīng)用程序的一項(xiàng)重要的安全控制,它定義了用戶在多長(zhǎng)時(shí)間段內(nèi)不用重新登錄而仍然維持一個(gè)登錄狀態(tài)。一般來(lái)說(shuō),有兩種會(huì)話過(guò)期——軟會(huì)話過(guò)期(Soft Session Timeout)和硬會(huì)話過(guò)期(Hard Session Timeout)。
軟會(huì)話過(guò)期,它指的是用戶在一定的時(shí)間內(nèi)與應(yīng)用系統(tǒng)沒有交互,則會(huì)話過(guò)期。舉一個(gè)簡(jiǎn)單的例子就是,一個(gè)用戶登錄了一個(gè)應(yīng)用系統(tǒng),他臨時(shí)離開了計(jì)算機(jī)40分鐘,而應(yīng)用系統(tǒng)設(shè)置的會(huì)話過(guò)期時(shí)間為30分鐘,這時(shí)候用戶回到計(jì)算機(jī)前再做任何操作,系統(tǒng)都會(huì)重定向?yàn)榈卿涰?yè)面讓用戶重新輸入用戶名和密碼。
那么,軟會(huì)話過(guò)期有什么用呢?我們知道在CSRF***中一個(gè)最基本的假設(shè)就是合法用戶處在一個(gè)登錄狀態(tài)中,如果我們?cè)O(shè)置了一個(gè)合理的且較低的會(huì)話過(guò)期時(shí)間,就提高了實(shí)施CSRF***的難度,從而保護(hù)了系統(tǒng)。
通常有3種辦法來(lái)設(shè)定軟會(huì)話過(guò)期,其級(jí)別由高到低依次為:Tomcat級(jí)別> Web應(yīng)用級(jí)別>Servlet運(yùn)行時(shí)context級(jí)別,這時(shí)候低級(jí)別的設(shè)定會(huì)覆蓋高級(jí)別的設(shè)定。
a.Tomcat級(jí)別的設(shè)定。
若你需要設(shè)定30分鐘的會(huì)話過(guò)期,你可以在{TOMCAT_HOME}\conf\web.xml中進(jìn)行設(shè)定如下:
<session-config>
<session-timeout>30</session-timeout><!-- set in minutes -->
</session-config>
b.Web應(yīng)用級(jí)別的設(shè)定。
若你需要設(shè)定15分鐘的會(huì)話過(guò)期,你可以在{TOMCAT_HOME}\webapps\ {APP_NAME}\WEB-INF\web.xml中這樣進(jìn)行設(shè)定:
<session-config>
<session-timeout>15</session-timeout><!-- set in minutes -->
</session-config>
c.在程序代碼中進(jìn)行設(shè)定。
若你需要在程序中設(shè)定5分鐘的會(huì)話過(guò)期,你可以用下面一行代碼來(lái)實(shí)現(xiàn):
httpSession.setMaxInactiveInterval(5*60); // set in seconds
如果我們按照上面的步驟進(jìn)行了會(huì)話過(guò)期設(shè)置,那么最后真正起作用的是在程序中進(jìn)行設(shè)定的5分鐘。
再看什么是硬會(huì)話過(guò)期。它指的是用戶登錄到系統(tǒng)中經(jīng)過(guò)一定的時(shí)間后,不管用戶做什么,該會(huì)話都會(huì)過(guò)期。大家都知道網(wǎng)絡(luò)游戲防沉迷系統(tǒng)吧?如果未成年人的累計(jì)在線時(shí)間已滿5小時(shí),則累計(jì)在線時(shí)間清零,這個(gè)與我們這里說(shuō)的硬會(huì)話過(guò)期很相似,只不過(guò)我們這里不是在線時(shí)間清零,而是強(qiáng)制用戶退出并重新登錄。
那么硬會(huì)話過(guò)期有什么用呢?它主要是用來(lái)防止永久的對(duì)一個(gè)賬號(hào)劫持。比如說(shuō)一個(gè)***者通過(guò)XSS得到了受害者的session,并用它冒充受害者進(jìn)行登錄,如果我們?cè)O(shè)定了硬會(huì)話過(guò)期,則經(jīng)過(guò)了一段時(shí)間之后,系統(tǒng)會(huì)強(qiáng)制用戶重新進(jìn)行認(rèn)證。
沒有專門的API或者配置來(lái)設(shè)定硬會(huì)話過(guò)期,但我們可以通過(guò)在web filter中寫自己的代碼來(lái)實(shí)現(xiàn)這個(gè)功能?;舅悸啡缦拢簩?duì)每個(gè)用戶登錄成功后記錄下此時(shí)的時(shí)間,并且把這個(gè)時(shí)間與他們的Session ID綁定起來(lái),如果用同一個(gè)Session ID發(fā)送的請(qǐng)求的時(shí)間減去這個(gè)Session ID剛登錄成功的時(shí)間大于了我們?cè)O(shè)定的會(huì)話過(guò)期時(shí)間,則使這個(gè)會(huì)話無(wú)效,并重定向到登錄頁(yè)面。
3.保護(hù)你的Cookie
Cookie有兩個(gè)很重要的屬性:secure和HttpOnly,設(shè)置好這兩個(gè)屬性對(duì)于保護(hù)你的Cookie至關(guān)重要。
首先說(shuō)secure屬性。聲明了它,則說(shuō)明當(dāng)前這個(gè)Cookie只會(huì)在HTTPS的鏈接中進(jìn)行傳遞,這樣就可以使得***者無(wú)法很容易地通過(guò)分析網(wǎng)絡(luò)流量來(lái)獲得會(huì)話ID,從而有效地防治了中間人***(Man-in-the-Middle)。
HttpOnly這個(gè)屬性我們?cè)?/span>XSS這一章已經(jīng)介紹過(guò),它不允許一些腳本(如JavaScript等)直接操作document.cookie這個(gè)DOM對(duì)象,這個(gè)屬性對(duì)于阻止通過(guò)XSS竊取會(huì)話ID是必需的。
一個(gè)好消息是,Tomcat 7支持了Servlet 3.0,所以我們可以在web.xml設(shè)定上面的兩個(gè)屬性。
<session-config>
<cookie-config>
<secure>true</secure>
</cookie-config>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config>
需要注意的是Tomcat 6以前的版本不支持,Tomcat 6支持的是Servlet 2.5。
4.提供logout功能
上面介紹的是系統(tǒng)自動(dòng)按照設(shè)定的時(shí)間使會(huì)話過(guò)期,一個(gè)好的應(yīng)用程序應(yīng)該提供一個(gè)功能,即用戶可以手動(dòng)地使當(dāng)前會(huì)話過(guò)期,這就是我們?cè)趲缀跛芯W(wǎng)站上都看到的logout按鈕。那么一般的logout需要完成哪些功能呢?讓我們看看ESAPI中是如何實(shí)現(xiàn)logout功能的吧。
Class: org.owasp.esapi.reference.DefaultUser
public void logout() {
ESAPI.httpUtilities().killCookie( ESAPI.currentRequest(),
ESAPI.currentResponse(),
HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME );➊
HttpSession session = ESAPI.currentRequest().getSession(false);
if (session != null) {
removeSession(session);
session.invalidate();➋
}
ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(),
ESAPI.currentResponse(),
"JSESSIONID");➌
loggedIn = false;
logger.info(Logger.SECURITY_SUCCESS, "Logout successful" );
ESAPI.authenticator().setCurrentUser(User.ANONYMOUS);
}
killCookie的實(shí)現(xiàn)代碼如下:
public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) {
String path = "//";
String domain="";
Cookie cookie = getFirstCookie(request, name);
if ( cookie != null ) {
path = cookie.getPath();
domain = cookie.getDomain();
}
Cookie deleter = new Cookie( name, "deleted" );
deleter.setMaxAge( 0 );➍
if ( domain != null ) deleter.setDomain( domain );
if ( path != null ) deleter.setPath( path );
response.addCookie( deleter );
}
我們簡(jiǎn)單地分析一下上面的代碼:
➊ 的作用是清除remember me這個(gè)Cookie,這是針對(duì)網(wǎng)站有remember這個(gè)功能來(lái)說(shuō)的。
➋ 是使得當(dāng)前的會(huì)話無(wú)效,這樣即使當(dāng)前的會(huì)話ID泄露出去了,***者也無(wú)法用這個(gè)會(huì)話ID進(jìn)行登錄。
➌ 的作用是清除JSESSIONID這個(gè)Cookie。
➍ 使deleter(與傳遞進(jìn)來(lái)的Cookie同名)立即無(wú)效。
本文節(jié)選自《Web應(yīng)用安全威脅與防治——基于OWASP Top 10與ESAPI》
王文君 李建蒙 編著
電子工業(yè)出版社出版