Pull to refresh

Защита Asterisk при атаке на номер 8-800

Reading time3 min
Views35K
После прочтения этой статьи с ужасом для себя понял, что мы никак не защищены от такой атаки. И легко можем попасть на 10-20 тысяч рублей в день. Решил это дело исправить. Накидал на коленке защитный скриптик — возможно кому-то пригодится…

Подготовка системы
Скрипт написан на php и использует sqlite3 для хранения информации по этому нужно установить php-cli и php5-sqlite3
в моем случае система поднята на убунте, по этому ставится так

sudo apt-get install php5-cli php5-sqlite


Непосредственно сам скрипт.
Принцип работы такой. Скрипту передается номер телефона звонящего. Он заносит номер в базу, проверяет — сколько раз он уже звонил и сверяет это с правилами (они задаются в первой строчка скрипта). Если лимиты превышены — возвращает слово 'stop', в противном случае позвращает 'continue'

<?php
$rules = array( 
                60 => 2, // Не более 2 звонков в минуту ( 60 секунд )
                3600 => 10 // Не более 10 звонков в час ( 3600 секунд )
);

if (!$argv[1] ) die("please use: '" . $argv[0] . " phone_number' \nfor example: ".$argv[0]." 88121234567\n");

// Открываем или создаем БД
$db = new SQLite3('/tmp/sqlite.db');
$db->exec('CREATE TABLE IF NOT EXISTS logs (phone bigint(12), datetime int(12))');

// Добавляем номер и дату звонка в базу
$phone = preg_replace('/[^0-9]/','', $argv[1]);
$db->exec("INSERT INTO logs (phone, datetime) VALUES ( '".$phone."','".time()."' )");

foreach( $rules as $secs => $limit ) {
    $res = $db->query( "SELECT count(*) as `c` FROM logs WHERE `phone` = '".$phone."' AND `datetime` >= " .( time() - $secs ) );
    $row = $res->fetchArray();
    // Если привышен лимит
    if ( $row['c'] > $limit ) {
        die('stop');   // возвращаем stop и выходим из скрипта
    }
}

// Очистка мусора - удаляем старые данные, чтобы не раздувать базу
$max_period = max(array_keys($rules) );
$db->exec("DELETE FROM logs WHERE `datetime` < " .( time() - $max_period ));

// возвращаем continue
die('continue');

?>


Подключение к Asterisk
Важно выполнять скрипт в первых строках — до вызова команды Answer() или любой другой команды, которая открывает «снимает» линию.

exten => 8800XXXXXXX,1,Set(resp=${SHELL(php /home/scripts/antiddos.php ${CALLERID(num)})});
exten => 8800XXXXXXX,2,Gosubif($[${resp}==stop]?${EXTEN},${MATH(${PRIORITY}+1),int}:${EXTEN},${MATH(${PRIORITY}+2),int});
exten => 8800XXXXXXX,3,HangUp();
exten => 8800XXXXXXX,4,Answer();
...


Разберем диалплан по строчкам:
1) Set(resp=${SHELL(php /home/scripts/antiddos.php ${CALLERID(num)})});
вызываем скрипт и присваиваем переменной resp значение, которое скрипт вывел в консоль
2) Gosubif($[${resp}==stop]?${EXTEN},${MATH(${PRIORITY}+1),int}:${EXTEN},${MATH(${PRIORITY}+2),int});
если значение равно 'stop', то переходим на следующую инструкцию текущего диалплана, где нас ожидает компанда HangUp()
в противном случае переходим через строчку, и выполняем дальше диалплан.

Что в итоге происходит.
Если лимит не превышен — получаем такую SIP сессию:
sip provider        me
        invite =>
               <=   trying
               <=   OK
         ack   =>

Значит всё ок, тарификация началась

Если мы делаем HandUp(), то SIP сессия такая:
sip provider        me
        invite =>
               <=   trying
               <=   DECLINE
         ack   =>


Decline означает, что вызываемый пользователь отклонил входящий вызов. Тарификация не должна начинаться, т.к. разговора небыло и сессия прекратилась.

UPD:
При атаке с анонимных номеров можно немного доработать скрипт и установить лимит на номера, закрытые антиАОНом…
Tags:
Hubs:
+76
Comments36

Articles

Information

Website
www.angels-it.ru
Registered
Founded
Employees
2–10 employees
Location
Россия