[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。
要明确的是,这个安全问题并不特定于JSPPHPASP或任何通过基于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的安全性

以下是常见的几种攻击和防范方法:

  1. 关闭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,那么攻击还会发生吗?

  1. 开启session.cookie_httponly

网站a.com强制要求用户使用cookie来接收SessionID.

这时黑客升级了自己的攻击方案:
  在a.com通过XSS,在小明经常访问的页面评论区注入了JavaScript脚本用来直接获取小明浏览器中的cookie数据.
    虽然攻击难度加大,但是小明会在毫不知情的情况下暴露SessionID.

网站a.com只要对用户的评论信息做转义就可以有效的防止XSS注入.
  这不属于SESSION安全的范畴,但是的确可以通过这种方法保护SessionID.

最主要的是开启session.cookie_httponly,杜绝使用JavaScript读取cookie.
但是这个设置有个前提,就是用户使用【高质量的浏览器】,因为这个设置只能起到通知浏览器的作用,具体逻辑是由浏览器实现的.

  1. 在表单中使用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 互联网大厂的最佳实践(网友观点)

  • 参考文献

对于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 天是没事的。

Session的安全性 - 白一梓

X 参考文献

posted @ 2023-09-09 13:07  千千寰宇  阅读(165)  评论(0编辑  收藏  举报