有两种方法携带Session ID:一是使用cookie,一是URI。

Cookie

HTTP 1.1支持Cookie,具体在RFC6265 HTTPState Management Mechanism。Client发送HTTP请求,Server在HTTP响应中带有Set-Cookie消息头,Client随后的HTTP请求将携带Cookie消息头,其值根据Set-Cookie的信息设定。

Set-Cookie和Cookie的值是键值对,可以多个,用分号“;”进行分隔。Set-Cookie有几个特定含义的键值对,这些值是用作控制信息的,浏览器在随后的请求中无需携带。

  • DomainPath:domain通知浏览器那个domain要发送cookie,而path进一步限制特定的URL。浏览器发送请求时,会将所有的匹配domain和path的cookie发送出去。如果之前有多个Set-Cookie中值都符合条件,将一起发送。
  • ExpiresMax-Age:expires给出cookie确定的到期日期,而Max-Age则给出还有多少秒有效期。浏览器在到期后会删除cookie,如果没有给出这两个值,那么浏览器在关闭的时候会删除cookie。讨论:如果Expires时候很久远,例如设置2021年某月某日某时,浏览器关闭后,并不删除这个值,可以利用此来放置用户的登陆名字。但具体的效果要取决于浏览器的选择。
  • Secure:secure属性不需要值,浏览器将只在HTTPS中给出cookie。
  • HttpOnly:httpOnly属性也没有值,浏览器只在直接请求中给出cookie,即不在flash和javasript中给出。

Java EE的sesion cookie的名字是JSEESSIONID,用来放置seesion id。

URL

这是一个携带Session信息的URL,下面给出PHP Server和J2EE Server的例子,其中PHP Server和普通的GET参数一样,而J2EE则不会和query string有所混淆。

Session ID是位于query string之前的,紧跟URL。和Cookie方式相比较,这种方式无需client对cookie进行保存。第一个session ID可以通过302重定向消息中的Location携带,随后的form action和链接中的URL中都要包括Session ID信息。在代码需要每次都在URL中添加相应的信息,J2EE提供了帮助,代码如下:

request.getSession(true);
System.out.println(response.encodeURL("index.jsp"));
System.out.println(response.encodeRedirectURL("index.jsp"));

我们可以看到:显示为index.jsp;jsessionid=FAAE19345A4B6FE365D707FC21945B24,encodeURL()和encodeRedirectURL()帮助在URL中加入jsessionID。只有session有效,且没有出现在请求消息中,且属于同一个web app的相对路径才会在URL中加入JsessionId。如果例子程序在浏览器中测试失败,请在浏览器设置中删除相关的cookie,例如在firefox中-选项-隐私-移除单个cookie,这是因为如果在request中Cookie中含有jsessionid,而不会采用URL带session id的方式,因此需要将之删除。

<c:url>tag同样也可以在URL中加入session id。

Session的脆弱

建议在部署之前应对web进行安全扫描。

在URL中加入session id的一个问题是很容易被人copy URL,从而被视为你进入你的session。因此,在安全性方面,Cookie比URL的方式要安全。

会话固定攻击(session fixation attack):如果web同时接受Cookie和URL的sessionid,攻击者向用户发送一个包含session id的URL(可能是从其浏览器cookie中获取的),用户点击后,其session id就会固定为URL中的session id,黑客就可以用这个session id进入用户账户。要避免,可以禁止从URL中获取Session ID,或者在用户登录之后,修改session id或者复制到新的session并关闭原有session。

另一种session fixation attack和浏览器有关,恶意网站设置了其他网站的session(在HTTP协议是允许)。现在的浏览器禁止了对不同domain的session id设置,但是同一domain,例如a.example.com可以设置b.example.com的session id,要防范此,就是不要向不信任的人共享domain。

跨网站脚本攻击(cross-site scripting attacks):将JavaScript脚本注入到网页,利用JavaScript DOM 的 document.cookie获取session id。要防止这样攻击,设置Cookie的HttpOnly的tag,这样JavaScript、flash和其他浏览器plunin的脚本就无法获取session id,甚至无法知道是否存在。带有HttpOnly,浏览器将在HTTP和HTTPS,包括链接、form确认、Ajax请求中中带有cookie。

中间人攻击(man-in-the-middle attack):也缩写为MitM attack,也就是在client和server之间截获数据包。可以采用HTTPS进行保护,但是有个问题,如果用户在浏览器请求中使用了HTTP,而非HTTPS,即使server进行了redirect,仍可能泄漏。这时应该将secure置位,即只有在HTTPS请求时才携带session id。

对于HTTPS中,为了避免每次请求都要获取加密的key,引入了SSL/TLSSession ID,理念和HTTP相似,引入了SSL/TLS Session ID是在HTTPS握手加分配的,决定后续的请求使用哪个key进行加密。要获取SSL/TLS Session ID就困难很多。在一些金融网站中,不再使用Cookie或者URL携带session id,而是将SSL/TSL Session ID作为session id。这种方式是相当安全的,万一存在相关漏洞,浏览器会很快升级版本消除隐患。它的问题在于在J2EE 6之前,这不是J2EE的标准,需要使用到一些web容器特定的类,但在J2EE之后,很容易配置web容器使用SSL session ID。采用SSL Seesion ID需要注意的一个问题是在集群环境中,相关的请求必须送到同一webserver中进行处理,这和SSL通信有关。SSL Session Id的有效期具体由浏览器和server决定,可能对太长,可能会太短,因此很难完全替代HTTP SessionID。(在集群方式中,如果load balance就是HTTPS的server(管理了SSL通信),进行HTTPS和HTTP的转化,例如NGINX可以这样做,那么SSL Session ID就无法传递到后面具体处理业务的服务器上。)


相关链接: 我的Professional Java for Web Applications相关文章