【转】安全的session管理
好文章,转载以备份。原文出自这里。
前言
session 的管理對網站應用程式非常的重要,不適當或不夠嚴謹的管理也會造成安全上的問題,以下針對 session 管理相關的安全問題分項探討。
使用 Framework 內建的 Session Manager
藉由 session 限制與維護使用者的行為是網路安全很重要的一環。大多數的人會使用網站應用程式框架內建的 session 管理,有些人會使用 Perl CGI。網站開發者應盡量避免自行開發 session 管理,因為自行開發常常會藏有許多漏洞,而框架內建的 session 管理經過多次測試與修復相對上較為安全。此外這些框架會持續維護其安全性,因此要確保做好更新與安裝修補程式的動作。
- 密碼強度
Session handler 的一個重點是 session token 或 session ID 的密碼強度,Session handler 產生的 token 必須是無法預知並且長度夠長讓人無法猜測的到。Session tokens 必須每個使用者都不同、無法預測、能抵抗反向工程。 - 適當的 Key Space
一個加密的演算法也可能因為 Key space 不夠大造成攻擊者使用暴力解來取得內容。因此 token 的 Key space 必須足夠大來防禦暴力攻擊法,並且注意電腦計算能力與寬頻能力已隨著時代進步。 - Session Identifier(session ID)
Session Identifier 應該使用最大可用的 character set,如果一個 session ID 要由 8 characters of 7 bits 組成,有效密鑰長度為56位,但如果使用的 character set 只有整數可用 4 bits表示,有效密鑰長度就只有32位。因此一個好的 session ID 應盡量使用越多 characters 越好,但一些特殊字元有轉譯的麻煩,所以大多的 frameworks 採用 A-Z 和 0-9 有些還添加了小寫 a-z。
驗證由客戶端傳來的 Session ID
所有由客戶端傳來的資料都必須經過編碼和驗證,許多 frameworks 會驗證和編碼從 GET 和 POST 而來的 input,但未充分編碼從客戶端 cookie 傳來的 session ID 值。下面的 ESAPI code 片段使用 ESAPI 中的 method 來驗證 session ID 的值:
public String getRequestedSessionId() { String id = request.getRequestedSessionId(); String clean = " "; try { clean = ESAPI.validator().getValidInput( "Requested cookie: " + id, id, "HTTPJSESSIONID", 50, false ); } catch (ValidationException e ) { // already logged } return clean; }
確保 idle 和 timeouts 時間夠短
根據業務需求和安全性的考量,session 必須有一有限的壽命,在一定時間後過期。網站應用程式必須將靜止一段時間的 session 設為過期,刪除此 session 並一併更改 session cookie 的內容。
在使用者登出後銷毀 session
當使用者登出後網站應用程式需讓 session 無效或者移除此 session,並且如果隨後有別的使用者登入必須取得不同的 session ID。要做到這樣的機制,當使用者 logout 時必須改寫 session cookies 註明已過期並銷毀 session。以下是 ESAPI code 的例子:
public void logout() { ESAPI.httpUtilities().killCookie( ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME ); HttpSession session = ESAPI.currentRequest().getSession(false); if (session != null) { session.invalidate(); } ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), "JSESSIONID"); loggedIn = false; logger.info(Logger.SECURITY, "Logout successful" ); ESAPI.authenticator().setCurrentUser(User.ANONYMOUS); }
public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) { String path = "//"; String domain=" "; Cookie cookie = ESAPI.httpUtilities().getCookie(request, name); if ( cookie != null ) { path = cookie.getPath(); domain = cookie.getDomain(); } SafeResponse safeResponse = new SafeResponse( response ); safeResponse.addCookie(name, "deleted", 0, domain, path); }
輪換 Session ID
對於高安全性網站,網站應用程式在處理重要的程序之前,或是經過某一段時間和幾次 requests 後,必須重新產生 session ID。中等或低等的網站,在使用者權限改變時也應該重新產生 session ID ,例如從訪客變成登入的會員或從登入者變成管理者。以下是 ESAPI code 重新產生 Session ID 的例子:
public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException { // get the current session HttpSession session = request.getSession(); // make a copy of the session content Map temp = new HashMap(); Enumeration e = session.getAttributeNames(); while (e != null && e.hasMoreElements()) { String name = (String) e.nextElement(); Object value = session.getAttribute(name); temp.put(name, value); } // kill the old session and create a new one session.invalidate(); HttpSession newSession = request.getSession(); // copy back the session content Iterator i = temp.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); newSession.setAttribute((String) entry.getKey(), entry.getValue()); } return newSession; }
保護 Session ID
如果可以的話,網站應用程式應該都以 HTTPS 的方式傳輸。如果無法,至少包含敏感性資料的頁面或處理程序要使用 HTTPS; 如果 HTTPS 無法保護整個網站的 session,在 HTTPS 傳輸時必須搭配 session ID,將此 session ID 和網站伺服器的 session 做配對檢查。
如果必須藉由 URL 參數來傳遞 session ID 時,必須使用 POST。如果 cookies 用來儲存並由 HTTPS 傳送 session ID 時,必須被設為”安全”這樣就不會經過 non-SSL 的管道。
網站應用程式必須提供登出的機制,並確保登出後此 session 過期和被銷毀。
作者列表
- 2011/03/21 姚辰旻 初稿
參考資料
PS:
ESAPI的站点都访问不了?