Привет всем! Не так давно столкнулся с проблемой защиты веб-приложения написанного на Phalcon PHP Framework вместе с AngularJS. Проблема заключалась в том что на странице есть несколько форм, которые посылают AJAX-запросы на сервер. Как подружить два фреймворка в вопросах безопасности, централизованного решения я не нашёл, пришлось его собирать по кусочкам из разных источников. И в этой статье я бы хотел предложить всем кто столкнулся, или столкнётся с такой проблемой, готовое рабочее решение.
К сожалению источника я сейчас не помню, но не раз замечал что между тегов частенько в meta лежали токены на крупных сайтах. Если вы посмотрите документацию Phalcon, то увидите что генерация токена происходит в форме. Вот так по-умолчанию генерируется токен в форме:
А что делать если формы две? На форуме было решение, но оно было с использованием сторонней библиотеки, что в моём случае не было хорошим решением, поэтому ещё немного порыскав на форуме, я нашёл решение генерировать токен в meta-теге.
Прочитав документацию AngularJS по работе с токенами, там предлагается передавать токен в заголовке с именем X-XSRF-TOKEN, но увы в Phalcon нужно было писать отдельную библиотеку для обработки таких токенов. У меня нет на это времени, я ленивый, поэтому пришлось найти другое более просто решение.
Помимо простоты, оно ещё и гибкое, ибо абсолютно все AJAX запросы, отправленные с помощью функции $http защищены токеном, который Phalcon легко и удобно воспринимает стандартными средствами.
1. Если человек захочет посмотреть исходный код страницы (не через firebug, а в отдельном окне), то при загрузке с генерируется новый токен, и вернувшись на страницу ни один запрос не будет обработан. Возможно это и хорошо, нечего лезть в исходный код.
2. Если вдруг на странице не будет favicon, или же будет пустой background-image: url(""); то браузер запрашивая эти данные, с генерирует незаметно для нас новый токен. Я дня два или три потратил на то чтобы найти причину отказа токену, и меньше всего я мог подозревать блок с пустым background-image: url("");
3. А как защитить формы без AJAX, и без $http? Очень просто!
В контроллере добавляем новые скоупы:
А в вьюхе выводим их в скрытом поле:
Теперь все формы защищены, Phalcon доволен привычным ему данным, и не задействует лишних библиотек, а Angular без проблем цепляем ко всем $http токен.
Я далеко не специалист по безопасности, я не гуру PHP, я просто решаю задачи, которые возникают когда я занимаюсь любимым делом. Я не нашёл такого же удобного и понятного решения, именно поэтому мне захотелось им поделится. Это решение я использую в реальном проекте, пока не было замечено проблем. Скорее всего вы в комментариях укажите мне на ошибки, и я буду вам благодарен. С Phalcon и AngularJS я работаю с августа этого года, до этого я работал с CodeIgniter и jQuery, поэтому сильно не судите за такое решение задачи, если оно оказалось не таким крутым, как мне казалось.
Спасибо за внимание. Если вам интересно почитать про Phalcon, подписывайтесь, у меня есть ещё несколько полезных решений проблем при работе с ним.
Генерируем токен в meta-теге
К сожалению источника я сейчас не помню, но не раз замечал что между тегов частенько в meta лежали токены на крупных сайтах. Если вы посмотрите документацию Phalcon, то увидите что генерация токена происходит в форме. Вот так по-умолчанию генерируется токен в форме:
<input type="hidden" name="<?php echo $this->security->getTokenKey() ?>" value="<?php echo $this->security->getToken() ?>"/>
А что делать если формы две? На форуме было решение, но оно было с использованием сторонней библиотеки, что в моём случае не было хорошим решением, поэтому ещё немного порыскав на форуме, я нашёл решение генерировать токен в meta-теге.
<meta name="token" title="<?= $this->security->getTokenKey() ?>" content="<?= $this->security->getToken() ?>"/>
Крепим токен ко всем AJAX запросам
Прочитав документацию AngularJS по работе с токенами, там предлагается передавать токен в заголовке с именем X-XSRF-TOKEN, но увы в Phalcon нужно было писать отдельную библиотеку для обработки таких токенов. У меня нет на это времени, я ленивый, поэтому пришлось найти другое более просто решение.
var app = angular.module('selfmd', [], function ($httpProvider)
{
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
$httpProvider.defaults.transformRequest = [function (data) {
if (data === undefined) {
data = {};
}
var token = $('meta[name=token]');
data[token.attr('title')] = token.attr('content');
return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];
}
Помимо простоты, оно ещё и гибкое, ибо абсолютно все AJAX запросы, отправленные с помощью функции $http защищены токеном, который Phalcon легко и удобно воспринимает стандартными средствами.
if ($this->security->checkToken())
{
//Токен верный
}
Возможные проблемы и решения
1. Если человек захочет посмотреть исходный код страницы (не через firebug, а в отдельном окне), то при загрузке с генерируется новый токен, и вернувшись на страницу ни один запрос не будет обработан. Возможно это и хорошо, нечего лезть в исходный код.
2. Если вдруг на странице не будет favicon, или же будет пустой background-image: url(""); то браузер запрашивая эти данные, с генерирует незаметно для нас новый токен. Я дня два или три потратил на то чтобы найти причину отказа токену, и меньше всего я мог подозревать блок с пустым background-image: url("");
3. А как защитить формы без AJAX, и без $http? Очень просто!
В контроллере добавляем новые скоупы:
var token = $('meta[name=token]');
$scope.token_id = token.attr('title');
$scope.token_val = token.attr('content');
А в вьюхе выводим их в скрытом поле:
<input type="hidden" name="{{token_id}}" value="{{token_val}}"/>
Теперь все формы защищены, Phalcon доволен привычным ему данным, и не задействует лишних библиотек, а Angular без проблем цепляем ко всем $http токен.
Послесловие
Я далеко не специалист по безопасности, я не гуру PHP, я просто решаю задачи, которые возникают когда я занимаюсь любимым делом. Я не нашёл такого же удобного и понятного решения, именно поэтому мне захотелось им поделится. Это решение я использую в реальном проекте, пока не было замечено проблем. Скорее всего вы в комментариях укажите мне на ошибки, и я буду вам благодарен. С Phalcon и AngularJS я работаю с августа этого года, до этого я работал с CodeIgniter и jQuery, поэтому сильно не судите за такое решение задачи, если оно оказалось не таким крутым, как мне казалось.
Спасибо за внимание. Если вам интересно почитать про Phalcon, подписывайтесь, у меня есть ещё несколько полезных решений проблем при работе с ним.