[web] Session 原理与安全
1 Session
本文适合有 web 开发
与 用户会话管理
基础的开发者阅读。
-
Web三大概念:cookie,session,application
-
Session(会话):记录一系列状态
用户登录
用户登录后的操作
-
Session与cookie功能效果相同。Session与Cookie的区别在于Session是记录在服务端的,而Cookie是记录在客户端的。
-
解释session:
当用户访问服务器某个网页时,服务应用程序会在服务器端的内存里开辟一块内存,这块内存就叫做session,而这个内存是跟浏览器关联在一起的。
这个浏览器指的是浏览器窗口,或是浏览器的子窗口
意思就是,只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。
而另外一个浏览器也需要记录session的话,就会再启一个属于自己的session
- 原理:
HTTP协议是非连接性的,取完当前浏览器的内容,然后关闭浏览器后,链接就断开了,而没有任何机制去记录取出后的信息。
而当需要访问同一个网站的另外一个页面时(就好比如在第一个页面选择购买的商品后,跳转到第二个页面去进行付款)这个时候取出来的信息,就读不出来了。
所以,必须要有一种机制让页面知道原理页面的session内容。
1.1 为什么会有SESSION机制?
因为HTTP协议是无状态的,而SESSION是用于维持用户访问状态,区分访问者的机制。
比如,在没有SESSION机制的前提下,想让服务器完成每个访问者的个性化定制是不可能是事情.
你可能会想到使用IP和UA等信息区分客户,但是在局域网通过网关连接广域网的时候就不能起作用,而且一旦涉及到登录等敏感操作的时候需要更加稳定,难以伪造的用户区分机制。这样就有了SESSION.
1.2 用户会话隔离:如何知道浏览器和这个服务器中的session是一一对应的呢?又如何保证不会去访问其它的session呢?
- 原理解答:
- 当用户访问一个页面的时候给浏览器创建一个独一无二的号码(
SessionId
),也给同时创建的session赋予同样的号码;- 这样就可在打开同一个网站的第2/3/.../n个页面时获取到第1个页面中session保留下来的对应信息
(理解:当访问第二个页面时将号码同时传递到第二个页面。找到对应的session
- 这个号码也叫
sessionID
,session的ID号码,session的独一无二号码。
1.3 Session传递方式:Session在浏览器与服务器间的传递方式?
1.3.1 方式1:通过cookies来实现
把session id 放在cookie里面
为什么是使用cookies存放session id呢?
因为cookie有临时的,也有定时的。
临时的就是当前浏览器什么时候关掉即消失。也就是说,session本来就是当浏览器关闭即消失的,所以可以用临时的cookie存放。
保存在cookie里的sessionID一定不会重复,因为是独一无二的。
当允许浏览器使用cookie的时候,session就会依赖于cookies。
当浏览器不支持cookie后,就可以通过第二种方式获取session内存中的数据资源。
1.3.2 方式2:通过URL重写来实现
在客户端不支持cookie的情况下使用。为了以防万一,也可以同时使用。如果不支持cookie,必须自己编程使用URL重写的方式实现。
URL重写功能其实就是对URL进行一个编码加密,客户端看不到真实的URL。
而且它还有一个重要的功能就是每次进行完URL重写之后都会将SessionID放置到URL中。
这样每次服务器在获取相应的Session时需要的SessionID可以从URL中找到。
而且URL还进行了加密,增强了安全性。
如何重写URL?通过response.encodeURL()方法
encodeURL()的两个作用
第1个作用:转码(说明:转中文的编码,或一些其他特殊的编码。
就好比如网页的链接中存在中文字符,就会转换成为一些百分号或者其他的符号代替。)
第2个作用:
URL后面加入sessionID,当不支持cookie的时候,可以使用encodeURL()方法,encodeUTL()后面跟上【sessionID】。
这样的话,在禁用cookie的浏览器中同时也可以使用session了。
但是需要自己编程,只要链接支持,想用session就必须加上encodeURL()。
提示:若想程序中永远支持session,那就必须加上encodeURL(),当别人禁用了cookie,一样可以使用session。
demo: 1
<a href="www.bug.com/admin/info.jsp">Info List</a>
改为: <a href="<%=response.encodeRedirectURL("www.bug.com/admin/info.jsp")%>">Info List</a>
demo: 2
response.sendRedirect("www.bug.com/admin/info.jsp")
改为: response.sendRedirct(response.encodeRedirectURL("www.bug.com/admin/info.jsp"))
简单的代码例子:
- 在没有使用encodeURL()方法前的代码
out.printl("<br><a href=" + "SessionInfoServlet" + ">refresh</a>");
out.println("</BODY></HTML>");
- 在使用encodeURL()方法后的代码
out.printl("<br><a href=" + response.encodeURL("SessionInfoServlet") + ">refresh</a>");
out.println("</BODY></HTML>");
看下图,当重写URL 的时候,每一次访问的时候都会将sessionID传过来,传过来了,就没有必要再在cookie里读了。
- 规则:
- 若浏览器支持cookie,则:创建session多大的时候,会被sessionID保存再cookie里。
只要允许cookie,session就不会改变,如果不允许使用cookie,每刷新一次浏览器就会换一个session(因为浏览器以为这是一个新的链接)
- 若不支持cookie,则:必须自己编程使用URL重写的方式实现session
- Session不像cookie一样拥有路径访问的问题,同一个application下的servlet/jsp都可以共享同一个session,前提下是同一个客户端窗口。
小结:会话固定攻击/会话修复攻击 : Cookies / URL重写并不安全
- 参考文献
- 风险 : 会话固定攻击/会话修复攻击
URL重写(为了维持与客户端的会话而完成的一种方法),这是一个安全问题。如下2种攻击方式均为:会话固定攻击/会话修复攻击
攻击方式1 : 基于黑客预设的、且有效的sessionId,并骗取用户信息重新登录,以窃取目标用户的信息
攻击原理:利用程序漏洞(登录时没有清理会话信息)————即: 登录后的
sessionId
和登录前的sessionId
没有变化
很多WEB开发语言为了防止浏览器禁止了cookie而无法识别用户,允许在URL中携带sessionid,这样虽然方便,但却有可能引起钓鱼的安全漏洞。
下图是从测试组发来的安全报告中剪出来的,图有些小问题,本来想重画1个,在visio中没找到合适的图。所以只能用别人的图了。
让我们对上图的步骤进行详细说明:
1. 黑客用自己的帐号登录,假设登录页面是:http://www.abc.com/login.jsp
2. 服务器返回登录成功。
3. 黑客从cookie中查看自己的sessionid,比如是1234
4. 黑客把带自己sessionid的地址发送给一般用户。http://www.abc.com/login.jsp;jsessionid=1234
不同的语言带sessionid的方式不一样,这里是jsp的方式)
5. 用户在黑客给的地址中用自己的帐号进行登录,登录成功。
这个时候用户登录的信息就会覆盖黑客之前的登录信息,且2个人用的是同1个sessionid
6. 黑客刷新页面,看到的账户信息就是用户的信息了,而不是之前黑客自己帐号的信息。
【攻击的注意事项】
1. 要注意你使用的语言是如何在URL中带sessionid。(我测试的时候开始在URL中使用大写的jsessionid,导致一直不起效)
2. 要http://www.abc.com/login.jsp;jsessionid=1234页面登录表单的action也带上了jsessionid,不然也没用。
对于这个问题你可能觉得如果login.jsp表单的action是写死,而不是读取当前URL的,
可能就不会出现这个钓鱼问题。这只能防住1个方向。
黑客可以做1个和login.jsp一模一样的页面(比如http://www.abc1.com/login.jsp),
然后,把这个地址发个客户,而这个地址中的表单这样写就可以:
<form action="http://www.abc.com/login.jsp;jsessionid=1234" ....
攻击方式2 : 先窃取用户的SessionId,再使用固定的SessionId去访问目标系统
如果您快速记下jsessionid
的值,无论是从其他人错误地公开复制过的URL,还是公开发布的一些HTTP调试工具(Firebug)的屏幕截图来显示请求/响应标头,
并且有问题的网站维护用户通过登录,只需将jsessionid Cookie
附加到URL
或请求标头
,就可在同一用户下登录。
很快,因为这些会话在闲置30分钟后默认过期。
这也被称为session fixation攻击
(会话固定攻击/会话修复攻击)。
- 解决方法
攻击方式1:
step1 在登录时,将原来的session作废(session.invalidate()),
step2 生成新的session,并保存用户信息至新Session中。
在 Spring Security
中,【防御会话固定攻击】很简单,只需要下面配置即可:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/signIn")
.and()
.cors()
.and().sessionManagement().sessionFixation().migrateSession()
.sessionRegistry(sessionRegistry);
}
}
Spring Security
默认已经开启了该配置,主要利用SessionManager
这个配置其来完成的。
- none: 该策略对会话不做任何变动,登录之后会沿用旧的session;
- newSession: 用户登录后会创建一个新的session;
- migrateSession: 默认策略,用户登录后创建一个新的session,并将旧session中的数据复制过来;
默认的防御策略是 migrateSession ,在用户匿名访问的时候是一个 sessionid,当用户成功登录之后,又是另外一个 sessionid,这样就可有效避免会话固定攻击。
- changeSessionId: 表示 session 不变,不会创建新的session,但是会修改 sessionid,内部使用由Servlet容器提供的会话固定保护。
Spring Security
之所以可以实现上述防御效果,主要是从以下三个方面来完成:
- 方法1:利用
StrictHttpFirewall
防火墙,如果发现请求地址中带有 “;
”,则该请求会被直接拒绝;这样可以阻止请求URL带上SessionID实质上,这等于禁用【URL重写方案】
- 方法2:响应的
Set-Cookie
字段中有HttpOnly
属性,这种方式会避免通过XSS 攻击
来获取 Cookie 中的会话信息,进而达成会话固定攻击。
设置HttpOnly。通过设置Cookie的HttpOnly为true,可以防止客户端脚本访问这个Cookie,从而有效的防止XSS攻击。(主要由【可靠的浏览器】实现本指令的特性)- 方法3:登录成功后,改变
sessionId
。既然问题是由于 sessionid 不变导致的,那我们就让 sessionid 变一下,利用Spring Security
提供的防御会话固定攻击的策略即可实现。
攻击方式2:(并不能完全解决session被攻击者窃取,并置入伪造请求的header or url中)
您可以完全禁用URL重写
,以便jsessionid
不会出现在URL中。
但是你仍然对会话修复攻击很敏感,一些黑客可能已经在公共网络或某些木马/病毒中安装了HTTP流量嗅探器,或者甚至使用XSS来了解这些cookie。
要明确的是,这个安全问题并不特定于JSP
,PHP
,ASP
或任何通过基于cookie
的会话维护登录的网站,对此非常敏感。
要在登录方面确保安全,请让登录和登录流量通过HTTPS
而不是HTTP
,并仅使用Cookie HTTPS
(安全)。
- 补充说明
还有一些额外的会话劫持问题(它们适用于所有支持
无Cookie会话
的Web开发框架 - 不仅仅是JSP)。
但即使使用cookie,会话劫持也是可能的。
1.4 Session 常用API
-
isNew():是否是新的Session,一般在第一次访问的时候出现
-
getid():拿到session,获取ID
-
getCreationTime():当前session创建的时间
-
getLastAccessedTime():最近的一次访问这个session的时间。
-
getRrquestedSessionid: 跟随上个网页cookies或者URL传过来的session
-
isRequestedSessionIdFromCookie():是否通过Cookies传过来的
-
isRequestedSessionIdFromURL():是否通过重写URL传过来的
-
isRequestedSessionIdValid():是不是有效的sessionID
其中,下面的结果图对应上面的8个方法:
其对应的代码:
1.5 session的时限有效性
- 当一个网站的第1个窗口关掉了,而没有继续接着访问第2/3/.../n个页面,就没有使用到session。那么,session会在中断程序后立刻关闭session吗?
这个时候session就需要给它保留的时间,当最近一次访问的时候开始计时,每刷新一次重写开始计时。
当隔了这么久的时间,没有访问这个session后,对不起,要关闭这个session了。
session有过期时间,session什么时候过期,要看配置,
1.6 session的本质:当前用户登录会话存放在服务器端上对应一块内存
- session就是当前用户登录会话存放在服务器端上对应一块内存。
内存里面能放任何东西,只要是
K-V
对就可以了。
session里面的key
永远都是String类型
1.7 SESSION 安全
由于SessionID
就像一把钥匙,获取了这把钥匙,就能获取用户数据.
所以,SESSION的攻击就集中在如何获取SessionID上.基本上可以分为两种情况:
- 截获SessionID
截获SessionID只能作用于设计不良的网站上、或浏览器安全性存在巨大隐患的场景中.
- 诱导持有SessionID的用户执行特定操作
这是CSRF攻击的基础
值得注意的是CSRF攻击的预防,不能仅仅止步于对SESSION的管理上,而是要通过表单中插入Token等,只是CSRF攻击以SESSION机制作为其根本
基本上对于SESSION低成本的攻击都可以归类为这两种.
1.7.1 PHP中SESSION的安全性
以下是常见的几种攻击和防范方法:
- 关闭
session.use_trans_sid
session.use_trans_sid: 在没有开启cookie的网站上允许通过URL传递SessionID.
开启这项配置,攻击者可以通过迷惑用户使用带有SessionID的的URL访问目标网站,之后用户在此SessionID下留有的信息,对于攻击者都是可见的.
在关闭这项配置的时候也可以使用JavaScript读取用户cookie中的SessionID.
例如:
A网站存在上述缺陷,小明点击黑客提供的链接a.com/?ssid=aabbcc访问了网站A,并在该网站登录,然后退出.这时黑客在使用链接a.com/?ssid=aabbcc登录后发现正处于登录状态,然后进行非法操作.
如果a.com
网站关闭session.use_trans_sid
配置,这样小明就无法登录,因为禁用了cookie
。
这样虽然对用户体验很不友好,但是绝大多数成熟网站都是这样做的.
更进一步,应该开启session.use_strict_mode
防止类似aabbcc这种未初始化的SessionID被使用,但是黑客可以将aabbcc
改为a.com
正常生成的SessionID
虽然作用有限,但是强烈建议开启这个配置.
假设a.com关闭了session.use_trans_sid配置,小明也可以正常接收cookie,那么攻击还会发生吗?
- 开启
session.cookie_httponly
网站a.com强制要求用户使用cookie来接收SessionID.
这时黑客升级了自己的攻击方案:
在a.com通过XSS,在小明经常访问的页面评论区注入了JavaScript脚本用来直接获取小明浏览器中的cookie数据.
虽然攻击难度加大,但是小明会在毫不知情的情况下暴露SessionID.
网站a.com只要对用户的评论信息做转义就可以有效的防止XSS注入.
这不属于SESSION安全的范畴,但是的确可以通过这种方法保护SessionID.
最主要的是开启session.cookie_httponly
,杜绝使用JavaScript
读取cookie
.
但是这个设置有个前提,就是用户使用【高质量的浏览器】,因为这个设置只能起到通知浏览器的作用,具体逻辑是由浏览器实现的.
- 在表单中使用
Token
或【验证码】
网站
a.com
做了XSS防范
,并且开启了session.cookie_httponly
.
黑客继续升级自己的攻击方案
在自己的网站上使用JavaScript恶意向目标网站a.com提交表单,
一旦小明开始向a.com提交表单, a.com就会向浏览器索要SessionID,
小明的浏览器就自动将SessionID传给a.com,
根据表单的含义,黑客就可以控制小明浏览器在a.com上的行为.
这种方法就是常见CSRF的一种.
那么,a.com如何甄别提交表单的是小明还是黑客呢?
就是在表单中加入隐藏的一个Token,或使用验证码,
如果是黑客,这个Token是无法伪造的,且验证码攻破难度在现如今几乎是不可能的.
这样,a.com检测到的Token或【验证码】错误的情况下,不予操作.
这样一来,a.com在面对常规的攻击时就能很好的应对了.还有一些工作也要跟进,防患于未然.
Y 总结
Y.1 Session机制涉及的安全问题汇总
- CSRF(Cross-site request Forgery)攻击称为跨站请求伪造攻击
构造代码 → 伪装代码 → 发送给受害者 → 受害者打开 → 受害者执行了恶意代码 → 攻击完成
- XSS攻击
构造代码 → 伪装代码 → 发送给受害者 → 受害者打开 → 攻击者获取受害者的Cookie → 攻击者使用受害者的Cookie去干坏事 → 攻击完成
- session fixation 攻击(会话固定攻击)
Y.2 一言以蔽之,Session 机制,并不安全!
要同时防止如下情况,Session机制并办不到
- Session URL方案中【URL session Id被黑客骗取】
- 【SessionId被泄露】 或 【黑客窃取SessionId成功】后,【基于真实有效的SessionId,伪造会话请求】
此问题,目前完全无法被解决,启用HTTPS协议
- 【网络传输】过程中【被抓包/窃取SessionId】,通过 URL or header
此问题,可通过
HTTPS
协议解决
Y.3 若实需使用,最佳实践是如何的?
- 仅用于在【浏览器与服务器】之间的交互、且确需【保存用户一段时间内的状态信息】,使用此方式;
- 【服务器与服务器】之间的【API调用】,则:可使用更安全的方式
- 对【安全性要求极高】的应用系统,则:不建议session方案;例如,所有操作均需使用【https】+【一次性Token(防会话固定攻击)】+【timestamp(防重放)】+【sign(防篡改)】的方案等
-
启用 HTTPS 协议
-
尽量关闭
URL重用
方案 / 尽量不要在URL中暴露SessionID
或
Cookie
+URL重用
并用;但推荐用户出于安全考虑,建议选用Cookie方案。
-
在登录时,将原来的session作废(session.invalidate()),生成新Session
-
服务器启用
Cookie httponly
此方案的可靠性还取决于浏览器;建议用户使用【可靠的浏览器】
-
Cookie的保留时长
不易过长,建议最多保留7-30天 -
其他预防动作:尽可能频繁的更换SessionID
比如: 在登录成功后为用户更换SessionID.
- 其他预防动作:敏感操作时二次验证
例如: 支付时要求输入密码或验证码.
- 其他预防动作:编写可靠的SESSION回收程序
PHP的SESSION在过期后不会被立即删除,而是要等待gc(垃圾清理)程序不定期清理.高可靠性的网站应该尽量自己实现gc,在SESSION过期后尽可能快的删除.
- 增加对浏览器特征(设备型号、User-Agent、IP所在地)的校验与识别
如果登录或API调用过程中出现登录异常,则:调用失败;废弃旧会话,让重新走登录流程
IP常用地的识别与异常地预警(邮件预警、软件内部预警、退出重登预警、短信预警等)
K 互联网大厂的最佳实践(网友观点)
- 参考文献
Cookie vs LocalStorage
对于Session来说,Cookie可以比LocalStorage更安全的保存Session Token。
Bilibili
- 为什么b站登录的sessionid 设置cookie时间为一年,不会有安全风险吗?
风险不大。
问题提问者之所以担心时间长有风险,估计是认为时间越长,被攻破的可能性越大,如果是用户密码的可能有这个特性,但是 cookie 不同于用户密码。
首先这个 cookie 标记了 httponly,通过前端 js 是无法读取的。
其次 cookie 的值是一个长随机字符串,通过盲猜是很难得到的。
并且各大网站为了安全性,还在cookie中增加了校验码机制,不符合后台特定规律的 cookie 值会被直接认定为非法 cookie。
对于 session 的安全性更应该关心的是跨站攻击之类的漏洞,但是这种漏洞被暴漏出来的根本原因是网站出现了 XSS,跟 session 生命周期长短是无关的。
具体可以参见我的博文:[Session的安全性](https://blog.whyun.com/posts/session/)
=======================
在刚刚发布的 Chrome 104 里,已把 Cookie 的最长存活期限制到了 400 天,之前的限制时间是个很巨大的数字。
这个改动是因为 HTTP 规范改了,HTTP 规范希望客户端根据自己的情况把 Cookie 存活期限制为 400 天或者更小的天数:
在 DevTools 的 Cookie 面板里,你之前看到这些 20 多年后才过期的 Cookie:
现在都会被裁成 2023 年 9 月份,也就是大概 13 个月。
因此,B 站这个 30 天是没事的。
X 参考文献
- Session原理 - CSDN
- session的根本原理及安全性 - CSDN
- SESSION机制及其安全管理 - CSDN
- Url重写 - 这是否会导致安全问题? - VoidCC
- Url Rewriting - Does that cause a security issue? - stackoverflow
- URL中允许携带sessionid带来的安全隐患 - CSDN
- 会话固定攻击(session fixation attack)及解决办法 - CSDN
- Cookies vs Localstorage for sessions – everything you need to know -
- 前端为什么还在用cookie? - Zhihu/Jerry学长
![QQ沟通交流群](https://blog-static.cnblogs.com/files/johnnyzen/cnblogs-qq-group-qrcode.gif?t=1679679148)
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!