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

时间:2008-08-22 17:02:27

标签: security session cookies

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

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

对于存储在会话cookie中的会话值本身,可能第二好使用某种加密技术?

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

14 个答案:

答案 0 :(得分:123)

加密会话值将不会产生任何影响。会话cookie已经是一个任意值,加密它只会生成另一个可以被嗅探的任意值。

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

编辑:这个答案最初写于2008年。现在是2016年,并且没有理由不在整个网站上安装SSL。没有更多的纯文本HTTP!

答案 1 :(得分:41)

SSL仅对嗅探攻击有帮助。如果攻击者可以访问您的计算机,我会认为他们也可以复制您的安全cookie。

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

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

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

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

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

答案 2 :(得分:21)

您是否考虑过阅读有关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. 我绝不是这方面的专家,我在这个特定主题上有一些经验,希望其中一些可以帮助那里的任何人。

答案 3 :(得分:20)

// 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地址在强烈意味着会话劫持尝试的同一会话中突然发生变化,那么您可以通过结束会话并强制用户重新进行身份验证来保护会话。这阻止了黑客攻击,攻击者也被迫登录而不是获得对会话的访问权限。通知用户尝试(稍微调整一下),以及挥动,稍微恼火+知情用户及其会话/信息受到保护。

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

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

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

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

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

答案 4 :(得分:9)

尝试由Liu,Kovacs,Huang和Gouda在this论文中描述的安全Cookie协议:

如文件中所述:

  

安全   在客户端和服务器之间运行的cookie协议   需要提供以下四种服务:身份验证,机密性,完整性和反重播。

至于易于部署:

  

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

简而言之:它既安全又轻巧,对我来说非常棒。

答案 5 :(得分:8)

没有办法阻止会话100%被阻止,但是通过一些方法我们可以减少攻击者隐藏会话的时间。

防止会话hijaking的方法:

1 - 始终使用ssl证书会话;

2 - 仅在httponly设置为true的情况下发送会话cookie(阻止javascript访问会话cookie)

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

3 - 设置会话超时

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

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

    //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防止会话被篡改是不好的方法,因为有些用户ip会随着每个请求而改变。这会影响有效的用户

7 - 我个人将会话数据存储在数据库中,这取决于您采用的方法

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

答案 6 :(得分:4)

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

答案 7 :(得分:3)

有很多方法可以创建针对会话劫持的保护,但是所有这些方法都会降低用户满意度或者不安全。

  • IP和/或X-FORWARDED-FOR检查。这些工作,并且相当安全......但想象用户的痛苦。他们来到办公室用WiFi,他们获得新的IP地址,并失去会话。必须再次登录。

  • 用户代理检查。与上面相同,浏览器的新版本已经用完,您将失去一个会话。此外,这些很容易“破解”。黑客发送伪造的UA字符串是微不足道的。

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

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

答案 8 :(得分:1)

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

答案 9 :(得分:1)

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

中给出的示例来散列值

期待您的意见。

答案 10 :(得分:0)

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

检查referer标头也可以是一个选项,但更容易被欺骗。

答案 11 :(得分:0)

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

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

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

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

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

答案 12 :(得分:0)

如果 ISP 劫持了证书验证,则 ISP 可能会发起中间人攻击。尤其是在证书授权机构遭到破坏的情况下。

所以我相信你无法阻止来自 ISP 的会话劫持。尤其是当法律力量带着从 CA 处获得的伪造证书时。

您将需要网络之外的东西来保护您的会话,例如一次性密码。这就是为什么一次性垫如此敏感,只有少数公司可以销售。

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

答案 13 :(得分:-14)

保护:

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