Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用PHP正确添加跨站点请求伪造(CSRF)令牌_Php_Security_Session_Csrf - Fatal编程技术网

如何使用PHP正确添加跨站点请求伪造(CSRF)令牌

如何使用PHP正确添加跨站点请求伪造(CSRF)令牌,php,security,session,csrf,Php,Security,Session,Csrf,我正在尝试为我网站上的表单添加一些安全性。其中一个表单使用AJAX,另一个是简单的“联系我们”表单。我正在尝试添加CSRF令牌。我遇到的问题是,标记只在某些时候出现在HTML“值”中。其余时间,该值为空。以下是我在AJAX表单上使用的代码: PHP: HTML //... 变量$token在会话中时未从会话中检索 安全警告:md5(uniqid(rand(),TRUE))不是生成随机数的安全方法。有关更多信息和利用加密安全随机数生成器的解决方案,请参阅 看起来你的if需要一个else if (

我正在尝试为我网站上的表单添加一些安全性。其中一个表单使用AJAX,另一个是简单的“联系我们”表单。我正在尝试添加CSRF令牌。我遇到的问题是,标记只在某些时候出现在HTML“值”中。其余时间,该值为空。以下是我在AJAX表单上使用的代码:

PHP:

HTML


//...

变量
$token
在会话中时未从会话中检索

安全警告
md5(uniqid(rand(),TRUE))
不是生成随机数的安全方法。有关更多信息和利用加密安全随机数生成器的解决方案,请参阅

看起来你的if需要一个else

if (!isset($_SESSION['token'])) {
    $token = md5(uniqid(rand(), TRUE));
    $_SESSION['token'] = $token;
    $_SESSION['token_time'] = time();
}
else
{
    $token = $_SESSION['token'];
}

对于安全代码,请不要这样生成令牌:
$token=md5(uniqid(rand(),TRUE))

  • md5()
试试这个:

生成CSRF令牌 PHP7 旁注:其中之一是将
random_bytes()
random_int()
反向移植到PHP5项目中。它是麻省理工学院授权的,在Github和Composer上可以作为

PHP 5.3+(或使用ext-mcrypt) 验证CSRF令牌 不要只使用
=
甚至
==
,使用(仅限PHP5.6+,但可用于库的早期版本)


继续使用每表单令牌 通过使用,可以进一步限制令牌仅可用于特定表单。HMAC是一种特殊的键控哈希函数,即使使用较弱的哈希函数(如MD5),也可以安全使用。但是,我建议改用SHA-2系列哈希函数

首先,生成第二个令牌用作HMAC密钥,然后使用如下逻辑呈现它:

<input type="hidden" name="token" value="<?php
    echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />
在不知道
$\u会话['second\u token']
的情况下,为一个表单生成的令牌不能在另一个上下文中重用使用一个单独的令牌作为HMAC密钥,这一点很重要,而不是您刚刚在页面上放置的令牌。

奖励:混合方法+树枝集成 使用的任何人都可以通过将此筛选器添加到其细枝环境中,从简化的双重策略中获益:

$twigEnv->addFunction(
    new \Twig_SimpleFunction(
        'form_token',
        function($lock_to = null) {
            if (empty($_SESSION['token'])) {
                $_SESSION['token'] = bin2hex(random_bytes(32));
            }
            if (empty($_SESSION['token2'])) {
                $_SESSION['token2'] = random_bytes(32);
            }
            if (empty($lock_to)) {
                return $_SESSION['token'];
            }
            return hash_hmac('sha256', $lock_to, $_SESSION['token2']);
        }
    )
);
使用此细枝功能,您可以使用以下两种通用令牌:

<input type="hidden" name="token" value="{{ form_token() }}" />

或锁定的变体:

<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />

Twig只关注模板渲染;您仍然必须正确验证令牌。在我看来,细枝策略提供了更大的灵活性和简单性,同时保持了最大安全性的可能性


一次性使用CSRF令牌 如果您有一个安全性要求,即每个CSRF令牌只允许使用一次,那么最简单的策略是在每次成功验证后重新生成它。然而,这样做会使之前的每一个标记失效,因为这些标记与一次浏览多个选项卡的用户不太匹配

Paragon Initiative Enterprises为这些角落案例提供了一个解决方案。它只适用于每个表单一次使用令牌。当会话数据中存储了足够的令牌(默认配置:65535)时,它将首先循环出最旧的未使用令牌。

CSRF保护

CSRF使用类型

形式上

<form>
   @csrf
</form>
会议

use Illuminate\Http\Request;

Route::get('/token', function (Request $request) {
    $token = $request->session()->token();

    $token = csrf_token();

    // ...
});
中间件

 App\Providers\RouteServiceProvider

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'stripe/*',
        'http://example.com/foo/bar',
        'http://example.com/foo/*',
    ];
}
App\Providers\RouteServiceProvider

只是好奇,
token\u time
是用来做什么的?@zerkms我现在没有使用
token\u time
。我打算限制令牌有效的时间,但尚未完全实现代码。为了清楚起见,我已经从上面的问题中删除了它。@Ken:这样用户就可以在打开表单时获取案例,发布表单并获取无效令牌?(因为它已经失效)@zerkms:谢谢你,但我有点困惑。你能给我举个例子吗?@肯:当然。假设令牌在上午10:00到期。现在是上午9点59分。用户打开表单并获取令牌(该令牌仍然有效)。然后用户填写表单2分钟,然后发送表单。只要现在是上午10:01,令牌就被视为无效,因此用户会得到表单错误用于安全上下文。这是对这家伙问题的正确答案。另一个答案是300张以上的选票,它解决了OP没有问的一个问题。很好,但在用户提交表单后如何更改$token?在您的例子中,一个令牌用于用户会话。请仔细查看它是如何实现的。令牌是一次性使用的,但它存储了多个。@ScottArciszewski您认为如何从会话id生成一个消息摘要和一个秘密,然后将收到的CSRF令牌摘要与再次哈希会话id和我以前的秘密进行比较?我希望你明白我的意思。我有一个关于验证CSRF令牌的问题。I如果$_POST['token']为空,我们就不应该继续,因为发送此POST请求时没有标记,对吗?因为它将被回送到HTML表单中,并且您希望它是不可预测的,因此攻击者不能伪造它。您实际上是在这里实现质询-响应身份验证,而不是简单地说“是的,此表单是合法的”,因为攻击者可以欺骗它。
$twigEnv->addFunction(
    new \Twig_SimpleFunction(
        'form_token',
        function($lock_to = null) {
            if (empty($_SESSION['token'])) {
                $_SESSION['token'] = bin2hex(random_bytes(32));
            }
            if (empty($_SESSION['token2'])) {
                $_SESSION['token2'] = random_bytes(32);
            }
            if (empty($lock_to)) {
                return $_SESSION['token'];
            }
            return hash_hmac('sha256', $lock_to, $_SESSION['token2']);
        }
    )
);
<input type="hidden" name="token" value="{{ form_token() }}" />
<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />
<form>
   @csrf
</form>
<input type="hidden" name="token" value="{{ form_token() }}" />
<meta name="csrf-token" content="{{ csrf_token() }}">
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});
use Illuminate\Http\Request;

Route::get('/token', function (Request $request) {
    $token = $request->session()->token();

    $token = csrf_token();

    // ...
});
 App\Providers\RouteServiceProvider

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'stripe/*',
        'http://example.com/foo/bar',
        'http://example.com/foo/*',
    ];
}