Jak blokować sieć TOR?

Dzisiaj wpis, który w zasadzie nie był planowany, ale że plany się lubią zmieniać, to jednak się pojawi. Rzecz tyczy się sieci TOR – projektu, który umożliwia użytkownikom niemalże anonimowe korzystanie z sieci. Sama idea jest naprawdę dobra i ją popieram, niestety jak to zwykle bywa w takich sytuacjach, z możliwości ukrywania się szybko skorzystali pedofile czy też włamywacze. Właśnie ten drugi wypadek miał miejsce podczas ataku na polski support MyBB – nie ma to jak z pięknego, otwartego projektu atakować darmowy, otwarty support… Od tamtego czasu nie jestem zwolennikiem tej sieci, zresztą jeżeli ktoś chce na poważnie z kimś współpracować, to na co mu takie ukrywanie się? Skoro zna TORa, to raczej używa i smarftona, a te pozwalają na bardzo wnikliwe szpiegowanie naszej aktywności.

No dobra, ale nie o tym miało być. Warto się zastanowić, czy sieć TOR da się blokować. Okazuje się, że i owszem, przynajmniej po części. W sieci są publikowane adresy IP z jakich TOR korzysta wychodząc do “normalnego internetu”. Można tą listę wykorzystać i kosić cały ruch jaki z niej idzie, po prostu. Oczywiście nie na wiele się to zda gdy ktoś połączy się z TORem, a potem dodatkowo będzie tunelował połączenie z jakimś normalnym hostem, ale odpadną nam np. script kiddies. Postanowiłem przedstawić poniżej kilka rozwiązań, które sam znam, jeżeli ktoś ma jeszcze jakieś, byłbym wdzięczny za podzielenie się nimi.

1. Blokowanie na poziomie serwera www

W zasadzie ciężko to nazwać prawdziwym blokowaniem, ponieważ odrzucane będą połączenia tylko na portach serwera www, a więc najczęściej 80 i 443. Nie uchroni to nas przed skanowaniem czy dobijaniem się na inne porty, a to raczej właśnie one (np. ssh) staną się prędzej celem ataku aniżeli www. Poniżej przedstawiam skrypt, a w zasadzie tylko jedno polecenie, które pobiera listę adresów do zablokowania i umieszcza ją w pliku czytanym przez serwer www – w tym wypadku jest to nginx, w którym poszczególne hosty można blokować z poziomu plików konfiguracyjnych wpisami deny IP;. W wypadku np. Apache, wystarczy zmodyfikować to i albo również umieszczać w oddzielnym pliku konfiguracyjnym załączanym w głównych plikach z ustawieniami, albo w plikach .htaccess.

wget -q https://www.dan.me.uk/torlist/ -O - | sed "s/^/deny /g; s/$/;/g" > /etc/nginx/conf.d/tor-ip.conf

Skrypt taki możemy odpalać z crona np. co półtorej godziny. Listę pobierać możemy i tak maksymalnie raz na godzinę z jednego adresu IP, częstsze aktualizacje więc nic nam nie dadzą.

2. Blokowanie z poziomu firewalla

Znacznie lepszym rozwiązaniem jest wycinanie ruchu na poziomie firewalla. Będzie to nie tylko lżejsze, ale i znacznie bardziej szczelne, bo nie dopuści użytkownika TORa do żadnych naszych portów. W sieci można znaleźć informacje o tym, jak to zrobić “na goło” przy użyciu ponownie crona i iptables, ale lepszym rozwiązaniem i tak jest używanie jakiegoś oprogramowania-nakładki (a przynajmniej ja się do niego przekonałem z czasem). Mowa tu o Config Server Security & Firewall. Nie będę się rozpisywał tutaj o jego możliwościach, bo z pewnością jest to dobry temat na jeden z przyszłych wpisów. Pozwala on na używanie plików z zapisanymi listami do blokowania danych IP, można więc i tu wykorzystać odpowiedni skrypt:

wget -q -O /etc/csf/csf.deny https://www.dan.me.uk/torlist/

Tak było jeszcze do niedawna. Obecnie nie jest to już potrzebne, bo autorzy CSF postanowili wyposażyć go w natywną obsługę blokowania TORa… czyżby i oni sądzili, że osoby chowające się w nim nie są godne zaufania? Obecnie wystarczy jedynie ustalić interwał czasowy aktualizacji danych o IP TORa i zrestartować CSF… to wszystko. Domyślnie plik konfiguracyjny to /etc/csf/csf.conf i zmienić należy następującą wartość:

LF_TOR = "0"

Zamiast domyślnego zera wskazujemy oczywiście interwał podany w sekundach, domyślnie 86400.

3. Blokowanie gdy korzystamy z CloudFlare

Problem pojawia się wtedy, gdy pomiędzy naszym serwerem i stronami a użytkownikami stoi jakiś pośrednik. Takim pośrednikiem jest np. CloudFlare i domyślnie np. serwer www widzi wszystkich jako jego IP. Są oczywiście na to odpowiednie metody używające wysyłanych przez CF nagłówków typu pluginy w skryptach, czy odpowiednie zapisy konfiguracyjne w serwerze www (np. set_real_ip_from w nginx), ale nadal nas to nie zabezpiecza. Powinniśmy blokować ruch z poziomu samego CF, a dodatkowo na serwerze mieć zapisy w iptables (bo jak wspomniałem, www może nikogo nie interesować). Można to zrobić wbijając ręcznie adresy IP, ale byłoby to oczywiście niemożliwością. CloudFlare udostępnia na szczęście API pozwalające na wykonywanie zdalnych i automatycznych operacji. Poniżej skrypt w PHP, który (odpalany z crona) może wysyłać do CF informacje o tym, co powinien dla nas blokować:

/*
 * Settings
 * 
 */
define('CF_EMAIL', 'nasz_email_w_cf');
define('CF_API_KEY', 'nasz_klucz_api_w_cf');
define('CF_API_URL', 'https://www.cloudflare.com/api_json.html');
define('TOR_LIST_URL', 'https://www.dan.me.uk/torlist/');

/*
 * Code, DO NOT EDIT
 * 
 */
@ini_set('error_log', 'error_log');
@ini_set('max_execution_time', 900);

$curly = array(); 
$mh = curl_multi_init(); 

// Get IPs list from TOR database
$ips = file_get_contents(TOR_LIST_URL);
$ips = array_map('trim', $ips);

// Prepare all handlers
foreach ($ips as $ip)
{
    if (!preg_match('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#', $ip))
    {
        continue;
    }

    $post_vars = 'a=ban&tkn=' . CF_API_KEY . '&email=' . CF_EMAIL . '&key=' . $ip;
    $curly[$ip] = curl_init(CF_API_URL);
    curl_setopt($curly[$ip], CURLOPT_POST, 1);
    curl_setopt($curly[$ip], CURLOPT_POSTFIELDS, $post_vars);
    curl_setopt($curly[$ip], CURLOPT_FOLLOWLOCATION, 0);
    curl_setopt($curly[$ip], CURLOPT_HEADER, 0);  // DO NOT RETURN HTTP HEADERS
    curl_setopt($curly[$ip], CURLOPT_RETURNTRANSFER, 1);  // RETURN THE CONTENTS OF THE CALL

    curl_multi_add_handle($mh, $curly[$ip]);
}

// Execute the handles
$running = null;
do 
{
    curl_multi_exec($mh, $running);
} 
while ($running > 0);

// Clean handlers
foreach ($curly as $id => $c) 
{
    curl_multi_remove_handle($mh, $c);
}
curl_multi_close($mh);

No i to by było na tyle. Oczywiście opis ten jest dosyć lakoniczny, ogranicza się tylko do niektórych platform, no ale mam nadzieję, że ktoś znający lepsze metody, lub widzący wady tych przedstawionych przeze mnie, przyłączy się do dyskusji.

Leave a Reply