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

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

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

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

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

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

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

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

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

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

 {!! csrf_field() !!} 

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

Вот некоторые отрывки из того, как я получил свой 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 (включая представления форм и просмотры списков lazyloading) не TokenMismatchException из-за TokenMismatchException .

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

Я думаю, что моя основная проблема заключалась в фрагменте jquery, выше которого предполагалось добавлять заголовок X-XSRF-TOKEN для каждого запроса ajax. Это не работало для меня в моем приложении jQuery Mobile (в частности, в моем плагине lazyloader ), пока я не добавил некоторые параметры для самого модуля. Я добавил новый селектор по умолчанию csrf (который в этом случае будет meta[name="_token"] ) и новый параметр по умолчанию csrfHeaderKey (который в этом случае будет X-XSRF-TOKEN ). В принципе, при инициализации плагина новое свойство _headers инициализируется маркером CSRF, если он 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))); } 

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

 $('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()}}"); } } }); 

Вам нужно пройти по заголовку 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 (Угловая делает это легко). В 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')); } }); 

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

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

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

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

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