Защита web-приложения, использующего Ajax (Ajax Fingerprinting)
Date January 28th, 2010 Author Vitaly Agapov
На редком сайте сейчас не увидишь привычных уже крутящихся иконок асинхронной загрузки через Ajax. Однако новые технологии приносят и новые беды в виде новых уязвимостей и дыр в безопасности. Меж тем, недавно я узнал о таком понятии как “Ajax fingerprinting”. Почитал про него, попробовал в деле, узнал кое-какие тонкости и сейчас об этом пишу. В целом идея тривиальна: в браузере JavaScript’ом генерируем Ajax’овый GET-запрос с дополнительным полем в заголовке XHR (XMLHttpRequest), а на стороне сервера с помощью mod_security фильтруем всё Ajax-запросы, не содержащие этого поля или содержащие неверное значение. Не самая крепкая защита, но лучше, чем ничего. Не зря гласит народная мудрость: “Если у Вас паранойя, то это не значит, что за Вами не следят”.
Посмотрим, как это всё реализовать.
Допустим, у нас есть некоторый сайт, динамически обновляющийся с помощью Ajax. При этом Ajax-запросы идут на некоторый web-сценарий с названием ajaxcgi.php, то есть сайт подгружает ссылки вида http://domain.name/ajaxcgi.php?arg1=value1&arg2=value2. При этом мы хотим защитить этот сценарий от запусков со стороны посторонних.
Для начала обратимся к JS-функции, производящей фоновый Ajax-запрос к ajaxcgi.php. Если мы используем фрэймворк jQuery и вызов $.ajax, то функция может выглядеть примерно так:
function callAjax(value1,value2) { var target=$("#output"); $.ajax({ url: "/ajaxcgi.php?arg1="+value1+"&arg2="+value2, cache: false, beforeSend: function(xhr){ target.html('<img src="/images/loading.gif">'); xhr.setRequestHeader("Ajax-Timestamp", Date()); }, success: function(html) { target.html(html); } }); }
Если jQuery мы не используем, то эта функция может выглядеть так:
function callAjax(value1,value2) { if (window.XMLHttpRequest) { http = new XMLHttpRequest(); } else if (window.ActiveXObject) { http=new ActiveXObject("Msxml2.XMLHTTP"); if (! http) { http=new ActiveXObject("Microsoft.XMLHTTP"); } } var target=document.getElementById("output"); var nocache=Math.random(); http.open('GET', '/ajaxcgi.php?arg1="+value1+"&arg2="+value2+"&nocache ="'+nocache'); http.setRequestHeader("Ajax-Timestamp",Date()) http.onreadystatechange = function() { if (http.readyState == 4) { var response = http.responseText; target.innerHTML = response; } else if (http.readyState == 2) { target.innerHTML = '<img src="/images/loading.gif">'; } } http.send(null); }
В обоих случаях мы сделали добавление к заголовкам запроса заголовок Ajax-Timestamp с текущим временем. Само собой, можно сделать какое-то более сложное значение.
Теперь обратимся к тёмной серверной стороне. Там мы уже подключили к Apache модуль mod_security2 и готовы добавить парочку правил SecRule. Открываем наш файл modsecurity_crs_15_customrules.conf (или любой другой конфиг, читаемый Апачем) и пишем:
<Location ~ "/ajaxcgi.php"> SecRuleInheritance On SecDefaultAction log,auditlog,deny,status:403,phase:2 # Запрещаем запросы с отсутствующим заголовком Ajax-Timestamp SecRule &REQUEST_HEADERS:Ajax-Timestamp "@eq 0" #Запрещаем запросы, содержащие в этом заголовке не то, что мы ожидаем увидеть SecRule REQUEST_HEADERS:Ajax-Timestamp "!^\w{3}\s\w{3}\s\d{2}\s\d{4}\s\d{1,2}\:\d{2}\:\d{2}\sGMT(\+|-)\d{4}\s\(\w+\)$" </Location>
Перечитываем Апачем конфиги и пытаемся воспользоваться Ajax-вызовом. Работает! А если старой версией JS-функции?
Не работает! Получили отбой с ошибкой 403, как и хотели.
В целом идея такова, дальше можно подумать над её усовершенствованием и use cases.
Ссылка:
http://www.modsecurity.org/documentation/Ajax_Fingerprinting_and_Filtering_with_ModSecurity_2.0.pdf
Tags: Ajax, jQuery, ModSecurity
Category:
Apache, Security, Web-dev |
1 Comment »
2 December 2010 - 18:38
хм.. а в чём защита?
Это запрос подменяется на раздва…