[说明: 本文为 http://www.smithfox.com/?e=32 原创, 转载请注明原文, 谢谢]
session的安全有两层意思:
1> 对最终客户来说, 不会因为session的share和造成混乱, 使end-user的信息泄漏以及其他安全问题
2> 对系统本身来说, 不会因为有hacker通过模拟sessionid和cookie来获取server信任进而进行恶意破坏
让我们逐层解释和展开问题:
1> 首先说明, 本文所有session专指Servlet HttpSession
2>
后台Session和Browser之间通过JSESSIONID来关联, JSESSIONID是Servlet标准,也是关键字,
Servle规定Browser用Mem Cookie来存储JSESSIONID, 注意并不是disk
cookie.一旦浏览器关闭后JSESSIONID就从PC消失, 更加安全.
3> Session也是一种很好的安全认证机制, 后台会标识session是不是已经被认证了, 如果是,就不会让用户再输入password. JSESSIONID可以被理解成为一个已经认证的key, 所以Session有安全问题.
4> Servlet容器不会构造相同的JSESSIONID, 客户端也很难预期JSESSIONID
5> HTTPS SSL等技术可以防止网络传输中有人恶意篡改JSESSIONID
6> 禁用Cookie的情况JSESSIONID就必须用URLRewrite. 我们可以通过对URL本身采用摘要算法,自认证来防止恶意篡改JSESSIONID.
比如: http://www.smithfox.com/abc?x=x&y=y&JSESSIONID=sdfdfsdfsdfsdfsdf&HWD=4FE23AD9892C
HWD的值是对整个URL的一个摘要算法, 如果有人改动了URL,这个HWD值就对不上了, 前提应该是这个算法别人不知道的.
7> 用户在自己的PC上肯定是可以看到当前的JSESSIONID的, 就象就你在自己的日记中看到了自己备忘的password一样, 这个不是技术安全问题.
8>
一台机器有多个自然人在使用, 出现的JSESSIONID欺骗, 应该没有技术办法可以解决.
只能是end-user自己小心,用完就关闭Browser. 我想这应该是在情理之中的事, 你是合租被盗应该是怪不得小区保安的,
也是需要自己平时提高防盗意识,呵呵.
9> 最近看到Tomcat7有个新的特性说是支持"防JSESSIONID劫持", 这个需要更多了解.
10> User和Session的关系.
Session是只认JSESSIONID不认人的, 包括自然人和系统Account. 这个问题比较搞.
我们用EndUser来表示自然人, User-Account表示系统帐号, 我们分析以下几种情况
10.1> 两个EndUser共用一个UserAccount并且在同一台PC, 这个混乱不是技术问题, 大家都可以理解
10.2> 两个EndUser分别使用不同的UserAccount在同一台PC, 这个是合租情况, 造成混乱不一定是技术问题
10.3> 某EndUser有两个UserAccount在同一台PC上, 我们需要考虑JSESSIONID在client端可以会混乱的问题.
因为不同的浏览器对于Cookie share的策略不同, 我们按程序设计必须按最容易出问题的Case想,比如IE8.
无论你是IE多窗口还是多TAB都是Share Cookie的. 所以总的指导方针是在client端做一些机制不允许用户这么做.
Google
的gmail就是这么做的, 你可以一台机器上用IE打开两个不同的gmail
account(两个窗口或是两个TAB都行),点新email或是其他需要和后台交互的行为时,gmail会退出一个,提示让你重新login并且
gmail account已经固定为后输入的User-Account.
具体在Client怎么防止两个Account还需高手指点.
10.4> 某EndUserA用自己的UserAccountA先已经login,再访问另一个UserAccountB的资源,而且该资源是需要访问密码的.
这种情况,往往因为后台Session设计的层次不清晰,造成了UserAccountA无需Password就直接访问到了UserAccountB的资源. 而且这个解决方案不能放在Client端, 因为访问UserAccountB的资源可能就是一个在Email中的Link,这个click动作客户端程序JavaScript是无法拦截的.
10.5> 总结来说:
11> 从第10>点可以看出, session和自然人或是UserAccount有着千丝万缕的联系,但不是所有的系统只有User这一层业务概念,所以我们需要理解后台的Session分划和设计好Session.Attribute层次.
我
们以一个假设业务模型为例说明问题: 这是一个只面向企业的图片共享web服务, 可以为多个公司(企业)提供服务, 用户必须属于某一个公司,
用户可以创建"图片分组", 图片分组可以设置为private(需要密码访问), 也可以直接公开. 图片分组是公司财产,
user可以创建"图片分组", 但是图片分组资源是归属公司, 同一公司内部的所有user可以直接访问图片分组(如果是公开),
也可以通过password(如果需要)访问图片分组.
这个业务模型中, 既有比User更高层的概念, 比如公司. 也有比User更底的概念, 比如用户的上传图片分组(imageGroup).
11.1> 不同的war包部署在tomcat,不同的war包之间的session是不会混乱的, 这个是由tomcat架构决定的. 另他的没有做过调查, 也有可能是Servlet标准, 有高手可以帮确认一下.
11.2> 多个公司又是运行在同一个tomcat application内, 怎么防止不同公司之间的session混乱
可以采用类似于防止重复提交的技术, 首先做一个优先级很高的filter, 每次reqeust和response都需要经过这个filter
在
所有login模块, 设置一个ticket cookie,写入当前company信息, 每个reqeust到达的第一步就是检测client
cookie和当前的URL信息, 以及session信息是否一致,
如果enduser是从一个company中click了一个其他company的link, 该filter就会发现ticket信息不一致,
然后就强制logout, 再次让user login. 并且每次response时做ticket的改动, 使client无法模拟
11.3> 怎么防止imageGroup信息混乱
Session本身是一个集合, 具体还是使用session.attribute["key"]
Session本身是User level的, 对于低于User level的信息, 需要好好规划attribute key
想像这样的case:
有两个imageGroup, 一个是public的, 一个是需要password的,
http://www.smithfox.com/companyIBM/public_images/
http://www.smithfox.com/companyIBM/password_images/
后台对imageGroup输入密码逻辑的伪代码如下:
boolean needpasswd = true;
if(session.getAttribute("NEED_PASSWORD") == null){
session.setAttribute("NEED_PASSWORD", needpasswd);
boolean needpasswd = 一个很耗时很复杂的验证函数(user, imageGroup, xxx);
} else{
needpasswd = session.getAttribute("NEED_PASSWORD");
}
if (needpasswd ){
showPasswordDialog() ;
}
看出什么问题没?
应该将上面的代码中的所有attribute key改成 "NEED_PASSWORD"+{imageGroupID}
否则用户只要先看了一个public后, 后面的所有图片分组都无需passwd就可以访问了, 即使这个imageGroup是private的.
13> 在用session之前一定需要检查是否真的一定需要session来解决, 比如只是想传value到JSP page, request.setAttribte()更适合
14> 比较小而多的业务对象,如果必须save在session一定要及时removeAttribute否则session用的内存会暴涨.
因为Session不会因为客户端不用了,就会自动清理,而是必须到SessionTimeOut才会,如果在SessionTimeOut期间内有很多的对象在Session内,就会有问题。所以需要即时清理已经不用的Session.Attribute
15> Cookie和Session一样, 同样需要注意 cookie key的层次问题,以及过期问题,domain, path问题等等