防止会话劫持


70

如何防止多个客户端使用相同的会话ID?我之所以这样询问,是因为我想增加一层安全保护,以防止网站上的会话劫持。如果黑客以某种方式找出另一个用户的会话ID并使用该SID发出请求,那么我如何检测到服务器上有不同的客户端共享一个SID,然后拒绝劫持尝试?

编辑

经过深思熟虑后,我接受了Gumbo的回答,因为我意识到由于无状态HTTP协议的限制,我所要求的是不可能的。我忘了HTTP的最基本原理是什么,现在我想到这个问题似乎有点琐碎。

让我详细说明一下:

用户A登录example.com后,为他提供了一些随机会话ID,为简单起见,将其设为“ abc123”。该会话ID在客户端存储为cookie,并通过服务器端会话进行验证,以确保登录的用户在从一个网页转到另一个网页时仍保持登录状态。如果HTTP不是无状态的,则该cookie当然不需要存在。因此,如果用户B窃取用户A的SID,并在其计算机上创建一个值为'abc123'的cookie,则他本可以成功劫持用户A的会话,但是服务器根本无法合法地识别出用户B的会话。该请求与用户A的请求有任何不同,因此服务器没有理由拒绝任何请求。即使我们要列出服务器上已经处于活动状态的会话,并尝试查看某人是否正在访问已处于活动状态的会话,我们如何才能确定是另一个用户在非法访问该会话而不是同一用户谁已经使用会话ID登录,但只是尝试使用它发出另一个请求(即导航到另一个网页)。我们不能。检查用户代理?可以被欺骗-但是作为深度防御措施还是不错的。IP地址?可以出于正当理由进行更改-但我建议不要检查IP地址的前两个八位位组,而不是根本不检查IP地址,因为即使是出于完全正当理由而不断更改IP的数据计划网络用户通常只具有其IP更改的最后两个八位位组。

最后,无状态HTTP谴责我们永远无法完全保护我们的网站免受会话劫持,但是良好的做法(如Gumbo提供的做法)将足以防止大多数会话攻击。因此,尝试通过拒绝同一SID的多个请求来保护会话免遭劫持,这简直是荒谬的,并且会破坏会话的整个目的。


好!检查IP的前两个八位字节也无效。尽管使用相同的Internet服务的不同人员总体上具有不同的IP地址,但他们可能具有相同的前两个八位字节。
WatsMyName 2012年

前两个八位位组也可能合法更改-例如,在大型组织中,存在通过不同ISP的多个Internet网关。
shonky linux用户,2015年

Answers:


83

不幸的是,与真正的请求相反,没有有效的方法来明确识别来自攻击者的请求。因为大多数反措施检查的属性(例如IP地址或用户代理特性)要么不可靠(IP地址可能在多个请求中更改),要么很容易伪造(例如,User-Agent请求标头),从而产生不必要的误报(即,真实的用户交换IP地址)或否定的错误(即攻击者能够使用相同的User-Agent成功伪造请求)。

因此,防止会话劫持的最佳方法就是确保攻击者无法找到其他用户的会话ID。这意味着您应该设计您的应用程序及其会话管理,以使(1)攻击者无法通过使用足够的熵来猜测有效的会话ID,以及(2)攻击者没有其他方法可以通过已知的攻击获得有效的会话ID。 /漏洞,例如嗅探网络通信,跨站点脚本,通过Referer泄漏等。

也就是说,您应该:

除此之外,您还应该在某些会话状态更改(例如,登录后确认真实性或更改授权/特权)后使旧的ID(请参见session_regenerate_id函数)无效的同时重新生成会话ID,并且您还可以定期执行此操作以减少时间跨度成功进行会话劫持攻击。


5
我同意+1。我还要提到会话ID必须过期,并且/ dev / urandom应该用作entropy_file。(blog.ptsecurity.com/2012/08/not-so-random-numbers-take-two.html)。也是会议骑马,又名CSRF是一个问题。
菜鸟2012年

1
我不明白 如果所有会话信息都存储在服务器上,那么如何不可能检索存储在您自己的服务器上的数据?我已经看过PHP的会话处理程序,但是我认为它不能实现我想要的功能,也许我错了吗?
黑森

1
@Rook我知道Gumbo提出的要点。最重要的是,我只想了解PHP是否具有检索所有活动会话的机制,如果没有,为什么?从理论上讲,这似乎是可能的,因为PHP会话是在服务器端进行的,因此一定有充分的理由不使用这种机制,因为它将对网站的安全性产生巨大的好处。谢谢。
hesson 2012年

1
@AnanduMDas但是您需要解密加密的HTTP消息才能读取Cookie。
Gumbo 2014年

1
INI指令session.entropy_filesession.entropy_lengthsession.hash_function去除PHP 7.1的
Code4R7

4

我们可以做这样的事情吗?

将会话ID存储在数据库中。还要存储该会话ID的IP地址和HTTP_USER_AGENT。现在,当一个包含匹配会话ID的请求到达服务器时,检查脚本中来自哪个代理并ip。

可以通过为会话设置通用功能或类来使该基础工作,以便在处理每个请求之前对其进行验证。几乎不需要几微秒。但是,如果许多用户正在访问您的站点,并且您拥有庞大的会话数据库,那么这可能不是性能问题。但是,与其他方法(例如=>使用重新生成的会话)相比,肯定会非常安全。

在重新生成会话ID时,再次没有会话劫持的机会。

假定复制了用户的会话ID,并且该用户一段时间未工作或处于活动状态,并且未向具有旧会话ID的服务器发出请求以请求重新生成新会话ID。然后,如果会话ID被劫持,黑客将使用该会话ID并向具有该ID的服务器发出请求,然后服务器将使用重新生成的会话ID进行响应,以便黑客可以继续使用服务。实际用户将不再能够操作,因为他不知道重新生成的ID是什么以及在请求中将传递什么请求会话ID。完全消失了。

如果我在某个地方不对,请纠正我。


3

会话劫持是一个严重的威胁,它必须通过对涉及事务的高级应用程序使用安全套接字层或使用简单的技术(例如使用cookie,会话超时和重新生成ID等)来进行处理,如上所述。
互联网诞生时,HTTP通信被设计为无状态的。也就是说,两个实体之间的连接仅在将请求发送到服务器所需的短暂时间内存在,并且结果响应被传递回客户端。黑客遵循以下几种方法劫持会话

  • 网络窃听
  • 不知情的曝光
  • 转发,代理和网络钓鱼
  • 反向代理

始终建议SSL安全套接字层在脚本开始时也
使用cookie遵循ini_set()指令,以覆盖php.ini中的所有全局设置:

ini_set( 'session.use_only_cookies', TRUE );                
ini_set( 'session.use_trans_sid', FALSE );

使用会话超时和会话重新生成ID

<?php
// regenerate session on successful login
if ( !empty( $_POST['password'] ) && $_POST['password'] === $password )         
{       
 // if authenticated, generate a new random session ID
  session_regenerate_id();

 // set session to authenticated
  $_SESSION['auth'] = TRUE;

 // redirect to make the new session ID live

 header( 'Location: ' . $_SERVER['SCRIPT_NAME'] );
}                   
// take some action
?>

2

有许多针对会话劫持的标准防御措施。其中之一是将每个会话匹配到一个IP地址。

其他方案可能使用从以下位置生成的HMAC:

  • 客户端IP的网络地址
  • 客户端发送的用户代理标头
  • SID
  • 存储在服务器上的密钥

仅在用户位于公共代理之后的情况下才使用IP的网络地址,在这种情况下,其IP地址可以随每个请求而更改,但是网络地址保持不变。

当然,为了真正安全起见,您真的应该对所有请求强制使用SSL,以使SID在一开始就不会被潜在的攻击者拦截。但并非所有站点都这样做(:: cough :: Stack Overflow :: cough ::)


4
ip地址不切实际,检查用户代理是否是一个玩笑,尤其是如果它存储在cookie值中,那么攻击者就知道拥有会话令牌的UA是什么(但如果他知道cookie值,他就应该知道)。您没有提及Cookie标志。请阅读浓汤的答案。也不要构建自己的会话处理程序,PHP的会话处理程序比这更好。
rook 2012年

1
hmac不是加密方法。同样,如果使用XSS或OWASP A9违规窃取了会话ID,则攻击者还将拥有用户代理。
rook 2012年

1
hmac的意思是“哈希邮件验证码”,哈希!=加密,验证不同于保密,通常都需要两者。另外,我非常不喜欢您的会话处理程序。浓汤有正确答案。
菜鸟2012年

2
永远不要将哈希函数与加密函数混为一谈,这可能是发现非密码学家的最佳方法。如果您不相信我,请尝试发布到crypto.stackexchange.com。同样,您不能依靠攻击者控制的变量来强制执行访问控制。就像您建议使用一个Cookie变量说那样is_hacker=false
rook 2012年

1
欺骗hmac?你到底在说什么?这就是我正在谈论的内容:addons.mozilla.org/en-US/firefox/addon/user-agent-switcher 您没有提出安全系统的建议,因为您依赖的是始终由攻击者控制的变量攻击者。如果您具有会话ID,则您将始终具有不可预测的用户代理。因此,请删除此可怕的帖子,永远不要向任何人推荐该系统!
rook 2012年

1

在我看来,您可以在用户登录时将会话ID存储在数据库中,并在登录之前检查每个用户是否相同。删除用户注销时已存储在数据库中的相同会话ID。您可以轻松地找到每个用户的会话ID,否则我可以为您提供帮助。


2
将会话ID存储在数据库中会削弱会话。攻击者可以通过SQL注入和登录来窃取它,而不必破坏密码哈希。使用PHP的会话处理程序,不要自己动手。
菜鸟2012年

@Rook,我查看了PHP的会话处理程序,但不确定如何将其用作解决问题的方法。如果您知道如何实现PHP的会话处理程序以拒绝多个SID,请将其作为答案。
hesson 2012年

@hesson浓汤的答案很棒。您使用其他所有人使用的保护系统。此外,请确保您的系统没有XSS缺陷和owasp a9违规,毕竟这是最常见的两个导致会话危害的缺陷。

1
@ Rook,PHP的会话处理程序支持数据库存储。
雅科(Jacco)2012年

4
@Rook:如果您的站点中存在一个SQL注入漏洞,那么您已经遇到了严重的问题。解决方案不是将SID从数据库中移出(放置它的绝对合理的位置),而是为了确保SQL注入漏洞的安全-除非您建议安全站点根本不应该使用SQL数据库。
冒犯君主

1

可以通过在数据库中创建一个表(作为登录用户),然后在登录时使用用户名和其SID更新该表来完成一种简单的实现,这将防止其他用户以同一用户身份登录,现在注销时,只需运行一个简单的查询,删除数据库中登录的数据,这也可以一次跟踪您网站上的登录用户。


1
这是另一个问题的解决方案。这有助于防止单个用户从多个客户端登录。问题是如何防止多个人假装成为同一用户和客户。
Zeal

1

显然,当您在浏览器中设置会话cookie时,该cookie将在请求中发送。现在,当请求到来时,服务器将检查数据库中的会话ID并授予访问权限。为了防止仅存储代理和ip的重要性,以便在检查服务器之前确保将会话访问权限授予唯一的客户端,而不是授予可以劫持的唯一会话id。


0

我不太了解编码部分。所以我可以告诉你一种算法来做到这一点。如果用户从LAN网络中嗅探会话ID(提供的用户和攻击者位于同一LAN中),则设置诸如SSL之类的内容,或将会话cookie设置为安全和httpOnly均无效。

因此,您可以做的是,一旦用户成功登录到应用程序,则为Web应用程序的每个页面设置唯一令牌,并在服务器端对其进行跟踪。这样,如果有效用户发送了访问特定页面的请求,该页面的令牌也将被发送到服务器端。由于令牌对于特定会话的用户而言是唯一的,因此即使攻击者可以获取会话ID,他也无法劫持用户会话,因为他无法向服务器提供有效令牌。


0

@Anandu M Das:

我相信您可能要指的是在每个会话ID中使用会话令牌。该站点可以解释会话中令牌的使用:

https://blog.whitehatsec.com/tag/session-token/

尽管会话令牌很容易受到XSS攻击的危害,但这并不意味着永远不要使用它们。我的意思是面对现实,如果服务器上的安全漏洞可以破坏某些东西,这不是方法的错误,也不是引入此漏洞的程序员的错误(以强调Hesson和Rook的观点)。

如果您遵循正确的安全惯例和惯例并保护您的网站免受SQL注入,XSS的侵扰,并要求所有会话都通过HTTPS进行管理,则可以使用会话中存储的服务器端令牌轻松管理来自CSRF的潜在攻击,并在用户每次对其会话进行操作(例如提交$ _POST)时进行更新。另外,无论您认为会话如何编码,都不要将会话或其内容存储在url中。

当用户的安全至高无上(应该如此)时,会话令牌的使用将允许提供更好或更高级的功能,而不会损害他们的会话安全性。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.