Bezpečná aplikácia v PHP

733

security_background

V posledných týždňoch čítam väčšinou články narážajúce na bezpečnosť aplikácii napísaných v PHP. Vidím v tom jednoduché vysvetlenie, prečo také drvivé percento aplikácii je napísaných nebezpečne. PHP je jednoduchý jazyk, ktorý doslova povoľuje rôzne porušovanie praktík bez sprvu viditeľných následkov. Je jednoduché naučiť sa ho, keďže internet je plný zdrojov. Avšak naučiť sa PHP správne a bezpečne vyžaduje oveľa viac.

„PHP code is as secure as the programmer writes it.“

Pri písaní PHP aplikácie je dobré držať sa týchto pár bodov.

  • Overovať všetky vstupné dáta
  • Ošetrovať vstupy proti XSS
  • Ošetrovať vstupy proti CSRF
  • Ošetrovať vstupy proti SQLi
  • Zabezpečiť súborový systém
  • Zabezpečiť „session“
  • Správne spracovávať chyby
  • Ošetrovať vkladané (include) súbory

To sú body, ktorých sa treba držať pri písaní, avšak treba dbať aj na serverovú implementáciu:

  • Vypnúť „bad features“ pre PHP
  • Udržiavať aktuálnu verziu PHP
  • Udržiavať aktuálnu verziu distribúcie OS
  • Udržiavať aktuálnu verziu web servera
  • Skryť PHP
  • SSL

Takže to sú základné body o ktorých si niečo napíšeme, samozrejme treba správne nakonfigurovať server pre užívateľské prístupy a podobne. Ale to je naozaj príliš robustná téma, keďže PHP-čko spustíte aj na Linuxe, aj na Windowse atď…

Ošetrovať všetky vstupné dáta

Všetky dáta, ktoré bude spracovávať vaša aplikácia, musia byť overované podľa určitého vzoru, ktorý si nastavíte, a rovnako tak musia byť filtrované a overené serverovi, čiže priamo v PHP.

Vždy sa pridržiavajte hesla: „Neverte používateľom“. Ak predpokladáte, že váš web bude len pre pár dobrých ľudí, ktorý nič zlé nechcú, nedajte sa zmiasť. Vždy sa nájde niekto (alebo niečo – robot), čo vám vašu predstavu o dobrých ľuďoch zrúca ako domček z karát.

Nikdy neoverujeme dáta len na strane klienta. Toto overovanie sa dá jednoducho prekabátiť, trebárs obyčajným vypnutím Javascriptu, prepísaním pravidel a podobne. Overovanie v JS je pekné a v neposlednom rade aj user-friendly, avšak nedostatočné.

VŽDY OVERUJEME NA SERVERY (tj, v PHP scripte)

Ošetrovanie vstupov pred XSS

Ako som spomínal, v prvom bode učiť sa, učiť sa, učiť sa, ošetrovanie, ošetrovanie, ošetrovanie… to je základ bezpečnej aplikácie. O XSS som písal už myslím dávnejšie, alebo som o tom mal prednášku, naozaj si nespomínam, ak nájdem link, doplním. Je to však metóda, ktorou vložíme pre majiteľa nechcený kód do tela stránky, napríklad nejaký script, ktorý ukradne session, prípadne vloží nejaký iframe do webu a podobne.

Každý input, aj taký ktorý je „hidden“ má nejakú „value“, ktorá sa dá zmeniť, napríklad cez Firebug. Preto overujeme absolútne každý jeden vstup, ktorý do aplikácie vkladá používateľ. Na toto overovanie použijeme napríklad strip_tags().
Pre toto som už dávnejšie vytvoril knižnicu, ktorá by mala byť 100% bullet-proof, a je free na našom firemnom githube: https://github.com/detroitstudio/xss_remove.

Vo výpise zasa použijeme funkciu htmlentities(). To je z dôvodu, že ak sa náhodou niekomu podarilo prepašovať bordel do databázy, tak ho jednoducho „escapne“ vo výpise. Avšak pri HTMLENTITIES si treba dávať pozor na použitie, samotné použitie napríklad htmlentities($value); nie je bezpečné. Dôvody prečo sa dočítate v tomto článku.

Preto správne používame túto funkciu takto: htmlentities($value, ENT_QUOTES, „UTF-8“);

Ošetrovanie vstupov proti CSRF

XSS ošetrovanie by sme mali, prejdime k CSRF. Čo to vlastne je, je to typ útoku pri ktorom môžeme, napríklad, nechtiac odoslať formulár, ktorý využíva metódu GET za pomoci napríklad obrázka, ktorý sa vždy získava metódou GET.

Obrana pred týmto typom útoku sa dávnejšie riešila za pomoci HTTP_REFERER, avšak toto nie je účinná obrana, pretože tento údaj sa dá falšovať. Stretol som sa rovnako tak s radou, aby sa overoval User-Agent, čo je totálna hlúposť, najmä ak vieme, že user-agent sa nastavuje u obete, CSRF využíva rovnaký prehliadač, takže sa v praxi nemení.

Čo však pomáha, je napríklad odosielať „signed forms“, alebo aj podpísané formuláre, v ktorých využijeme takzvané CSRF Tokeny, ktoré sa overujú na serveri. Dôležité je aby sa k danej hodnote tokenu nedostal útočník napríklad „sniff-ovaním“ na Wi-Fi zapomoci Wiresharku, to vieme ochrániť napríklad SSL Certifikátom.

Ošetrovať vstupy proti SQL injection

Toto je podľa mňa najpálčivejšia otázka, čo sa týka webového programovania na Slovensku (možno aj celkovo). Je to jeden v podstate s najzákrejenších a najjednoduchších útokov, pri ktorých sa vieme dostať k dátam a v najhoršom zapisovať a exekuovať súbory na serveri, no stále sa nájde v množstve aplikácii.

Naposledy sa mi podarilo SQL injection náhodne, keď kolega nechtiac odoslal formulár na obnovenie hesla s úvodzovkou na web-stránke známej Žilinskej reštaurácie. (stále to nemajú opravené).

Chránime sa tomu v podstate jednoducho. Zásadne nepoužívame mysql_query. Najideálnejšia možnosť momentálne, ako sa pripojiť k DB natívnou knižnicou, je PDO, ktoré dovoľuje takzvané bindovanie parametrov.

Takže každé query sa pred exekúciou vyskladá, a to, že parametre (values) nabindujeme a správne escapneme nám nedovolí do aplikácie vpustiť nechcené znaky, ktoré by exekúciu query mohli narušiť.

Článok o SQL Injection a príkladoch ako na ňu som písal dávnejšie tu.

Zabezpečenie súborového systému

Ako aj vstupy, ktoré smerujeme do databázy, tak aj vstupy, ktoré smerujeme do priečinka našej aplikácie, treba chrániť. Rovnako tak treba dbať na to, aby sme písali správne scripty na stiahnutie a takisto mali nastavené dostatočné práva, aby sa útočník nedostal k zdrojákom, prípadne nevložil niečo škaredé na náš server.

Ako chrániť upload som písal v minulosti, tu sú nejaké tie tipy a triky.

Zabezpečenie Session

V predvolenom nastavení sa session ukladá do adresára „temp“, takže ak využívate zdieľaný hosting, tak je celkom jednoduché napísať script a túto session ukradnúť.

Na ochranu session môžete použiť, napríklad, šifrovanie informácii v nej. Dôrazne sa neodporúča mať v session uložené heslá, či čísla kreditných kariet. Ideálny stav je, ak ukladáte dáta do DB.

Od verzie PHP 5.4 je možné používať takzvaný objekt typu SessionHandlerInterface a ukladať do session_set_save_handler(). Viac info v dokumentácii.

Správne spracovávanie chýb

V prípade, že aplikáciu vytvárame, je pre nás error_reporting spása. Avšak keď púšťame aplikáciu „live“, to znamená, že koncovým užívateľom je potrebné reportovanie chýb skryť, pretože by sa mohli dostať k niektorým citlivým informáciám, napríklad k štruktúre nášho súborového systému a podobne.

Takže v prípade, že aplikáciu spúšťame verejne, nastavíme error_reporting(0);

Avšak to nie je úplne korektné, pretože ťažko budeme hľadať chyby, ak nejaké prídu, takže ideálne si nastavíme aj logovanie chýb do zabezpečeného súboru, to nastavíme v set_error_handler.

Pri rôznych frameworkoch samozrejme nezabúdame na skrývanie debug toolbarov, ktoré niektoré majú. Väčšinou sa to nastavuje v konfiguračných súboroch zmenou hodnoty true na false.

Ošetrovanie vkladaných (include) súborov

Väčšina súborov používa takzvané vkladanie iných súborov do samých seba, v týchto súboroch môžu byť napríklad údaje pre pripojenie k databáze a podobne. Veľakrát sa stáva, že sa používa prípona .inc, čo je veľká chyba, keďže je súbor interpretovaný ako text. A ak ho útočník priamo napíše do prehliadača, získa vaše dáta. Preto majte na pamäti, vždy používať súbory s koncovkou .php vo verejne neprístupných adresároch.

Vypnutie „bad features“ v PHP

Z času na čas prídu vývojári PHP-čka s niečím, o čom si myslia, že uľahčí vývoj, no a práve naopak, stane sa z toho veľmi zlá „zábava“.

Čo odporúčam vypnúť v PHP nastaveniach je určite:

register_globals

Táto srandička vyšla ako pomoc pri rýchlom vývoji. Funguje na princípe, predstavme si URL http://someweb.com/index.php?somewar=value.

Namiesto získania hodnoty pre somewar takto: $_GET[‘somewar’] ju získame takto: $somewar
Môže to síce vyzerať užitočne, avšak takto dokážeme meniť premenné v celom dokumente. Čiže, ak máme nejaké overenie a podmienku
if($user->isLoggedIn()){ $access = 1; },
tak nám stačí do url vložiť parameter &access=1 a tým oklameme celé overenie. Takže jednoznačne
register_globals = Off.

V prípade, že nemáme prístup k php.ini, tak ho vieme vypnúť aj zapomoci .htaccess súboru
php_flag register_globals = 0.

magic_quotes

Magic quotes boli vymyslené, aby pomohli programátorom ľahko ošetrovať vstupy a prácu s addslahshes(). Avšak magické úvodzovky, ako v preklade znejú, majú minimálne tri problémy, a to prvý, ak máte zapnuté magic_quotes a použijete addslashes(), môže sa stať, že sa pridá viac lomiek a tým spôsobíte chybu. Prípad číslo dva, ak predpokladáte, že sú magic_quotes zapnuté, a nie sú, tak ostanú všetky vstupy neošetrené a tretí že dokážu ošetrovať len úvodzovky a to konkrétne ‘ a ” nič viac, a keďže DB používa viac špecifických znakov, môže nastať problém.

Takže sa odporúča:
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off

V prípade že nemáte prístup k php.ini tak opäť raz .htaccess súbor
php_flag magic_quotes_gpc 0 php_flag magic_quotes_runtime 0.

Udržiavať aktuálnu verziu PHP/Udržiavať aktuálnu verziu distribúcie OS /Udržiavať aktuálnu verziu web servera

K tomuto snáď niet čo písať, je samozrejme dôležité priebežne si sledovať, aké vyšli verzie softwaru, ktorý používate, a udržiavať ich aktuálne. Môže vám to ušetriť množstvo problémov. Tu by som ešte priložil jednu stránku, kde sa dajú pozrieť variácie os a php, ktoré je ideálne mať nakonfigorované.
http://blog.ircmaxell.com/2014/12/php-install-statistics.html

Skryť PHP

PHP obsahuje jednu užitočnú funckiu, ktorou môžeme predísť veľa nepríjemnostiam, tým, že útočníkovi poskytneme čo najmenej informácii (ideálne žiadne!). Na toto slúži takzvané expose_php = Off.

Čo však ešte môžeme použiť sú rôzne metódy kamufláže. Napríklad, že zamaskujeme PHP za iný jazyk. Na toto sa dá použiť .htaccess a vyzerá to nasledovne

AddType application/x-httpd-php .asp .py .pl

Druhý scenár, môžeme si vytvoriť vlastnú príponu a nezobrazovať .php koncovku pri súboroch

AddType application/x-httpd-php .bop .foo .133t

A podobne. Viac o kamufláži nájdete na php.net.

SSL

To, že SSL je potrebné, som písal už dávnejšie v blogu tu, takže sa o tom nejdem rozpisovať znova.

Čo však treba dodať, tak to, že SSL si treba pravidelne kontrolovať. Pred pár mesiacmi sme tu mali dve za sebou idúce zraniteľnosti, prvá Heartbleed a druhá Poodlebleed, o ktorej som tiež písal.

Ideálne je pozerať si sem tam stránku: http://dayswithoutansslexploit.com/.

Takže to by bolo v skratke všetko. Snáď som na nič nezabudol, ak áno, doplňte v komentároch, budem rád. A znova opakujem: „NEVERTE POUŽÍVATEĽOM!!!“

Dobrý článok? Chceš dostávať ďalšie?

Už viac ako 6 200 ITečkárov dostáva správy e-mailom. Nemusíš sa báť, nie každé ráno. Len občasne.

Súhlasím so spracovaním mojich osobných údajov. ( Viac informácií. )

Tvoj email neposkytneme 3tím stranám. Posielame naňho len informácie z robime.it. Kedykoľvek sa môžeš odhlásiť.