Защита 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, то функция может выглядеть примерно так:
01.
function
callAjax(value1,value2) {
02.
var
target=$(
"#output"
);
03.
$.ajax({
04.
url:
"/ajaxcgi.php?arg1="
+value1+
"&arg2="
+value2,
05.
cache:
false
,
06.
beforeSend:
function
(xhr){
07.
target.html(
'<img src="/images/loading.gif">'
);
08.
xhr.setRequestHeader(
"Ajax-Timestamp"
, Date());
09.
},
10.
success:
function
(html) {
11.
target.html(html);
12.
}
13.
});
14.
}
Если jQuery мы не используем, то эта функция может выглядеть так:
01.
function
callAjax(value1,value2) {
02.
if
(window.XMLHttpRequest) {
03.
http =
new
XMLHttpRequest();
04.
}
else
if
(window.ActiveXObject) {
05.
http=
new
ActiveXObject(
"Msxml2.XMLHTTP"
);
06.
if
(! http) {
07.
http=
new
ActiveXObject(
"Microsoft.XMLHTTP"
);
08.
}
09.
}
10.
var
target=document.getElementById(
"output"
);
11.
var
nocache=Math.random();
12.
http.open(
'GET'
,
'/ajaxcgi.php?arg1="+value1+"&arg2="+value2+"&nocache ="'
+nocache');
13.
http.setRequestHeader(
"Ajax-Timestamp"
,Date())
14.
http.onreadystatechange =
function
() {
15.
if
(http.readyState == 4) {
16.
var
response = http.responseText;
17.
target.innerHTML = response;
18.
}
19.
else
if
(http.readyState == 2) {
20.
target.innerHTML =
'<img src="/images/loading.gif">'
;
21.
}
22.
}
23.
http.send(
null
);
24.
}
В обоих случаях мы сделали добавление к заголовкам запроса заголовок Ajax-Timestamp с текущим временем. Само собой, можно сделать какое-то более сложное значение.
Теперь обратимся к тёмной серверной стороне. Там мы уже подключили к Apache модуль mod_security2 и готовы добавить парочку правил SecRule. Открываем наш файл modsecurity_crs_15_customrules.conf (или любой другой конфиг, читаемый Апачем) и пишем:
01.
<Location ~ "/ajaxcgi.php">
02.
SecRuleInheritance On
03.
SecDefaultAction log,auditlog,deny,status:403,phase:2
04.
05.
# Запрещаем запросы с отсутствующим заголовком Ajax-Timestamp
06.
SecRule &REQUEST_HEADERS:Ajax-Timestamp "@eq 0"
07.
08.
#Запрещаем запросы, содержащие в этом заголовке не то, что мы ожидаем увидеть
09.
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+\)$"
10.
</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
хм.. а в чём защита?
Это запрос подменяется на раздва…