Архив ‘Безопасность’

Сервис отслеживания вредоносных JavaScript

Четверг, 12 февраля, 2009

Site Guard — система, позволяющая отслеживать появление на сайте вредоносных или подозрительных JavaScript. Работает следующим образом: запоминает скрипты на главной странице и в случае появления новых, содержащих подозрительные элементы, высылает уведомление на Email или SMS. Также высылает уведомления в случае недоступности сервера или окончания срока регистрации домена.

Статья о PHP-безопасности

Понедельник, 13 октября, 2008

Весьма полезная статья про типовые уязвимости PHP-скриптов: http://vingrad.ru/blogs/dominus/rokovyie-oshibki-php/ и вторая часть http://vingrad.ru/blogs/dominus/rokovyie-oshibki-php-v2/

Уязвимость через разрешенные теги или Зачем различать GET и POST на стороне сервера

Пятница, 10 октября, 2008

На сайтах, где пользователям разрешается отправлять свои сообщения/комментарии с использованием некоторого ограниченного количества HTML-тегов (в частности, <img src=”"> или <a href=”"> или их BBCode-аналоги ), возможна следующая уязвимость: злоумышленник вставляет в страницу ссылку, выполняющую какое-то нежелательное действие штатными средствами скрипта (например, удаление комментария предыдущего пользователя). Далее, если на эту страницу заходит модератор или администратор, его броузер заходит по этой ссылке, пытаясь скачать вставленную картинку, а вместо этого выполняет удаление сообщения, причем даже не заметив этого факта.

Бороться с подобной уязвимостью можно двумя способами: либо при выполнении опасных действий модераторов/администраторов проверять, что запрос на выполнение действия отправлен методом POST (который существенно сложнее сфальсифицировать без применения соц. инженерии), либо генерировать при входе администратора/модератора случайный ключ, который запоминать в сессии или cookie и приписывать в качестве параметра к запросу на выполнение опасного действия.

Второй способ является более надежным и универсальным и избавляет от ограничений, связанных с POST-запросом.

Пример такой ситуации. Пусть index.php?action=delete&post=111 — ссылка для удаления комментария. Если проверок на POST-метод или ключ не выполняется, то злоумылшенник может отправить сообщение с картинкой <img src=”index.php?action=delete&post=111″>, и если в тему зайдет модератор, сообщение 111 будет удалено. При добавлении проверки по ключу ссылка для модератора принимает вид index.php?action=delete&post=111&key=случайный_ключ, при этом ключ хранится в сессии модератора и никому, кроме него неизвестен. При попытке выполнить удаление без ключа (или с неправильным ключом) действие просто не будет выполнено, в результате чего атака злоумышленника провалится.

Простая защита от DoS-атак с помощью MySQL

Понедельник, 8 сентября, 2008

Организовать простую защиту от DoS-атак для сайтов, использующих PHP + MySQL, можно следующим образом. В MySQL создается таблица (будем называть ее ip_check) с полями lasttime, count, ip (все поля — типа INTEGER, причем ip — первичный ключ).

Сразу после подключения к базе данных выполняется проверка, есть ли для данного IP-адреса запись в таблице. Если записи нет, она создается, при этом в count пишется 1, в lasttime — текущее время. Если запись уже есть, то проверяется, когда она была сделана (поле lasttime), и если прошло менее определенного количества секунд, то поле count увеличивается на 1, в противном случае приравнивается единице, и производится обновление записи в таблице (записывается новый count и lasttime). Далее происходит проверка величины count, и если она превысила некоторое пороговое значение, выдается статус 500 (или 403 или 503), сообщение об ошибке, и выполнение скрипта завершается.

Таким образом, для выполнения проверки требуется всего одной таблица с предельно простой структурой и 2 запроса SQL. Также можно добавить еще одно поле status, в которое ставить 1, если IP-адрес забанен навсегда по каким-то причинам, и совместить проверку на DoS-атаки с проверкой на забаненные IP-адреса без добавления дополнительных запросов. При необходимости можно еще одним запросом добавить учет суммарной нагрузки за все время или нагрузки по подсетям.

Кроме того, перед завершением скрипта можно добавить проверку, сколько времени заняло его выполнение, и если оно превышает какую-то пороговую величину (например, 10 секунд), начислять этому IP-адресу “штрафные очки” (т.е. дополнительно увеличивать поле count).

В виде кода это можно представить следующим образом:

// $link -- соединение с БД, $ip_text -- IP-адрес
define('ALERT_TIME',300);
define('ALERT_COUNT',150);  

function check_dos($ip_text, $link)  {
  $ip=ip2long($ip_text);
  $res=mysql_query($link,'SELECT lasttime, count, status FROM ip_check WHERE ip="'.$ip.'"');
  if (mysql_num_rows($res)==0) { // нет такого IP
  $count=1;
  mysql_query($link,'INSERT INTO ip_check (lasttime,count,ip,status) VALUES ('.time().',1,"'.$ip.'",0)');
  }
else {
  $data=mysql_fetch_row($res);
  $status=$data[2];
  if ($status!=2 && ($data[0]<time()-ALERT_TIME || $data[1]<ALERT_COUNT)))   $count=1;
  elseif ($status!=2) { $count=$data[1]+1; if ($count>=ALERT_COUNT) $status=1; }
  if ($status!=2) {
    mysql_query($link,'UPDATE ip_check SET count='.$count,', status='.$status.', lasttime='.time().' WHERE ip="'.$ip.'"';
  }
  if ($status) {
    header($_SERVER['HTTP_PROTOCOL'].' 503 Temporary Unavailable');
    trigger_error('Превышено число допустимых запросов!',E_USER_ERROR);
  }
}

Кроме того, периодически следует очищать базу от старых записей (например, по cron). Кроме того, если ожидается большая интенсивность атак, возможно, имеет смысл заменить UPDATE-запрос на последовательность DELETE/INSERT, но в этом случае необходимо будет регулярно проводить оптимизацию таблицы.

Проверка имени файла на безопасность

Понедельник, 28 января, 2008

Если в работе скрипта производится работа с файлом, имя которого получается из параметров HTTP-запроса, то совершенно очевидно, что такое имя файла необходимо проверять на наличие небезопасных символов.

Таковыми в частности являются:

/ или \ в начале имени может позволить обратиться к произвольному месту в файловой системе (если не указан текущий путь, это позволит открывать файлы в любом месте файловой системы, а не только ниже текущего каталога);

.. (две точки) — позволит подняться в каталог более высокого уровня и открыть файл там;

~ (тильда) — если интерпретатор выполняется от реального пользователя, а не от nobody (т.е. PHP запускается как CGI), позволяет открыть файл в его родительском каталоге;

` (обратный апостроф) — в некоторых ситуациях может позволить выполнить внешнюю команду на сервере;

://(часть, отделяющая протокол в URL от доменного имени) — при разрешенном подключении внешних файлов позволит загрузить и выполнить произвольный PHP-код.

Кроме того, ряд символов может привести к созданию нечитаемых/неудаляемых файлов, вызывать ошибку при записи или ошибку в HTML при формировании ссылки, поэтому их тоже желательно фильтровать. К таким символам относятся: двоеточие (:), точка с запятой (;), запятая (,), кавычки (”), апостроф (’), амперсанд (&), символы больше и меньше (<>).

В результате получаем следющую процедуру проверки:

function check_name( $filename) {
    $result=(substr($filename,0,1)!='/' && substr($filename,0,1)!='\\');
    if ($result) {
      $test=array('..','://','~','`','\'','"',':',';',',','&','>','<');
      for ($i=0, $count=count($test); $i<$count && $result; $i++) $result=(strpos($filename,$test[$i])===false);
    }
    return $result;
}

Кроме того, при загрузке файла имеет смысл сохранять его с таким именем, чтобы пользователь не мог его угадать (лучше всего для этой цели подходит MD5-хеш от имени + размера + времени загрузки + случайного значения), и не давать возможности узнать имя файла из URL ссылки для его скачивания/просмотра (т.е. если данные о загруженном файле хранятся в таблице БД, то скрипту для скачивания передавать номер этого файла в таблице, а не его имя).

О защите от XSS-атак

Воскресенье, 6 января, 2008

При разработке систем, в которых у пользователя есть возможность авторизации и отправки сообщений, существует ряд уязвимостей, с помощью которых можно организовать XSS-атаку путем внедрения JavaScript для отсылки cookies (или URL с идентификатором сессии).

Типичными способами являются следующие:

1. Самый очевидный и наиболее просто блокируемый: вставка тега <script> (отслеживается регулярным выражением вида

preg_replace('|<script(.*?)</script(.*?)>|is','',$buffer)

2. Добавление тега с обработчиком JavaScript-события (конструкции вида <div onMouseOver=”alert(’test’)”></div>. Такие регулярные выражения можно отследить следующим куском кода:

$events = array('onLoad','onClick'); // массив событий
$count=preg_ match_all('|<\w+(\s+[^>]*)>|s',$buffer,$matches);
for ($i=0; $i<$count; $i++) {
  $oldbuf=$matches[0][$i];
  foreach ($events as $curevent) {
    preg_replace('|\s+'.$curevent.'=".*?"|is','',$matches[1][$i]); // вместо '' можно вставить какое-нибудь уведомление о замене
    preg_replace('|\s+'.$curevent.'=\'.*?\'|is','',$matches[1][$i]);
    preg_replace('|\s+'.$curevent.'=\S*|is','',$matches[1][$i]);
  }
  $buffer=str_replace($oldbuf,$matches[0][$i],buffer); // замена исходной строки на очищенную от некорретных обработчиков
}

3. Добавление JavaScript в адрес ссылки или рисунка: (<a href=”javascript:alert(’test’)”></a>).
Корректируется следующим регулярными выражениями:

preg_replace('|<a href="\w+script:.*?"[^>]+>.*?</a>|is','',$buffer);
preg_replace('|<a href=\'\w+script:.*?\'[^>]+>.*?</a>|is','',$buffer);
preg_replace('|<a href=\w+script:\S*[^>]+>.*?</a>|is','',$buffer);

4. Добавление JavaScript в url внутри параметра style, например: <div sytle=”background: url(’javascript:alert(\’test\’)')”></div>

Корректируется следующим куском кода:

$count=preg_ match_all('|<\w+(\s+[^>]*)>|s',$buffer,$matches);
for ($i=0; $i<$count; $i++) {
  $oldbuf=$matches[0][$i];
  preg_replace('|\s+style=".*?"|is','',$matches[1][$i]); // вместо '' можно вставить какое-нибудь уведомление о замене
  preg_replace('|\s+style=\'.*?\'|is','',$matches[1][$i]);
  preg_replace('|\s+style=\S*|is','',$matches[1][$i]);
  $buffer=str_replace($oldbuf,$matches[0][$i],buffer); // замена исходной строки на очищенную от некорретных обработчиков
}

Но кроме этих, довольно широко известных способов реализации XSS, имеется еще ряд малоизвестных особенностей, которые следует учитывать:

1. Броузер MSIE позволяет писать название протокола не только слитно, но и через символы табуляции (а также, возможно, перевода строки), т.е. ссылка вида <a href=”javas cript:alert(’test’)”>Тест</a> в нем будет работоспособна. Поэтому имеет смысл сначала проводить удаление символов табуляции внутри ссылок

2. В броузере MSIE, кроме JavaScript, существует еще и Visual Basic Script, который также может быть внедрен в URL ссылок и рисунков. Он обозначается vbscript (код в примере 3 написан с учетом его возможного наличия).

3. Если пользователю разрешается добавлять прикрепленные файлы и они сохраняются на диск в доступном для просмотра с помощью броузера месте с расширениями .htm, .html , то возможна атака следующего типа: в HTML-файл помещается код JavaScript, и затем атакуемому с помощью средств социальной инженерии предлагается перейти по ссылке, ведущей на этот файл напрямую, вместо того, чтобы скачать его скриптом.

Кроме того, даже если существует проверка на то, что загружаемый файл является графическим, внедрение JavaScript-кода в файл все равно возможно через так называемые EXIF-параметры (например. вместо имени автора можно прописать <script>alert(’test’)</script>), если файл сохраняется на диск с расширениями .html или .htm.

Примечание: предложенные здесь варианты решения являются упрощенными и для серьезных проектов я порекомендовал бы написать полноценный парсер HTML-кода, а не обходиться регулярными выражениями.


Rambler's Top100