公司最近需要接入第三方系统,由于系统采用的是session认证,导致在移动设备上出现问题,因为一般情况下session认证只适合同源场景,在移动端更好的解决方案应该是JWT(JSON WEB TOKEN)解决方案,如果我非要使用session的话,有没有什么办法呢?在研究的过程中,对会话跟踪和安全有了更深的理解,在这里记录!
会话跟踪
服务器需要识别请求来自哪个客户端,这就需要用到会话跟踪,本质原理都是通过sessionId。常用的方式有如下几种:
1.Cookie
cookie在浏览器允许使用的状态下,当客户请求服务器后,服务器会发送一个含有唯一的sessionID的cookie给客户端,并保存到客户端本地,下次客户端再和服务器交互时,通过在request头中加入cookie中的信息,服务器能辨识出客户身份,从而维持会话。
2.隐藏域
适用于表单提交
<input type="hidden" name="sessionid" value="12345">
隐藏域的优缺点
- 优点:Cookie被禁时可以使用
- 缺点:所有页面必须是表单提交之后的结果。
3.URL重写
有些用户为了安全,通常会选择关闭cookie,这种方式我们如何实现会话跟踪呢?答案就是我们可以通过重写URL的方式来达到目的。
在每个 URL 末尾追加一些额外的数据来标识 session 会话,服务器会把该 session 会话标识符与已存储的有关 session 会话的数据相关联。session 会话标识符被附加为 sessionid=12345,标识符可被 Web 服务器访问以识别客户端。
URL重写的优缺点:
- 优点:在Cookie被禁用的时候依然可以使用
- 缺点:必须对网站的URL进行编码,所有页面必须动态生成,不能用预先记录下来的URL进行访问。
4.HttpSession
会话对象生存于服务器上,会话自动通过Cookie或URL重写与客户端关联起来,允许我们将任何对象存储到会话中。
session由servlet容器产生,是一种会话跟踪技术,能解决用户在登录或者购物时多页面请求或存储相关用户信息。
上面说到的cookie,其中就包含有session的ID,session是借助cookie来实现会话跟踪的,所以如果cookie被删或者禁用的话,session这个会话跟踪技术也就无法使用了。
URL重写
在这里我们重点聊聊URL重写。
使用场景:为了防止用户禁用cookie,可以使用URL重写技术来实现会话跟踪!
url重写原理
当服务器程序调用request.getSession()时,内部处理步骤是这样的。
- 其会先看request.getCookies()方法中有没有名为JSESSIONID的cookie带过来
- 如果没有,就看URL有没有被重写(即附带JSESSIONID),如果有,则从服务器中找key为JSESSIONID的session对象
- 如果都没有,则创建一个新的session。如果用户禁用了cookie,则只能通过URL重写方式实现会话跟踪!
语法:
HttpServletResponse接口定义了两个用于URL重写的方法
- encodeURL用于超链接和form表单的action属性中设置的URL进行重写
- encodeRedirectURL方法用于对传递给HttpServletResponse.sendRedirect方法的URL进行重写。
例子:
调用response的encodeURL方法,将自动将JSESSION追加到url后面,如:url;jsessionid=BD111FFC653497E81B702A29B3AC6FE4。
服务器默认创建一个Cookie回传给用户:
Cookie cookie = new Cookie("JSESSIONID",session.getId());
response.addCookie(cookie);)
此cookie的默认生命周期为关闭浏览器cookie即销毁,所以当浏览器关闭后,使用cookie实现的回话跟踪应用(如购物)将会失效。
解决浏览器关闭,装载sessionid的cookie失效的问题: 可以通过创建一个名为"JSESSIONID"的cookie,然后设置cookie的生命周期(cookie.setMaxAge())来设置cookie的失效时间。
session的原理是通过向浏览器发送存放sessionId的cookie来实现session的,而存放sessionId的cookie的默认生命为浏览器关闭cookie消亡,所以当用户关闭浏览器后,其购物车商品会消失,为避免这种情况,应设置存放sessionId的cookie的生命周期。
cookie.setMaxAge(30*60);//设置放置sessionId的cookie的生命周期为30分钟。
如果用户禁用了cookie,则只能通过url重写的方式来实现回话跟踪。但是url重写的方式实现回话跟踪的不能解决浏览器关闭,无法获取原来的sessionid的问题,即不能解决浏览器关闭,回话失效的问题。
总结:
- 如果用户禁用cookie,则关闭了浏览器后,重新开启浏览器,则回话失效,无法实现回话跟踪;如果是用户没有禁用cookie,则可以通过设置装载JSESSIONID的cookie的失效时间来控制浏览器关闭后session仍未失效。
- 如果用户没有禁用cookie,而且又使用URL重写,则用户在第一次访问EncodeURLServlet时,由于不知道用户是否禁用了cookie,所以response.encodeURL()方法内部会将JSESSIONID重写在url上,但是一旦第二次访问时,由于用户是带着cookie来的,所以response.encodeURL()不会将JSESSIONID重写在url上。
Cookie简介
cookie路径:cookie一般都是由于用户访问页面而被创建的,可是并不是只有在创建cookie的页面才可以访问这个cookie。在默认情况下,出于安全方面的考虑,只有与创建cookie的页面处于同一个目录或在创建cookie页面的子目录下的网页才可以访问。那么此时如果希望其父级或者整个网页都能够使用cookie,就需要进行路径的设置。
让这个设置的cookie 能被其他目录或者父级的目录访问的方法:
document.cookie = "name = value; path=/";
cookie域:domain表示的是cookie所在的域,默认为请求的地址。
Cookie的优缺点:
- 优点:数据可以持久保存,不需要服务器资源,简单,基于文本的Key-Value
- 缺点:大小受到限制,用户可以禁用Cookie功能,由于保存在本地,有一定的安全风险。
此外对于每一个cookie可以单独设置域、路径、过期时间、httponly和secure等
HttpOnly属性用来禁止禁止js获取sessionId,具体如下:
一般的Cookie都从document对象中获取,在设置cookie的时候可以接受一个叫做HttpOnly的参数,一旦HttpOnly被设置,document对象中就看不到这个cookie,而浏览器在浏览的时候不受任何影响,因此Cookie会被放在浏览器头中发送出去,对于一些敏感的cookie采用HttpOnly,对于一些需要在应用程序中用js操作的cookie我们就不予设置。这样能有效防止XSS攻击。
Secure属性:当设置为true时,表示创建的cookie会被以安全的形式向服务器传输,也就是只能在HTTPS连接中被浏览器传递到服务器端进行会话验证,如何是HTTP连接则不会传递该信息,所以不会被窃取到cookie的具体内容。
sessionId简介
SessionId简单说明
- 第一次访问服务器的时候,会在响应头里面看到Set-Cookie信息,只有在首次访问服务器的时候
- 浏览器会根据响应头的set-cookie信息设置浏览器的cookie并保存
- 当再次请求的时候,非首次请求,浏览器会在请求头里将cookie发送给服务器,每次请求都是这样
Cookie&Session联系
当浏览器与服务器第一次连接的时候,服务器如果启用了session,那么他就会随机生成一个sessionid,然后发送一条setcookie的指令并且带上这个生成的sessionid给浏览器。
虽然浏览器设置了sessionid在cookie里面,但是这个cookie仍然是不能被访问到的,这个是出于跨域的安全考虑,此时服务器的session也会保存一份cookie在里面。
浏览器出于安全不让用户去访问这个sessionid,但是他自己可以访问,只要浏览器没有被关闭,那么这个sessionid在过期之前就会存在在浏览器的内存中,以后的每次请求,浏览器都会自动把这个sessionid添加到请求头部,浏览器收到这个请求,根据这个sessionid就可以识别用户了。
当用户提交数据的时候,浏览器会自动附带上sessionid的数据。服务器就可以获取这个sessionid,来把用户需要临时保存的信息保存在这个sessionid对应的session里面。
移动端跨域
介绍了这么多基础知识,回到最初的问题,移动端跨域有没有解决session的问题呢?百度后有如下办法,但是没有测试哈。
客户端
默认情况下通过CORS这样的方式是不会传递cookie.一般强制性将cookie添加到header的做法,也会被浏览器拒绝并报错。可以配置对象属性如下:
xhr.withCredentials = true; //支持跨域发送cookies
其他封装HTTP请求库的使用自行百度,比如jQuery和angular如下
//jQuery
$.ajax({
type: "POST",
url: "http://xxx.com/api/test",
dataType: 'jsonp',
xhrFields: {
withCredentials: true
},
crossDomain: true,
success:function(){},
error:function(){}
})
// angular
$http.post(url, {withCredentials: true, ...})
// 或者
$http({withCredentials: true, ...}).post(...)
// 或者
.config(function ($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}
服务器
单独客户端设置是没有作用的,需要服务端的配合,添加响应头header。
服务端设置header,Access-Control-Allow-Credentials来控制是否允许Cookie的提交。
header("Access-Control-Allow-Credentials: true");
关于安全
为什么开启HttpOnly能缓解XSS攻击呢?
XSS攻击
跨站点脚本攻击是困扰Web服务器安全的常见问题之一。跨站点脚本攻击是一种服务器端的安全漏洞,常见于当把用户的输入作为HTML提交时,服务器端没有进行适当的过滤所致。跨站点脚本攻击可能引起泄漏Web 站点用户的敏感信息。
从上面介绍HttpOnly就知道,开启他可以阻止客户端脚本访问Cookie。包含在HTTP-only cookie中的任何信息暴露给黑客或者恶意网站的几率将会大大降低。
如何利用的呢?
大致原理就是找到XSS漏洞之后,给用户发送被你植入了代码之后网址,比如生成二维码或者链接,用户点击之后,会执行你植入的代码,比如获取用户的Cookie,如果这个脚本命令浏览器向攻击者的计算机发回一个cookie,即使这个cookie包含有敏感信息,您的浏览器也会老老实实地执行。