Подтвердить что ты не робот

Laravel 5 CSRF глобальный токен скрытого поля для всех форм на странице

Недавно я перешел на Laravel 5, и теперь проверка CSRF находится на каждой отправке сообщений. Я думал об удалении его, но я хочу следовать лучшим практикам, поэтому я сохраню его таким образом.

С другой стороны, у меня проблемы с отправкой ajax-запросов. Моя страница имеет несколько форм, а некоторые представления даже не из форм, а просто для вызовов ajax. Моя идея состоит в том, чтобы на странице был введен один скрытый "токен" и прикрепить его к каждой отправке. Есть ли недостатки в том, что этот универсальный входной токен?

Также, как я могу вывести токен? Было бы хорошо просто создать скрытый ввод в нижнем колонтитуле страницы?

4b9b3361

Ответ 1

Я не вижу недостатков. Вы можете легко создать глобальное поле маркера в вашем файле макета:

<input type="hidden" name="_token" id="csrf-token" value="{{ Session::token() }}" />

Или, если вы используете построитель форм:

{!! Form::token() !!}

В jQuery вы можете использовать что-то вроде this для прикрепления маркера к каждому запросу.

Ответ 2

Существует helper, чтобы добавить формальные форматы внутри форм. Вы можете просто использовать

{!! csrf_field() !!}

внутри форм. Он добавит скрытый ввод и токен.

Ответ 3

Вот некоторые отрывки из того, как я получил свой CSRF, работающий для всех различных сценариев в моем приложении jQuery Mobile, который я недавно обновил, чтобы использовать Laravel 5:

Я добавил зашифрованный токен csrf в переменную, которая будет передана моим представлениям в моем основном базовом контроллере: app\Http\Controllers\MyController.php

$this->data['encrypted_csrf_token'] = Crypt::encrypt(csrf_token());

Затем я добавил метатег в свой заголовок главного заголовка: resources\views\partials\htmlHeader.blade.php

<meta name="_token" content="{!! $encrypted_csrf_token !!}"/>

Затем я также добавил этот фрагмент jquery, как предлагается на некоторых форумах:

    $(function () {
            $.ajaxSetup({
                    headers: {
                            'X-XSRF-TOKEN': $('meta[name="_token"]').attr('content')
                    }
            });
    });

Но ключ (по крайней мере, для моей настройки) заключался в добавлении проверки для cookie XSRF-TOKEN в моем обычном промежуточном программном обеспечении VerifyCsrfToken: app\Http\Middleware\VerifyCsrfToken.php:

    /**
     * Determine if the session and input CSRF tokens match.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function tokensMatch($request)
    {       
            $token = $request->session()->token();

            $header = $request->header('X-XSRF-TOKEN');

            $cookie = $request->cookie('XSRF-TOKEN');

            return StringUtils::equals($token, $request->input('_token')) ||
                   ($header && StringUtils::equals($token, $this->encrypter->decrypt($header))) ||
                    ($cookie && StringUtils::equals($token, $cookie));
    }

Прежде чем я добавил, что почти все мои POST-адреса AJAX (включая представления форм и списки просмотров) не срабатывали из-за TokenMismatchException.

EDIT: С другой стороны, я не уверен, насколько это важно, чтобы сравнить токен сеанса с тем, который был установлен в cookie (который был бы получен из токена сеанса в первую очередь?). Возможно, это просто обошло безопасность всего этого.

Я думаю, что моя основная проблема заключалась в фрагменте jquery выше, который должен был добавить заголовок X-XSRF-TOKEN для каждого запроса ajax. Это не работало для меня в моем приложении jQuery Mobile (в частности, в плагине lazyloader), пока я не добавлю некоторые опции для самого модуля. Я добавил новый селектор по умолчанию csrf (который в этом случае будет meta[name="_token"]) и новый параметр по умолчанию csrfHeaderKey (который в этом случае будет X-XSRF-TOKEN). В принципе, при инициализации плагина новое свойство _headers инициализируется маркером CSRF, если он локализуется селектором csrf (по умолчанию или определяется пользователем). Затем, в трех разных местах, где может быть запущен ajax POST (при сбросе переменных сеанса или при lazyloading listview), параметр headers в $.ajax устанавливается с тем, что находится в _headers.

Во всяком случае, поскольку X-XSRF-TOKEN, полученный на стороне сервера, поступает из зашифрованного meta_token, я думаю, что защита CSRF теперь работает так, как должна.

Теперь мой app\Http\Middleware\VerifyCsrfToken.php выглядит так (что в основном относится к реализации по умолчанию, предоставленной Laravel 5 - LOL):

    /**
     * Determine if the session and input CSRF tokens match.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function tokensMatch($request)
    {
            $token = $request->session()->token();

            $_token = $request->input('_token');

            $header = $request->header('X-XSRF-TOKEN');

            return StringUtils::equals($token, $_token) ||
                   ($header && StringUtils::equals($token, $this->encrypter->decrypt($header)));
    }

Ответ 4

В нижней части страницы вы можете использовать что-то вроде этого:

$('form').append('{{csrf_field()}}');

Это добавит скрытый ввод ко всем вашим forms:

<input type="hidden" name="_token" value="yIcHUzipr2Y2McGE3EUk5JwLOPjxrC3yEBetRtlV">

И для всех ваших запросов AJAX:

$.ajaxSetup({
    beforeSend: function (xhr, settings) {
        //////////// Only for your domain
        if (settings.url.indexOf(document.domain) >= 0) {
            xhr.setRequestHeader("X-CSRF-Token", "{{csrf_token()}}");
        }
    }
});

Ответ 5

Вам нужно пройти по заголовку X-XSRF-TOKEN, который содержит зашифрованную версию csrf-token.

Есть два способа сделать это, о котором я знаю. Вы можете зашифровать токен и передать его в представление:

$xsrfToken = app('Illuminate\Encryption\Encrypter')->encrypt(csrf_token());

return view('some.ajax.form.view')->with('xsrf_token', $xsrfToken);

Или вы можете захватить токен из файлов cookie с помощью JavaScript (Angular делает это проще). В vanilla JS вы можете сделать что-то вроде этого:

function getCookie(name) {
    var pattern = RegExp(name + "=.[^;]*")
    matched = document.cookie.match(pattern)
    if (matched) {
        var cookie = matched[0].split('=')
        return decodeURIComponent(cookie[1])
    }
    return false
}

В jQuery вы можете сделать что-то подобное для запроса ajax:

$.ajax({
    // your request
    //
    beforeSend: function(request) {
        return request.setRequestHeader('X-XSRF-TOKEN', getCookie('XSRF-TOKEN'));
    }
});

Ответ 6

Я думаю, что вы можете сделать что-то вроде этого (не проверено будет обновляться, если я получаю шанс)

$(document).on('submit', 'form', function(e)
      $(this).append('<input name="_token" value="{{{ Session::token() }}}">);
});

вы действительно можете захотеть сохранить токен в переменной, которую вы повторно заполняете по мере ее истечения.

Преимущество добавления его в submit - если вы добавляете элементы через ajax, я думаю, что он по-прежнему будет работать без необходимости добавлять что-либо еще.

РЕДАКТИРОВАТЬ: Вот отличная статья об использовании Rails UJS с Laravel (которая включает в себя эту функцию автоматического маркера CRSF): https://medium.com/@barryvdh/unobtrusive-javascript-with-jquery-ujs-and-laravel-e05f444d3439