В этой записи пойдет речь о том, как сделать безопасный и правильный XMLHttpRequest в Yii2 используя CSRF токен.
Если вы не знаете, что такое CSRF и что за ним стоит, то ниже вы можете прочитать краткое описание.
CSRF (Cross-Site Request Forgery) — это атака, с помощью которой хакер может выполнить массу различных действий от имени других, зарегистрированных посетителей.
behaviour() и XMLHttpRequest
Давайте представим, что у вас есть контроллер с action, который принимает только POST запрос (в Yii это делается с помощью behaviour()
метода).
Пример behaviour()
:
public function behaviors() { return [ ... 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'news' => ['post'] ], ], ]; }
В этом примере, я указываю, что action news
принимает только POST
запросы и если пользователь попытается отправить какой-либо другой, ему выдаст ошибку.
В случае с POST запросом, вам так же нужно отправить CSRF токен, который автоматически генерируется в Yii2. Об это я распишу далее в этом посте.
После того как у нас готов action и настроили какие запросы могут на него приходит, самое время сделать XMLHttpRequest с CSRF токеном.
Для примера, я написал callback функцию, которая работает на подобии Ajax в jQuery. Суть ее в том, чтобы она отправляет POST запрос по ссылке. Для корректной работы функции, нужно указать 3 аргумента.
- Первый — это токен
- Второй — это объект, внутри которого есть параметр url (у вас может быть что-то другое)
- Третий — это callback
function sendPostRequest(_csrf, news, callback) { var xmlhttp = new XMLHttpRequest(); var _postdata = 'title=' + news.title + '&_csrf=' + this.options._csrf; self = this; xmlhttp.open("POST", 'http://domain.ru/api/news', true); xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); xmlhttp.setRequestHeader('X-CSRF-Token', _csrf); xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xmlhttp.onload = function() { if(xmlhttp.readyState == XMLHttpRequest.DONE && xmlhttp.status == 200) { callback(xmlhttp.responseText); } else if (xmlhttp.status == 400) { throw Error('Ошибка: ' + xmlhttp.status); } else { throw Error('Ошибка, что-то пошло не так.'); } } xmlhttp.send(encodeURI(_postdata)); }
В _postdata
нужно вписать параметры, которые будут отправляться в action. В данном примере — это title
и _csrf
. Они могут быть приняты как Yii::$app->request->post('title')
или Yii::$app->request->post('_csrf')
, но токен нам точно не понадобиться, потому что он проверяется в Yii2 автоматически и если он отличается, то выдаст ошибку в доступе.
Так же не забудьте указать следующий header:
- X-CSRF-Token (сам токен)
Потому что без него, у вас так же будет выводить ошибку в доступе.
А теперь давайте покажу как правильно вызвать эту callback функцию (чтобы дождаться ответа и продолжить работу скрипта):
sendPostRequest('<?=Yii::$app->request->csrfToken?>', 'Заголовок новости', function(data) { console.log('Ответ получен:'); console.log(data); });
Вызываем ее как callback функцию и как только получает ответ от action, он сразу же возвращается обратно к нам. В data
будет находится ответ из экшена.
Если вам нужно получить CSRF токен с помощью JavaScript, вы можете использовать метод yii.getCsrfToken()
.
Источники
- https://yii2-cookbook.readthedocs.io/csrf/
- https://stackoverflow.com/a/27248827/3661795
- https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest