2009-09-14 85 views
15

在我的web.xml我已經定義了一個用戶數據約束一些資源:如何防止tomcat會話劫持?

<security-constraint> 
    <web-resource-collection> 
     <web-resource-name>Personal Area</web-resource-name> 
     <url-pattern>/personal/*</url-pattern> 
    </web-resource-collection> 
    <web-resource-collection> 
     <web-resource-name>User Area</web-resource-name> 
     <url-pattern>/user/*</url-pattern> 
    </web-resource-collection> 
    <user-data-constraint> 
     <transport-guarantee>CONFIDENTIAL</transport-guarantee> 
    </user-data-constraint> 
</security-constraint> 
  1. 當我加載頁面的HTTP我有我的JSESSIONID ID1在我的cookie。
  2. 當我更改爲context/user/sample.faces時,Tomcat將302重定向到HTTPS。但是我的JSESSIONID仍然是ID1。

我認爲這是一個漏洞?或者它是我的配置錯誤?

我看到的問題如下:使用Cookie ID1通過HTTP瀏覽時,有一個攻擊者正在偵聽我的網絡流量。他「竊取」我的Cookie ID1。現在我切換到HTTPS,我的cookie仍然是ID1。我登錄。攻擊者然後能夠接受我的會話,因爲他知道我的cookie ...

+0

重新評論:SSLID相同的原因是會話是一樣的(畢竟我只是單擊了Firefox中的刷新)。你可以在會話管理中使用這個事實。至於SSLID是如何構建的 - 它不包含在servlet規範中,因此每個供應商都可以使用自己的機制。我希望你必須檢查Tomcat的來源。無論如何,你不應該依賴任何具體的實現 - 就像你使用JSESSIONID一樣使用它 - 作爲一個不透明的值。 – 2009-09-28 15:47:53

回答

11

如果它是最新版本的Tomcat,則可能沒有問題。但是,這取決於您檢查與會話關聯的SSL ID。這是可使用的代碼如

String sslId = (String) req.getAttribute("javax.servlet.request.ssl_session"); 

(請注意,屬性關鍵可能在未來改變javax.servlet.request.ssl_session_id - 作爲servlet 3.0規範的一部分)。

我成立了一個servlet用以下方法doGet

protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException { 
    HttpSession session = request.getSession(true); 
    String sid = session.getId(); 
    String sslId = (String) request.getAttribute(
       "javax.servlet.request.ssl_session"); 
    String uri = request.getRequestURI(); 
    OutputStream out = response.getOutputStream(); 
    PrintWriter pw = new PrintWriter(out); 
    HashMap<String, Object> secrets; 
    Object secret = null; 
    Object notSecret; 
    Date d = new Date(); 

    notSecret = session.getAttribute("unprotected"); 
    if (notSecret == null) { 
     notSecret = "unprotected: " + d.getTime(); 
     session.setAttribute("unprotected", notSecret); 
    } 
    secrets = (HashMap<String, Object>) session.getAttribute("protected"); 
    if (secrets == null) { 
     secrets = new HashMap<String, Object>(); 
     session.setAttribute("protected", secrets); 
    } 
    if (sslId != null) { 
     if (secrets.containsKey(sslId)) 
      secret = secrets.get(sslId); 
     else { 
      secret = "protected: " + d.getTime(); 
      secrets.put(sslId, secret); 
     } 
    } 
    response.setContentType("text/plain"); 
    pw.println(MessageFormat.format("URI: {0}", new Object[] { uri })); 
    pw.println(MessageFormat.format("SID: {0}", new Object[] { sid })); 
    pw.println(MessageFormat.format("SSLID: {0}", new Object[] { sslId })); 
    pw.println(MessageFormat.format("Info: {0}", new Object[] { notSecret })); 
    pw.println(MessageFormat.format("Secret: {0}", new Object[] { secret })); 
    pw.println(MessageFormat.format("Date: {0}", new Object[] { d })); 
    pw.close(); 
} 

我然後調用使用Firefox和Live HTTP頭延伸,以獲得會話cookie合適的未受保護的URL。這是當我瀏覽到

http://localhost:8080/EchoWeb/unprotected 

發送的響應(我的web.xml中,像你這樣,不僅保護/用戶/ *和/個人/ *):

 
URI: /EchoWeb/unprotected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: null 
Info: unprotected: 1254034761932 
Secret: null 
Date: 27/09/09 07:59 

接下來,我試着訪問受保護的URL

http://localhost:8080/EchoWeb/personal/protected 

和,一如預期,我被重定向到

https://localhost:8443/EchoWeb/personal/protected 

,反應也

 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0d67549489648e7a3cd9292b671ddb9dd844b9dba682ab3f381b462d1ad1 
Info: unprotected: 1254034761932 
Secret: protected: 1254034791333 
Date: 27/09/09 07:59 

注意,該cookie /會話ID是一樣的,但我們現在有一個新的SSLID。現在,讓我們嘗試使用會話cookie來欺騙服務器。

我成立了一個Python腳本,spoof.py

import urllib2 

url = "https://localhost:8443/EchoWeb/personal/protected" 
headers = { 
    'Host': 'localhost:8080', 
    'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3', 
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 
    'Accept-Language': 'en-gb,en;q=0.5', 
    'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 
    'Cookie' : 'JSESSIONID=9ACCD06B69CA365EFD8C10816ADD8D71' 
} 
req = urllib2.Request(url, None, headers) 
response = urllib2.urlopen(req) 
print response.read() 

現在,你不需要知道Python的,特別是 - 我只是試圖發送一個HTTP請求(不同的)保護的資源在Cookie中具有相同的會話ID。這裏的響應,當我跑我的惡搞腳本兩次:在上面的反應

 
C:\temp>spoof 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0eafb4ffa30b6579cf189c402a8411294201e2df94b33a48ae7484f22854 
Info: unprotected: 1254034761932 
Secret: protected: 1254035119303 
Date: 27/09/09 08:05 


C:\temp>spoof 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0eb184cb380ce69cce28beb01665724c016903650539d095c671d98f1de3 
Info: unprotected: 1254034761932 
Secret: protected: 1254035122004 
Date: 27/09/09 08:05 

注意,會話數據,在第一,未受保護的要求設置(與1254034761932時間戳值),目前已在發,因爲Tomcat正在使用相同的會話,因爲會話ID是相同的。這當然是不安全。但是,請注意,SSL ID 每次都是不同的,如果您使用這些鍵入會話數據(例如,如圖所示),則應該是安全的。如果我刷新我的Firefox標籤,這裏的響應:

 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0d67549489648e7a3cd9292b671ddb9dd844b9dba682ab3f381b462d1ad1 
Info: unprotected: 1254034761932 
Secret: protected: 1254034791333 
Date: 27/09/09 08:05 

注意,SSLID是相同作爲較早的Firefox請求。因此,服務器可以使用SSL ID值區分會話。特別要注意的是,對於每個來自Firefox會話的請求,「受保護的數據」都是相同的,但對於每個欺騙會話而言,不同,也不同於Firefox會話。

+0

偉大的解釋!但是我仍然有一個問題:它是如何工作的,你使用Firefox的最後一次調用發送了舊的SSLID。那個ID是如何建立的?此功能是否記錄在任何地方 – Marcel 2009-09-28 14:22:12

2

我認爲它的工作原理是這樣的設計。您不能將您的訪問控制設置爲會話。您需要使用其他參數。您需要添加身份驗證並使用基於角色的控制。

在雄貓,有保護,但完全相反。如果您在安全區域進行會話,則該會話不會轉移到未保護區域。 Tomcat通過在Cookie上設置「安全」標誌來實現這一點,從而使cookie不會發送到HTTP連接。

+0

我不基於會話訪問控制。上面的web.xml只是整個web.xml的一小部分。當然,我有一個基於JAAS的身份驗證方法。我試圖在我的描述中更清晰一些。 – Marcel 2009-09-24 15:12:33

2

我建議在驗證會話時更改sessionId。
以這種方式,舊的sessionId變得無用,會話劫持是不可能的。
要改變的SessionID在servlet容器:

  • 複製當前會話的所有屬性上的臨時收集
  • session.invalidate()
  • 會話= req.getSession(真)
  • 使用臨時集合中的屬性填充新會話

關於SSLID,請注意,客戶端和服務器都可以自由關閉任何連接 時間。 關閉時,將發生新的SSL握手並生成新的SSID。 因此,IMO SSLID不是跟蹤(或幫助跟蹤)會話的可靠方法。