一、需要会话的原因
从服务器的角度来说,当请求结束时,客户端与服务器之间就再有任何联系,如果有下一个请求开始时,就无法将新的请求与之前的请求关联起来。这是因为 HTTP请求自身是完全无状态的,会话就是用来维持请求和请求之间的状态的。
拿生活场景举例:你进入最喜欢的超市购物,找到一个购物车(从服务器获得会话),一边逛一边挑选喜爱的商品并将它们添加到购物车中(将商品添加到会话中),购物结束后将购物车中的商品取出并递给收银员,收银员扫描你购买的商品并接收你的付款(通过会话付款),付款结束后出门并还回购物车(关闭浏览器或注销,结束会话)。
二、使用会话cookie和URL重写
会话是由服务器或Web应用程序管理的某些文件、内存片段、对象或者容器,它包含了分配给它的各种不同的数据。这些数据元素可以是用户名、购物车、工作流细节等,用户浏览器中不用保持或维持任何此类数据,它们只由服务器或Web应用程序代码管理。容器和用户浏览器之间将通过某种方式连接起来,因此通常会话会被赋予一个随机生成的字符串,称为会话ID。
第一次创建会话时,服务器创建的会话ID将会作为响应的一部分返回到用户浏览器中,接下来从用户浏览器中发出的请求都将通过某种方式包含该会话ID,当应用程序收到含有会话ID的请求时,它可以通过该ID将会话与当前请求关联起来。剩下需要解决的问题就是如何将会话ID从服务器返回到浏览器中,并在之后的请求中包含该ID,目前有两种可用的技术:会话cookie和URL重写。
1、了解会话cookie
cookie是一种必要的通信机制,可以通过Set-Cookie响应头在服务器和浏览器之间传递任意的数据,并存储在用户计算机中,然后再通过请求头Cookie从浏览器返回到服务器中。cookie可以有各种不同的特性,如下:
① Domain:告诉浏览器应该将cookie发送到哪个域名中;
② Path:进一步将cookie限制在相对于域的某个特定URL中,每次浏览器发出请求时,它都将找到匹配该域和路径的所有cookie;
③ Expires:与Max-age,HTTP/1.0会优先处理该指令,它定义了cookie的绝对过期日期,如果cookie已经过期,浏览器将会立即删除它;
④ Max-age:与Expires互斥,HTTP/1.1会优先处理该指令,它定义了cookie将在多少秒后过期,如果cookie中不含有Expires和Max-age,cookie将会浏览器关闭时被删除;
⑤ Secure:(不需要有值),浏览器将只会通过HTTP发送cookie,这将保护cookie,避免以未加密的方式进行传输;
⑥ HttpOnly:它把cookie限制在直接的浏览器请求中,使得JavaScript、Flash以及其他插件无法访问cookie。
注意:尽管HttpOnly将阻止JavaScript使用doucment.cookie属性来访问cookie,但由JavaScript创建的AJAX请求仍然会包含会话ID cookie,因为是浏览器负责AJAX请求头的生成而不是JavaScript,这意味着服务器仍然能够将AJAX请求关联到用户的会话。
当Web服务器和应用服务器使用cookie在客户端存储会话ID时,这些ID将随着每次的请求被发送到服务器端,在JAVAEE应用服务器中,会话cookie的名字默认为JSESSIONID
2、URL中的会话ID
JavaEE服务器将会话ID添加到URL的最后一个路径段的矩阵参数中,通过这种方式分离开会话ID与查询字符串的参数,使它们不会互相冲突。
请求URL只会在将会话ID从浏览器发送到服务器时有效,那么第一次如何将请求URL中的会话ID从服务器发送到浏览器呢?答案是必须将会话ID内嵌在应用程序返回的所有URL中,包括页面的链接、表单操作以及302重定向。
HttpServletResponse接口定义了两个可以重写URL的方法:encodeURL和encodeRedirectURL,它们将在必要的时候将会话ID内嵌在URL中,任何在链接、表单操作或其他标签中的URL都将被传入到encodeURL方法中,然后该方法将会返回一个正确的、经过编码处理的URL(任何传入sendRedirect响应方法中的URL可以传入encodeRedirectURL方法中)。将JSESSIONID矩阵参数内嵌在URL的最后一个路径段中需要满足下面4个条件:
① 会话对于当前请求是活跃的(要么它通过传入会话ID的方式请求会话,要么应用程序创建了一个新的会话);
② JSEESSIONID cookie在请求中不存在;
③ URL不是绝对的URL,并且是同一Web应用程序中的URL;
④ 在部署描述符中已经启用了对会话URL重写的支持。
3、会话的漏洞
⑴ 复制并粘贴错误
不知情的用户决定要跟朋友分享应用程序中的某个页面,并将地址栏中的URL复制粘贴出来,那么他的朋友将看到URL中包含的会话ID,如果他们在该会话终结之前访问该URL,那么他们也会被服务器当成之前分享URL的用户,这明显会引起问题。
解决此问题的方法是:完全禁止在URL中内嵌会话ID。
⑵ 会话固定
攻击者会首先找到允许在URL中内嵌会话ID的网站,然后获取一个会话ID,并将含有会话ID的URL发送给目标用户。此时,当用户点击链接进入网站时,他的会话ID就变成了URL中已经含有的会话ID(攻击者已经持有的会话ID),如果用户接着在该会话期间登录网站,那么攻击者也可以登录成功。
解决这个问题的两种方法:一是同复制粘贴一样禁止在URL中内嵌会话ID,二是在登录后采用会话迁移,即当用户登录后,修改会话ID或者将之前的会话信息复制到一个新的会话中,并使之前的会话无效。
⑶ 跨站脚本和会话劫持
攻击者将利用网站的漏洞实行跨站脚本攻击,将JavaScript注入到某个页面,通过document.cookie读取会话ID cookie中的内容,当攻击者从用户处获得会话ID后,他可以通过在自己的计算机中创建cookie来模拟该会话。
解决这个问题的方法是:不要在网站中使用跨站脚本,并在所有的cookie中使用HttpOnly特性。
⑷ 不安全的cookie
最后一个需要考虑的是中间人攻击(MitM攻击),这是典型的数据截获攻击,攻击者通过观察客户端和服务端的交互或响应,从中获取信息。
解决这个问题的方法是:使用HTTPS来保护网络通信,cookie的secure标志将告诉浏览器只应该通过HTTPS传输cookie。
三、在会话中存储数据
1、在部署描述符中配置会话
下面是一份详细的关于在部署描述符中配置会话的说明:
<!--下面标签中的标签选项都是可选的,如果选择了要按照一定的顺序添加-->
<session-config>
<!--会话的超时时间(单位:分钟),Tomcat默认30分钟,如果此值小于0将永远不会过期-->
<session-timeout>30</session-timeout>
<!--只有在追踪模式中使用了cookie时,才可以使用此标签-->
<cookie-config>
<!--默认值,不需要修改-->
<name>JSESSIONID</name>
<!--domain标签和path标签对应着cookie的Domain和Path特性,Web容器已经设置了正确的默认值,通常不需要修改它们-->
<domain>example.org</domain>
<path>/shop</path>
<!--此标签内可以添加任意文本-->
<comment>other info</comment>
<!--此标签对应cookie的HttpOnly特性,默认为false,为了提高安全性,应将其设为true-->
<http-only>true</http-only>
<!--此标签对应cookie的Secure特性,默认为false,如果使用了HTTPS,应将其设为true-->
<secure>false</secure>
<!--此标签对应cookie的Max-Age特性,用于控制cookie的过期时间(单位:秒),默认没有过期时间(相当于设置为-1),
即浏览器关闭就过期。最好保持默认值不变,正常情况下也不要去使用这个标签-->
<max-age>1800</max-age>
</cookie-config>
<!--表示容器的追踪策略,可以设置一个或多个策略,从上到下安全等级递增,当使用了更高安全等级的策略就不可再使用低等级的策略-->
<tracking-mode>URL</tracking-mode>
<tracking-mode>COOKIE</tracking-mode>
<tracking-mode>SSL</tracking-mode>
</session-config>
下面是一份常用的关于在部署描述 符中配置会话的简单配置:
<!--该配置设置会话过期时间为30分钟,追踪策略为COOKIE,使用HttpOnly特性来解决安全问题,其他的将接受默认值-->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>