关于安全性:防止会话劫持的最佳方法是什么?

What is the best way to prevent session hijacking?

具体来说,这是关于使用客户端会话cookie标识服务器上的会话时的问题。

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

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

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


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

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

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


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

至少,确保老饼干在一段时间后失去价值。当cookie停止工作时,即使成功的hijaking攻击也会被阻止。如果用户的cookie来自一个多月前登录的会话,请让他们重新输入密码。确保每当用户单击站点的"注销"链接时,旧会话UUID就不能再次使用。

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

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

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

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


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

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

  • dis允许在同一个帐户下进行多个会话,请确保您不会仅通过IP地址检查。而是通过登录时生成的令牌进行检查,该令牌存储在数据库中的用户会话中,以及IP地址、HTTP用户代理等

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

    • 重新加载后,将执行多个检查。
    • 起始IP地址
    • HTTPU用户代理
    • 会话令牌
    • 你明白了。
  • 短寿命会话身份验证cookie。如上所述,一个包含安全字符串的cookie是一个好主意,它是会话有效性的直接引用之一。使其每x分钟过期一次,重新颁发该令牌,并将会话与新数据重新同步。如果数据中有任何不匹配的地方,要么注销用户,要么让他们重新验证会话。

  • 我决不是这个问题的专家,我在这个问题上有一些经验,希望其中的一些能帮助任何人。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 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-It向上一点),而vola,稍微恼火+知情用户,他们的会话/信息受到保护。

    我们引入了用户代理和x-forwarded-for,以便尽最大努力为代理/网络后面的系统捕获会话的唯一性。你可能会使用更多的信息,然后,感到自由的创造性。

    虽然不是100%,但效果相当好。

    当用户离开网站并返回时,您可以做更多的工作来保护会话,使其过期,可能会迫使他们重新登录。您可以通过捕获一个空白的HTTP引用(在URL栏中键入了域)来检测用户的离开和返回,或者检查HTTP引用中的值是否等于您的域(用户单击了一个外部/精心设计的链接以访问您的站点)。

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

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


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

    如文件所述:

    A secure
    cookie protocol that runs between a client and a server
    needs to provide the following four services: authentication, confidentiality, integrity and anti-replay.

    至于便于部署:

    In terms of efficiency, our protocol does not involve any database
    lookup or public key cryptography. In terms of deployability, our protocol can be easily deployed on an existing web server, and it does not require any change to
    the Internet cookie specication.

    简而言之:它是安全的,轻便的,对我来说非常有用。


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

    防止会话隐藏的方法:

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

    2-仅在httpOnly设置为true时发送会话cookie(防止javascript访问会话cookie)

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

    3-设置会话超时

    4-将浏览器用户代理存储在$_会话变量中,并在每次请求时与$_服务器['http_user_agent']进行比较。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
        //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,请告诉我。


    确保会话ID不使用递增整数。更好的方法是使用guid或其他随机生成的长字符串。


    有许多方法可以防止会话劫持,但所有方法都会降低用户满意度或不安全。

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

    • 用户代理检查。和上面一样,新版本的浏览器已退出,您将丢失一个会话。此外,这些很容易"黑客"。对于黑客来说,发送虚假的UA字符串是微不足道的。

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

    • 重新发行cookie。这可能是正确的方法。诀窍是一次只允许一个客户机使用一个cookie。因此,活动用户将每小时或更短时间重新发布cookie。如果发出新的cookie,旧的cookie将失效。黑客仍然是可能的,但要做得更困难——无论是黑客还是有效的用户都会被拒绝访问。


    afaik会话对象在客户端不可访问,因为它存储在Web服务器上。但是,会话ID存储为cookie,它允许Web服务器跟踪用户的会话。

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @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
    ");
    }

    我使用了sha-2算法来散列值,使用了在baeldung的sha-256散列中给出的示例

    期待您的评论。


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


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

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


    保护:

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