java 如何防止tomcat会话劫持?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1422977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to prevent tomcat session hiHymaning?
提问by Marcel
In my web.xml I've defined a user-data-constraint for some resources:
在我的 web.xml 中,我为一些资源定义了一个用户数据约束:
<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>
- When I load the page with http I've got my JSESSIONID ID1 in my cookie.
- When I change to context/user/sample.faces then Tomcat makes a 302 redirect to HTTPS. But my JSESSIONID is still ID1.
- 当我使用 http 加载页面时,我的 cookie 中有我的 JSESSIONID ID1。
- 当我更改为 context/user/sample.faces 时,Tomcat 会将 302 重定向到 HTTPS。但是我的 JSESSIONID 仍然是 ID1。
I think this is a vulnerability? Or is it my configuration mistake?
我认为这是一个漏洞?还是我的配置错误?
The problem I see is the following: While browsing over HTTP with cookie ID1 there is an attacker who is listening to my network traffic. He "steals" my cookie ID1. Now I switch to HTTPS and my cookie is still ID1. I login. The attacker is then able to taker over my session because he knows my cookie...
我看到的问题如下:在使用 cookie ID1 通过 HTTP 浏览时,有一个攻击者正在监听我的网络流量。他“偷”了我的cookie ID1。现在我切换到 HTTPS 并且我的 cookie 仍然是 ID1。我登录。然后攻击者能够接管我的会话,因为他知道我的 cookie...
采纳答案by Vinay Sajip
If it's a recent version of Tomcat, you may not have a problem. However, this depends on your checking the SSL ID associated with the session. This is available using code such as
如果是最新版本的Tomcat,则可能没有问题。但是,这取决于您检查与会话关联的 SSL ID。这可以使用代码获得,例如
String sslId = (String) req.getAttribute("javax.servlet.request.ssl_session");
(Note that the attribute key may change in the future to javax.servlet.request.ssl_session_id- as part of the Servlet 3.0 spec).
(请注意,javax.servlet.request.ssl_session_id作为 Servlet 3.0 规范的一部分,属性键将来可能会更改为-)。
I set up a servlet with the following doGetmethod:
我使用以下doGet方法设置了一个 servlet :
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();
}
I then invoked a suitable unprotected URL using Firefox and the Live HTTP Headers extension, to get the session cookie. This was the response sent when I navigated to
然后我使用 Firefox 和 Live HTTP Headers 扩展调用了一个合适的不受保护的 URL,以获取会话 cookie。这是我导航到时发送的响应
http://localhost:8080/EchoWeb/unprotected
(my web.xml, like yours, only protects /user/* and /personal/*):
(我的 web.xml 和你的一样,只保护 /user/* 和 /personal/*):
URI: /EchoWeb/unprotected SID: 9ACCD06B69CA365EFD8C10816ADD8D71 SSLID: null Info: unprotected: 1254034761932 Secret: null Date: 27/09/09 07:59
Next, I tried to access a protected URL
接下来,我尝试访问受保护的 URL
http://localhost:8080/EchoWeb/personal/protected
and, as expected, I got redirected to
而且,正如预期的那样,我被重定向到
https://localhost:8443/EchoWeb/personal/protected
and the response was
回应是
URI: /EchoWeb/personal/protected SID: 9ACCD06B69CA365EFD8C10816ADD8D71 SSLID: 4abf0d67549489648e7a3cd9292b671ddb9dd844b9dba682ab3f381b462d1ad1 Info: unprotected: 1254034761932 Secret: protected: 1254034791333 Date: 27/09/09 07:59
Notice that the cookie/session ID is the same, but we now have a new SSLID. Now, let's try to spoof the server using the session cookie.
请注意,cookie/会话 ID 是相同的,但我们现在有了一个新的 SSLID。现在,让我们尝试使用会话 cookie 来欺骗服务器。
I set up a Python script, spoof.py:
我设置了一个 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()
Now, you don't need to know Python, particularly - I'm just trying to send an HTTP request to a (different) protected resource with the same session ID in the Cookie. Here's the response when I ran my spoof script twice:
现在,您不需要了解 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
Notice in the above responses that the session data (a value with a timestamp of 1254034761932) which was set in the first, unprotected request, has been sent throughout, because Tomcat is using the same session because the session ID is the same. This is of course not secure. However, note that the SSL IDs were different each timeand if you use thoseto key into your session data (e.g. as shown), you should be safe. If I refresh my Firefox tab, here's the response:
请注意,在上述响应中,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
Notice that the SSLID is the sameas for the earlier Firefox request. So, the server can tell the sessions apart using the SSL ID value. Notice particularly that the "protected data" is the same for each request made from the Firefox session, but differentfor each of the spoofed sessions and also different from the Firefox session.
请注意,SSLID是相同的,作为较早的Firefox的请求。因此,服务器可以使用 SSL ID 值区分会话。请特别注意,Firefox 会话发出的每个请求的“受保护数据”都是相同的,但每个欺骗会话的“受保护数据”都不同,并且与 Firefox 会话也不同。
回答by Valentino Miazzo
I suggest to change the sessionId when you authenticate the session.
In this way the old sessionId becomes useless and session hiHymaning is impossible.
To change the sessionId in a servlet container:
我建议在验证会话时更改 sessionId。
通过这种方式,旧的 sessionId 变得无用,会话劫持是不可能的。
要更改 servlet 容器中的 sessionId:
- copy all the attributes of the current session on a temp collection
- session.invalidate()
- session = req.getSession(true)
- fill the new session with the attributes from the temp collection
- 在临时集合上复制当前会话的所有属性
- session.invalidate()
- session = req.getSession(true)
- 使用临时集合中的属性填充新会话
About SSLID, please note that both client and server are free to close the connection at any time. When closed a new SSL handshake will happen and a new SSID generated. So, IMO SSLID is not a reliable way to track (or help to track) sessions.
关于SSLID,请注意客户端和服务器都可以随时关闭连接。当关闭时,将发生新的 SSL 握手并生成新的 SSID。因此,IMO SSLID 不是跟踪(或帮助跟踪)会话的可靠方法。
回答by ZZ Coder
I think it works like this by design. You can't base your access control on session. You need to use other parameters. You need to add authentication and use role-based control.
我认为它的工作原理是这样的。您不能基于会话进行访问控制。您需要使用其他参数。您需要添加身份验证并使用基于角色的控制。
In Tomcat, there is protection but exactly opposite. If you get a session in secure area, that session is not transfered to unprotected area. Tomcat achieves this by setting "secure" flag on the cookie so the cookie is not sent to the HTTP connections.
在Tomcat中,有保护,但恰恰相反。如果您在安全区域获得会话,则该会话不会转移到未受保护的区域。Tomcat 通过在 cookie 上设置“安全”标志来实现这一点,这样 cookie 就不会发送到 HTTP 连接。

