防止PHP中不受信任的来源发生注销操作

Sco*_*ott 5 php security xss csrf csrf-protection

我在我的网站上有一个动作:

http://mysite.com/User/Logout
Run Code Online (Sandbox Code Playgroud)

这将使当前用户退出他/她的会话.由于这是一个简单的GET请求,恶意用户可以创建指向此页面的链接,甚至可以将此链接放在图像的src属性中,这会强制用户注销.我仍然希望保持注销链接的简单性而不必走得太远,但同时我希望能够防止上述情况发生.

有任何想法吗?

Zed*_*Zed 5

如果您使用GET请求将人员记录下来(或者做比使用GET请求更重要的事情),那么您的网站将容易受到跨站点请求伪造攻击.您应该使用POST注销人员.GET仅用于幂等操作,请参阅RFC 2616第9.1节.

请记住,虽然在这种情况下使用GET不符合RFC,但并不是您需要更改以防止XSRF.有关优秀的解释,请参阅ircmaxell的回答.

  • 请注意,切换到POST不会让您免受CSRF(跨站点请求伪造)攻击.它会使它变得非常困难,但你必须使用随机数或随机令牌来防止它们一起出现.如果有人将一些JS发布到您的网站而不是点击链接,我会相当容易.特别是[FF不是100%支持Same-Origin](https://wiki.mozilla.org/Security/Origin)这很容易.令牌仍然会阻止此类攻击,所以不要只切换到POST,也使用令牌!因此,就本问题而言,这个答案是不完整的. (4认同)

irc*_*ell 5

那么,您可以采取一些选项来帮助抵御CSRF攻击:

  1. 使用表单和随机令牌.因此,不要使用"链接",而是将会话中设置的随机标记用于表单

    <form action="/User/logout" method="post">
        <submit name="logout" value="Logout" />
        <input type="hidden" name="token" value="<?php echo getSessionToken(); ?>" />
    </form>
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,POST最适合此类操作,但您可以将表单更改为GET而不会有太多麻烦.

    然后在PHP中,只需:

    if (getSessionToken(true) != $_POST['token']) {
        die('CSRF!');
    }
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,getSessionToken应该像这样工作:

    function getSessionToken($reset = false) {
        if (!isset($_SESSION['random_token'])) {
            $_SESSION['random_token'] = sha1(uniqid(mt_rand(), true));
        }
        $token = $_SESSION['random_token'];
        if ($reset) {
            unset($_SESSION['random_token']);
        }
        return $token;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    另请注意,每当您获取令牌以进行检查时,您应该将其重置为新的(这就是它的作用).这可以防止攻击者在提交时检测到令牌并重新提交值的重放攻击.

  2. 如果必须使用链接,则将令牌嵌入链接中.但请注意,这更容易受到攻击,因为用户可能会将链接复制并粘贴给其他人.只要它是一个自动重置令牌,多个选项卡就不会有太多问题.但要意识到这不是最佳的:

    <a href="/User/logout?token=<?php echo getSessionToken(); ?>">Logout</a>
    
    Run Code Online (Sandbox Code Playgroud)

    这绝对比什么都好.但我仍然建议使用该表格以获得最佳保护.

强烈建议阅读并遵循OWASP CSRF指南以防止CSRF.它会告诉你几乎所有你需要知道的,以及为什么......