防止会话劫持的最佳方法是什么?

What is the best way to prevent session hijacking?

提问人:Adam 提问时间:8/23/2008 最后编辑:ChrisAdam 更新时间:3/11/2021 访问量:154919

问:

具体来说,这与使用客户端会话cookie来识别服务器上的会话有关。

对整个网站使用SSL/HTTPS加密的最佳答案,并且您有最好的保证,即中间没有人能够嗅探现有的客户端会话cookie?

也许对存储在会话 cookie 中的会话值本身使用某种加密是第二好的?

如果恶意用户对计算机具有物理访问权限,他们仍然可以查看文件系统以检索有效的会话 cookie 并使用它来劫持会话?

安全 会话 Cookie

评论


答:

0赞 Julio César 8/23/2008 #1

为了降低风险,您还可以将原始 IP 与会话相关联。这样,攻击者必须位于同一专用网络中才能使用会话。

检查 referer 标头也是一种选择,但这些标头更容易被欺骗。

评论

7赞 Olaf Kock 10/3/2008
不可以,您不能使用原始 IP,因为它可能会更改 - 通过动态 IP、在用户暂时断开连接时更改,或者通过代理场的(隐式或显式)使用。此外,来自同一ISP的会话劫持者可能会使用与合法用户相同的代理和IP。
1赞 Sembiance 6/30/2010
PHP-Nuke有一个很好的页面,关于他们的会话方法,他们详细讨论了如何将其与IP挂钩并不适用于所有ISP phpnuke.org/...
4赞 Blaise 3/28/2011
更改 IP 在移动互联网提供商中非常普遍。使用 Vodafone,我的 IP 会随着每个请求而变化。
1赞 Peter 3/27/2013
有点旧帖子,但要进一步说明这一点。如前所述,IP 是一个坏主意。如前所述,移动用户倾向于漫游 IP。过去,一些ISP也有漫游IP(如AOL),但是随着IPv4 IP的短缺,这种方法现在变得越来越流行。此类 ISP 包括 Plus net 和 BT
146赞 Josh Hinman 8/23/2008 #2

加密会话值将产生零效果。会话 cookie 已经是一个任意值,加密它只会生成另一个可以嗅探的任意值。

唯一真正的解决方案是 HTTPS。如果您不想在整个站点上执行SSL(也许您有性能问题),则可以仅使用SSL保护敏感区域。为此,首先确保您的登录页面是 HTTPS。当用户登录时,除了常规会话cookie外,还要设置一个安全cookie(这意味着浏览器将仅通过SSL链接传输它)。然后,当用户访问您的“敏感”区域之一时,将他们重定向到 HTTPS,并检查是否存在该安全 cookie。真正的用户会拥有它,会话劫持者不会。

编辑:这个答案最初写于2008年。现在是 2016 年,没有理由不在整个站点中使用 SSL。不再有纯文本 HTTP!

评论

31赞 Calimo 1/23/2012
HTTPS 将仅阻止嗅探。但是,如果你有一个XSS,或者会话ID可以很容易地被猜到,或者你容易受到会话固定的影响,或者你的会话ID存储很弱(SQL注入?),SSL将没有任何改善。
5赞 Pacerier 6/14/2012
@Josh 如果恶意用户对计算机具有物理访问权限,他们仍然可以查看文件系统以检索有效的会话 cookie 并使用它来劫持会话?
77赞 Josh Hinman 6/15/2012
如果恶意用户对文件系统具有物理访问权限,则无需劫持会话。
25赞 Pacerier 10/14/2012
@Josh。事实并非如此,有时用户对文件系统的物理访问时间有限。想象一下,一台笔记本电脑被一个冲向厕所的同事打开了,现在我需要做的就是去那台笔记本电脑,安装 EditThisCookie 插件,使用 EditThisCookie 导出功能在 plus.google.com 抓取他的 cookie,现在我有了他的帐户。所用时间:18 秒。
2赞 xorinzor 12/5/2012
我很确定谷歌会内置某种安全功能,因为这显然是您在谈论会话劫持时首先想到的事情
44赞 bsmcat 8/24/2008 #3

SSL仅有助于嗅探攻击。如果攻击者可以访问您的计算机,我假设他们也可以复制您的安全cookie。

至少,确保旧饼干在一段时间后失去价值。当 cookie 停止工作时,即使是成功的 hijaking 攻击也会被挫败。如果用户有一个多月前登录的会话的 cookie,请让他们重新输入密码。确保每当用户点击您站点的“注销”链接时,旧会话 UUID 都无法再次使用。

我不确定这个想法是否可行,但这里是: 在会话 cookie 中添加一个序列号,可能是这样的字符串:

SessionUUID、序列号、当前日期/时间

加密此字符串并将其用作会话 Cookie。定期更改序列号 - 也许当 cookie 已存在 5 分钟时,然后重新发布 cookie。如果您愿意,您甚至可以在每次页面查看时重新发布它。在服务器端,记录您为该会话发出的最后一个序列号。如果有人发送了序列号错误的 cookie,则意味着攻击者可能正在使用他们之前拦截的 cookie,因此使会话 UUID 失效并要求用户重新输入密码,然后重新发出新的 cookie。

请记住,您的用户可能拥有多台计算机,因此他们可能有多个活动会话。不要做一些事情,迫使他们每次在计算机之间切换时都再次登录。

评论

0赞 crush 2/16/2013
我知道这是一篇旧帖子,但只是想包括,如果攻击者在“序列号”分配的窗口内劫持会话,那么这不会影响他。
0赞 Johanneke 7/12/2013
@crush,但是攻击者会在分配的窗口之后被锁定,对吧?因此,至少攻击窗口很小。
3赞 elipoultorak 11/16/2015
唯一的问题是,如果用户离开您的网站 5 分钟,他们将不得不再次登录
2赞 mikiqex 10/7/2020
我在每个非 GET 请求上重新发出 cookie,在我需要一次发送多个 XHR 请求的情况下,它引起了麻烦。第一个请求奏效了,但其余的请求使用了现已过时的原始 cookie。我不想链接这些请求(不断更新 cookie)或使用一些“do_not_update_coookie”标志,所以我完全放弃了这个概念。
4赞 Kibbee 8/24/2008 #4

确保不要对会话 ID 使用递增整数。最好使用 GUID 或其他一些随机生成的长字符串。

9赞 Hubert 11/24/2008 #5

尝试 Liu、Kovacs、Huang 和 Gouda 在本文中描述的安全 Cookie 协议:

如文档所述:

一个安全的 在客户端和服务器之间运行的 Cookie 协议 需要提供以下四项服务:认证、保密、完整性和防重放。

至于部署的便利性:

在效率方面,我们的协议不涉及任何数据库 查找或公钥加密。在可部署性方面,我们的协议可以很容易地部署在现有的 Web 服务器上,并且不需要对 互联网 cookie 规范。

简而言之:它安全、轻巧,对我来说效果很好。

评论

9赞 Adam 8/8/2010
您的链接是协议的规范 - 您有指向实现的链接吗?那太好了 - 谢谢。
0赞 akostadinov 4/29/2022
不确定此协议有多大帮助,请参阅 security.stackexchange.com/a/20405/8618
-15赞 Nima 6/30/2011 #6

保护方式:

$ip=$_SERVER['REMOTE_ADDER'];
$_SESSEION['ip']=$ip;

评论

13赞 samayo 7/28/2013
我看不出你在这里做什么。
21赞 Nathan 7/4/2011 #7

你有没有考虑过读一本关于PHP安全的书?强烈推荐。

对于非SSL认证的站点,我使用以下方法取得了很大的成功。

  1. 禁止在同一帐户下进行多个会话,确保您不会仅通过 IP 地址进行检查。而是通过登录时生成的令牌进行检查,该令牌与数据库中的用户会话一起存储,以及 IP 地址、HTTP_USER_AGENT等

  2. 使用基于关系的超链接 生成链接(例如。http://example.com/secure.php?token=2349df98sdf98a9asdf8fas98df8 ) 该链接附加了一个 x-BYTE(首选大小)随机加盐的 MD5 字符串,在页面重定向时,随机生成的令牌对应于请求的页面。

    • 重新加载后,会进行多次检查。
    • 原始 IP 地址
    • HTTP_USER_AGENT
    • 会话令牌
    • 你明白了。
  3. 短生命周期会话身份验证 Cookie。 如上所述,包含安全字符串的 cookie(这是对会话有效性的直接引用之一)是一个好主意。使其每 x 分钟过期一次,重新颁发该令牌,并将会话与新数据重新同步。如果数据中有任何不匹配,请注销用户,或让他们重新验证其会话。

我绝不是这个主题的专家,我在这个特定主题上有一些经验,希望其中一些对那里的任何人有所帮助。

评论

4赞 Rikki 6/11/2012
有没有你会推荐的书?
1赞 matts1 3/17/2014
特别是考虑到 OP 没有具体说明任何关于 PHP 的内容,我想说最好只看一本通用的安全书籍(特别是因为不同语言之间的安全性仅在实现细节上有所不同,但概念保持不变)。
1赞 cowbert 1/24/2018
在2017年,即使是Facebook / Gmail等也允许在同一帐户下进行多个会话(尤其是使用移动设备),除非您讨厌您的用户,#1有点过时了。
12赞 Alexandru 11/1/2013 #8

没有办法 100% 防止会话劫持,但通过某种方法可以减少攻击者劫持会话的时间。

防止会话hijaking的方法:

1 - 始终使用带有SSL证书的会话;

2 - 仅发送会话 cookie,并将 httpOnly 设置为 true(阻止 JavaScript 访问会话 cookie)

2 - 在登录和注销时使用会话重新生成 ID(注意:不要在每个请求时使用会话重新生成,因为如果您有连续的 AJAX 请求,那么您有机会创建多个会话。

3 - 设置会话超时

4 - 将浏览器用户代理存储在 $_SESSION 变量中,并在每个请求中与 $_SERVER['HTTP_USER_AGENT'] 进行比较

5 - 设置令牌 cookie,并将该 cookie 的过期时间设置为 0(直到浏览器关闭)。 为每个请求重新生成 cookie 值。(对于 ajax 请求,不要重新生成令牌 cookie)。 前任:

    //set a token cookie if one not exist
    if(!isset($_COOKIE['user_token'])){
                    //generate a random string for cookie value
        $cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));

        //set a session variable with that random string
        $_SESSION['user_token'] = $cookie_token;
        //set cookie with rand value
        setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
    }

    //set a sesison variable with request of www.example.com
    if(!isset($_SESSION['request'])){
        $_SESSION['request'] = -1;
    }
    //increment $_SESSION['request'] with 1 for each request at www.example.com
    $_SESSION['request']++;

    //verify if $_SESSION['user_token'] it's equal with $_COOKIE['user_token'] only for $_SESSION['request'] > 0
    if($_SESSION['request'] > 0){

        // if it's equal then regenerete value of token cookie if not then destroy_session
        if($_SESSION['user_token'] === $_COOKIE['user_token']){
            $cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));

            $_SESSION['user_token'] = $cookie_token;

            setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
        }else{
            //code for session_destroy
        }

    }

            //prevent session hijaking with browser user agent
    if(!isset($_SESSION['user_agent'])){
        $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
    }

    if($_SESSION['user_agent'] != $_SERVER['HTTP_USER_AGENT']){
      die('session hijaking - user agent');
    }

注意:不要使用 Ajax 请求重新生成令牌 cookie 注意:上面的代码是一个示例。 注意:如果用户注销,则必须销毁cookie令牌以及会话

6 - 使用用户 IP 来防止会话 hijaking 不是一个好的建议,因为某些用户 IP 会随着每个请求而更改。影响有效用户

7 - 我个人将会话数据存储在数据库中,由您采用哪种方法决定

如果您发现我的方法有误,请纠正我。如果您有更多方法可以防止会话 hyjaking,请告诉我。

评论

0赞 Vishwanath Mishra 12/16/2014
嗨,我正在尝试防止会话劫持(ASP.NET)并考虑了您建议的上述所有步骤。它大致可以工作,但是当我使用浏览器的InPrivateBrowsing/隐身模式时,会话被劫持了。你能建议在sessionId字符串中添加任何其他东西吗?
21赞 theironyis 12/4/2013 #9
// Collect this information on every request
$aip = $_SERVER['REMOTE_ADDR'];
$bip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$agent = $_SERVER['HTTP_USER_AGENT'];
session_start();

// Do this each time the user successfully logs in.
$_SESSION['ident'] = hash("sha256", $aip . $bip . $agent);

// Do this every time the client makes a request to the server, after authenticating
$ident = hash("sha256", $aip . $bip . $agent);
if ($ident != $_SESSION['ident'])
{
    end_session();
    header("Location: login.php");
    // add some fancy pants GET/POST var headers for login.php, that lets you
    // know in the login page to notify the user of why they're being challenged
    // for login again, etc.
}

这样做的目的是捕获有关用户会话的“上下文”信息,这些信息在单个会话的生命周期内不应更改。用户不会同时在美国和中国使用计算机,对吧?因此,如果 IP 地址在同一会话中突然更改,这强烈暗示会话劫持尝试,则您可以通过结束会话并强制用户重新进行身份验证来保护会话。这挫败了黑客攻击的企图,攻击者也被迫登录而不是访问会话。通知用户尝试(ajax 它稍微增加一点),并且 vola,有点恼火+知情的用户,他们的会话/信息受到保护。

我们加入了用户代理和 X-FORWARDED-FOR,以尽最大努力为代理/网络后面的系统捕获会话的唯一性。您也许可以使用更多信息,然后随意发挥创意。

它不是 100%,但它非常有效。

您可以采取更多措施来保护会话,使它们过期,当用户离开网站并返回时,可能会强制他们再次登录。您可以通过捕获空白HTTP_REFERER(在 URL 栏中键入域)来检测用户离开和返回,或者检查HTTP_REFERER中的值是否等于您的域(用户单击外部/特制链接以访问您的网站)。

过期会话,不要让它们无限期地保持有效。

不要依赖 cookie,它们可能会被盗,它是会话劫持的攻击媒介之一。

评论

3赞 goofballLogic 8/20/2015
我以前遇到过这样的情况,某个(相当大的,但技术落后的)ISP会根据重新路由用户连接不时更改用户浏览器的IP。这使得REMOTE_ADDR检查为我们返回了假阴性。
0赞 Dan Bray 5/10/2017
为什么要费心创建哈希? 同样安全。$_SESSION['ident'] = $aip . $bip . $agent;
4赞 Kareem 10/30/2017
如果您有移动用户在旅途中使用您的网站并从一个接入点移动到另一个接入点,他们的 IP 将不断变化,并且他们将不断注销其帐户。
1赞 Bradley 12/15/2017
代理和VPN呢?任何使用 TOR 代理的人都将不断注销其帐户。事实上,每隔几分钟。
1赞 Bradley 12/15/2017
即使您忽略了这一点,IP 地址也可以被客户端操纵,因此这无论如何都不会对劝阻攻击者有太大作用。
1赞 davej 2/15/2014 #10

让我们考虑一下,在登录阶段,客户端和服务器可以就秘密盐值达成一致。此后,服务器在每次更新时都会提供一个计数值,并期望客户端使用 (secret salt + count) 的哈希值进行响应。潜在的劫持者没有任何办法获得这个秘密的盐值,因此无法生成下一个哈希值。

评论

3赞 Dcortez 6/24/2016
但是,您希望如何将这种盐存储在客户端,以免有人窃取它呢?
5赞 Hatch 8/17/2015 #11

有许多方法可以防止会话劫持,但是所有这些方法要么会降低用户满意度,要么不安全。

  • IP 和/或 X-FORWARDED-FOR 检查。这些工作,而且非常安全......但想象一下用户的痛苦。他们来到一个有WiFi的办公室,他们得到了新的IP地址,然后失去了会话。必须重新登录。

  • 用户代理检查。与上面相同,新版本的浏览器已发布,并且您丢失了一个会话。此外,这些真的很容易被“破解”。黑客发送虚假的 UA 字符串是微不足道的。

  • localStorage 令牌。登录时生成令牌,将其存储在浏览器存储中,并将其存储到加密的cookie(在服务器端加密)。这对用户没有副作用(localStorage 在浏览器升级后仍然存在)。它不那么安全 - 因为它只是通过默默无闻来确保安全。此外,您可以向 JS 添加一些逻辑(加密/解密)以进一步掩盖它。

  • Cookie 重新发布。这可能是正确的方法。诀窍是一次只允许一个客户端使用 cookie。因此,活跃用户将每小时或更短时间重新发布一次 cookie。如果发出新的cookie,则旧cookie将失效。黑客攻击仍然是可能的,但要困难得多 - 黑客或有效用户都将被拒绝访问。

3赞 Jzf 2/11/2018 #12

AFAIK 会话对象无法在客户端访问,因为它存储在 Web 服务器上。但是,会话 ID 存储为 Cookie,它允许 Web 服务器跟踪用户的会话。

为了防止使用会话 ID 进行会话劫持,您可以在会话对象中存储一个哈希字符串,该字符串使用两个属性(远程地址和远程端口)的组合进行,可以在请求对象内的 Web 服务器上访问该属性。这些属性将用户会话与用户登录的浏览器相关联。

如果用户在同一系统上从其他浏览器或隐身模式登录,则 IP 地址将保持不变,但端口将不同。因此,当访问应用程序时,Web 服务器将为用户分配不同的会话 ID。

下面是我通过将会话 ID 从一个会话复制到另一个会话来实现和测试的代码。它工作得很好。如果有漏洞,请告诉我您是如何模拟的。

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    HttpSession session = request.getSession();
    String sessionKey = (String) session.getAttribute("sessionkey");
    String remoteAddr = request.getRemoteAddr();
    int remotePort = request.getRemotePort();
    String sha256Hex = DigestUtils.sha256Hex(remoteAddr + remotePort);
    if (sessionKey == null || sessionKey.isEmpty()) {
        session.setAttribute("sessionkey", sha256Hex);
        // save mapping to memory to track which user attempted
        Application.userSessionMap.put(sha256Hex, remoteAddr + remotePort);
    } else if (!sha256Hex.equals(sessionKey)) {
        session.invalidate();
        response.getWriter().append(Application.userSessionMap.get(sessionKey));
        response.getWriter().append(" attempted to hijack session id ").append(request.getRequestedSessionId()); 
        response.getWriter().append("of user ").append(Application.userSessionMap.get(sha256Hex));
        return;
    }
    response.getWriter().append("Valid Session\n");
}

我使用 SHA-2 算法对值进行哈希处理,并使用 SHA-256 Hashing at baeldung 中给出的示例进行哈希处理

期待您的评论。

评论

0赞 Jzf 2/11/2018
@zaph对不起,我没有添加答案以获得代表。重点也不在于 SHA-2 加密。但事实上,我能够检测到另一个用户的会话 ID Cookie 在另一个用户的会话中的使用情况,即原始问题中的会话劫持。我测试了我的解决方案,发现它有效。但我不是这方面的专家,所以我希望社区检查我的答案是否足够好。也许你可以看到我的代码片段做了什么,并决定它是否回答了这个问题。谢谢!
0赞 Jzf 2/11/2018
@zaph 感谢您指出错误。我已经根据您的建议更新了我的答案。如果您认为答案有帮助,请删除您的反对票。
0赞 NetDiver 2/11/2021 #13

仅使用 SSL,而不是加密会话 ID 中的HTTP_USER_AGENT并在每个请求上对其进行验证,只需将HTTP_USER_AGENT字符串也存储在会话数据库中。

现在,您只有一个简单的基于服务器的字符串与 ENV'HTTP_USER_AGENT' 进行比较。

或者,您可以在字符串中添加某种变体,以便对浏览器版本更新更加可靠。 您可以拒绝某些HTTP_USER_AGENT ID。(空的,即) 不能完全解决问题,但它至少增加了一点复杂性。

另一种方法是使用更复杂的浏览器指纹识别技术,并将 theyse 值与HTTP_USER_AGENT相结合,并在单独的标头值中不时发送这些值。但是,您应该对会话 ID 本身中的数据进行加密。

但这会使它变得更加复杂,并提高每个请求解密的 CPU 使用率。

0赞 Danger Saints 3/11/2021 #14

如果 ISP 劫持证书验证,ISP 可能会发起中间人攻击。尤其是对于证书颁发机构受损的情况。

所以我相信你无法阻止ISP的会话劫持。尤其是当法律力量带着根据执法从 CA 获得的假证书时。

您将需要网络外部的东西来保护您的会话,例如一个时间键盘。这就是为什么一次性垫如此敏感并且只能由少数公司销售的原因。

小心,一次性垫子可能会被利用。选择您的一次性专业垫。

评论

0赞 Danger Saints 3/11/2021
关于OTP安全,请谷歌“crypto ag swiss”