CSRF-защита в своем ajax-запросе в Drupal 7

Допустим у нас есть форма и мы хотим в её интерфейс добавить какой-нибудь ajax-запрос к серверу. И Form API с его #ajax нам не поможет, потому что запрос происходит в каком-нибудь событии из сторонней библиотеки. В общем, надо сделать сервис и обратиться к нему через $.ajax(), или $.getJSON().

При этом, конечно, хотелось бы защитить сервис от CSRF. Ну и вообще, чтобы посторонние люди или боты не долбились по адресу сервиса без разрешения. Если бы запрос происходил через Form API, Друпал сделал бы всё за нас. Но и сами мы тоже можем, и на странице с формой для этого уже всё, что нужно — есть.

В Drupal 7 каждая форма защищена парой скрытых значений в $form['form_id'] и $form['form_token'], соответственно, мы можем их подобрать и использовать при отправке на сервер. Для простоты, сделаем эти значения частью пути к сервису.

var form = $('#my-custom-form');
var form_id = form.find('input[name="form_id"]').val();
var form_token = form.find('input[name="form_token"]').val();
var url = '/my-custom-service/' + form_id + '/' + form_token;
$.getJSON(url, function (data) {
    alert('Все готово!');
});

В hook_menu() пункт, отвечающий за генерацию ответа, оформим так:

$items['my-custom-service/%/%'] = array(
    'page callback' => '_my_custom_service',
    'access callback' => '_my_custom_service_access',
    'access arguments' => array(1, 2),
    'type' => MENU_CALLBACK,
);

То есть мы передаем в access callback эти два значения как аргументы. Тогда в функции, управляющей доступом, мы можем легко проверить валидность токена:

function _my_custom_service_access($form_id, $form_token) {
  return drupal_valid_token($form_token, $form_id);
}

И всё.

Комментарии