11

我想知道这是否是一种设置令牌的安全方法,除非确实生成了令牌,否则我会生成一个令牌,并在整个应用程序和这些表单中使用它。每个会话一个令牌?

if (!isset($_SESSION['token'])) {
    $data['token'] = uniqid(rand(), true);
    session_regenerate_id();
    $_SESSION['token'] = $data['token'];
}

是否有必要清除提交表单上的令牌?还是坚持下去,即使我提交了表格?

4

6 回答 6

11

如果您不知道这些链接,应该可以帮助您理解一些场景,特别是会告诉您 DO 和 DONT。希望能帮助到你。

于 2012-12-09T18:53:38.093 回答
10

就我个人而言,我会为我想要显示的每个表单生成一个新令牌。如果你这样做,只要会话保持活动状态,某人只需要一个会话 cookie 来读取你的令牌并使用它。

在我的应用程序中,我为每个表单显示生成一个令牌,如下所示:

<?php
$token = uniqid(rand(), true);
$_SESSION['csrf_tokens'][$token] = true;

HTML

<form>
    <input type="hidden" name="token" value="<?php echo $token ?>" />
</form>

在表单验证中,我检查该令牌,如下所示:

if (isset($_SESSION['csrf_tokens'][$token]) && $_SESSION['csrf_tokens'][$token] === true) {
    unset($_SESSION['csrf_tokens'][$token]);
    // additional code here
}
于 2012-12-09T18:08:03.710 回答
2

而不是使用per-session token我更喜欢per-form/url token额外的安全性,有些人可能会认为这per-request token是最安全的,但会影响可用性。

我还认为将会话存储与令牌存储分开并使用类似Memcache. 当您需要使用多个应用程序服务器等时,这会更好。我也更喜欢它,因为我可以添加自定义expiration to the token 而不必影响整体session

这是一个典型的例子

HTML

<form method="POST" action="#">
    IP:<input type="text" name="IP" /> <input type="hidden" name="token"
        value="<?php echo Token::_instance()->generate(); ?>" /> <input
        type="Submit" value="Login" />
</form>

加工

$id = "id44499900";
Token::_instance()->initialise($id); // initialise with session ID , user ID or IP

try {

    Token::_instance()->authenticate();
    // Process your form
} catch ( TokenException $e ) {
    http_response_code(401); // send HTTP Error 401 Unauthorized
    die(sprintf("<h1>%s</h1><i>Thief Thief Thief</i>", $e->getMessage()));
}

使用的类

class Token {
    private $db;
    private $id;
    private static $_instance;

    function __construct() {
        $this->db = new Memcache();
        $this->db->connect("localhost");
    }

    public static function _instance() {
        self::$_instance === null and self::$_instance = new Token();
        return self::$_instance;
    }

    public function initialise($id) {
        $this->id = $id;
    }

    public function authenticate(array $source = null, $key = "token") {
        $source = $source !== null ? $source : $_POST;

        if (empty($this->id)) {
            throw new TokenException("Token not Initialised");
        }

        if (! empty($source)) {
            if (! isset($source[$key]))
                throw new TokenException("Missing Token");
            if (! $this->get($this->id . $source[$key])) {
                throw new TokenException("Invalid Token");
            }
        }
    }

    public function get($key) {
        return $this->db->get($key);
    }

    public function remove($key) {
        return $this->db->delete($key);
    }

    public function generate($time = 120) {
        $key = hash("sha512", mt_rand(0, mt_getrandmax()));
        $this->db->set($this->id . $key, 1, 0, $time);
        return $key;
    }
}
class TokenException extends InvalidArgumentException {
}

注意:请注意,该示例可能会影响“返回”按钮或刷新,因为令牌将在120sec 后自动删除,这可能会影响用户友好的功能

于 2012-12-31T13:30:19.853 回答
2

我想知道这是否是设置令牌的安全方法

这取决于您的网络应用程序需要有多安全。此行在密码学上不是安全的(正如 PHP 文档中针对 uniqid() 和 rand() 的警告):

uniqid(rand(), true);

如果令牌生成时间已知/确定并且 rand() 种子已知/确定,则攻击者确定/暴力破解这一点可能是可行的。但是,出于您的目的,它可能会很好,因为它仍然可以防止攻击者不知道令牌值的 CSRF 攻击。

每个会话一个令牌?

每个会话使用一个令牌可能适合您的目的。但是,请注意:

  1. 如果会话长达 n 分钟,则攻击者有 n 分钟的窗口来尝试确定或获取您的令牌值并执行 CSRF 攻击。而当按表格生成令牌或定期重新生成令牌时,这种风险会降低,因为它们的寿命不够长。
  2. 如果攻击者确定/获取令牌,每个会话使用单个令牌会暴露应用程序的所有功能(使用该令牌)进行攻击。而在每个表单中使用令牌会将攻击限制在单个表单中。

是否有必要清除提交表单上的令牌?还是坚持下去,即使我提交了表格?

这取决于您的应用程序对攻击者的目标价值有多大,以及攻击对您造成的破坏程度。您现有的措施使得执行 CSRF 攻击变得困难,但如果它具有很高的价值并且您有非常坚定的攻击者,那么您可能希望通过以下方式进一步降低 CSRF 的风险:

  1. 使用加密安全令牌来防止确定或暴力破解令牌值的风险。
  2. 定期重新生成令牌以减少令牌寿命,如果令牌被确定或获得,则减少攻击窗口。
  3. 在确定或获得令牌的情况下,为每个表单生成令牌以将攻击限制为单一表单。
于 2013-01-02T04:42:04.713 回答
1

我已经在另一个论坛上回答了类似的问题:here。希望这会有所帮助。它解释了 CSRF 预防的基本过程,并链接到 CSRF 框架的一些代码。

如果您想要更高的安全性,请在每个会话的每个请求后更改令牌。如果您想要更好的可用性,请在每个会话中保留一个令牌。

于 2013-01-14T14:09:14.363 回答
1

您能否参考以下网站,这可能会得到一些想法。

1.) https://docs.djangoproject.com/en/dev/ref/contrib/csrf/

2.) http://blog.whitehatsec.com/tag/session-token/

谢谢您的回复。

于 2013-01-05T11:13:09.487 回答