SQL INJECTION ATTACK PREVENTION in PHP / CODE EXAMPLES

po několika letech penetračního testování a práce s vývojovými týmy na zabezpečení jejich systémů jsem si všiml, že neexistuje žádný úplný blogový příspěvek, který by podrobně vysvětlil, jak zabránit útokům SQL injection ve všech nejpopulárnějších jazycích a rámcích pro vývoj webových aplikací. Proto jsem se rozhodl napsat řadu více příspěvků, aby se stal nejúplnějším průvodcem, jak zabránit injekci SQL.

tato příručka je otevřená řada příspěvků a toto je první příspěvek. Pokud chcete, abych přidal jakoukoli technologii, prosím, komentujte níže a já vám velmi rád vysvětlím prevenci SQL injection pro tuto technologii. Tato příručka bude analyzovat následující technologie:

  • PHP
    • Laravel
    • Symfony
    • CodeIgniter
    • CakePHP
    • FuelPHP
    • Zend
  • JEE
    • JDBC
    • JPA
  • ASP.NET
    • ADO.NET
    • ADO.NET Dopper
    • NHibernate

tento první příspěvek se zaměří na jazyk PHP a všechny jeho rámce. Další články budou následovat, aby se zabývaly dalšími uvedenými technologiemi.

pro ty, kteří nevědí, co je SQL injection attack, dovolte mi udělat malý úvod, abych jim to vysvětlil. K útokům SQL injection dochází, když se špatný uživatel pokusí vložit škodlivý požadavek SQL do legitimního požadavku. Dopad SQL injection attack se liší od situace k jiné v závislosti na více prvcích souvisejících s prostředím aplikace a může jít od tak “jednoduchého” jako úniku informací k úplnému řízení serveru. Nebudu jít hlouběji do způsobu, jakým by útočník mohl tuto chybu zabezpečení zneužít, nebo jak ji můžeme objevit v testech blackbox, protože to není cílem tohoto příspěvku. Uvidíme, jak to objevit ve zdrojovém kódu a jak jej opravit.

jak zabránit útoku SQL injection v čistém zdrojovém kódu PHP ?

začněme naši cestu s jednou z nejstarších a populárních vývojových technologií PHP bez rámců. První věcí, kterou musíte udělat, je vědět, jak vypadá zranitelný řádek zdrojového kódu, aby bylo možné tuto chybu zabezpečení identifikovat.

zde je příklad požadavku na ověření:

$request = "SELECT * FROM users WHERE username=". $_POST ." AND password = ". $_POST;
$result = $mysqli->query($request);

Nyní, pokud se blíže podíváte na požadavek, zjistíte, že uživatelské parametry $_POST a $_POST jsou přímo injektovány do požadavku SQL bez filtrování. Tímto způsobem je tento kódový řádek zranitelný vůči SQL injection.

Chcete-li tuto chybu zabezpečení opravit, musíte data před použitím v požadavku sql filtrovat. Nejlepším řešením je použít připravená prohlášení. Zde je příklad toho, jak můžete použít připravené příkazy k opravě této chyby zabezpečení:

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param($username, $password);
$stmt->execute();

dobrá praxe

máte možnost Nepoužívat funkci bind_param() zde je příklad, jak to provést :

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=:username AND password=:password");
$stmt-> execute(array('username'=>$_POST,'password'=>$_POST));

po letech analýzy zdrojových kódů … jsem objevil zajímavou chybu, kterou mnoho vývojářů dělá při opravě této chyby zabezpečení. Podívejte se na následující příklad:

$stmt = $conn->prepare("SELECT * FROM users WHERE username=".$_POST." AND password=".$_POST);

pokud se podíváte na tento příklad, uvidíte, že používáme připravená prohlášení. Parametry jsou však přímo vloženy do požadavku SQL. To je velmi častá chyba, kterou vývojáři dělají, a činí kód zranitelným vůči SQL injection, i když použijeme připravené příkazy.

myšlenka připravených příkazů je filtrovat parametry před jejich vložením do požadavku SQL … mějte to na paměti .

velmi důležitá poznámka:

PHP nabízí některé další funkce pro filtrování požadavků SQL a zabránění vstřikování SQL, ale některé z nich nejsou efektivní. Například funkce mysql_real_escape_string () je jednou z nejznámějších funkcí PHP, která zabraňuje injekci SQL. Tato funkce je bohužel opravdu efektivní. Pomocí této funkce přidá Knihovna MySQL zpětné lomítko k následujícím znakům: NULL, \ x00, \ n, \ r,\,’, ” a \ x1a. nicméně pro situaci, jako je tato :

$id = mysql_real_escape_string("1 OR 1=1");
$request = "SELECT * FROM users WHERE id = $id";

v červené barvě je to, co vám špatný uživatel pošle

mysql_real_escape_string () vás před takovým útokem neochrání.

proto vždy doporučuji použít připravené příkazy místo této funkce.

jak zabránit útoku SQL injection v rámci Laravel ?

Nyní, když jsme viděli útok SQL injection proti čistému kódu PHP, nyní je čas zjistit, jak můžeme tuto chybu zabezpečení opravit ve webové aplikaci založené na rámci. Než o tom začneme mluvit, dovolte mi nejprve uvést malý popis rámce Laravel.

Laravel je open-source webový framework napsaný v PHP respektující princip model-view-controller a zcela vyvinutý v objektově orientovaném programování.

Příklad 1

zde je příklad zranitelného požadavku SQL v Laravelu :

$user = DB::select('select * from users where username='. $request->post('username').' AND password='. $request->post('password'));

Laravel framework nabízí některé úžasné funkce, které pomáhají vývojářům zabezpečit požadavek SQL před škodlivými injekcemi. Bohužel většina zranitelností SQL injection, které v takových aplikacích objevuji, souvisí se zneužitím funkcí Laravel.

pojďme analyzovat předchozí příklad, podle dokumentace Laravel může funkce DB:: select přijímat dva parametry. První je požadavek SQL a druhý jsou parametry. Chcete-li filtrovat parametry pro SQL injection, musíte vložit parametry do požadavku pomocí druhého parametru, jako je tento příklad:

$user = DB:: select (‘select * from users where username=? A heslo=?’, );

příklad 2

Laravel má několik způsobů komunikace s databází v tomto příkladu uvidíte, že laravel nutí vývojáře používat některé funkce, které automaticky filtrují uživatelská data, jako je následující příklad:

Users::where('username', $request->get('username'))->orderBy($request->get('orderby'))->get();

Tato funkce má být zabezpečena proti SQL injection a jsou, jediný problém je na úrovni orderBy() funkce. Podle dokumentace Laravel tato funkce nefiltruje uživatelská data, takže prostřednictvím této funkce je stále možný útok SQL injection.

nejlepší věc, kterou musíte udělat, není dát uživateli možnost ovládat název tabulky,ale pokud je to něco nevyhnutelného, budete muset použít bílý seznam k ověření uživatelských dat před jejich vložením do této funkce.

jak zabránit útoku SQL injection v Symfony ?

podívejme se na další známý rámec PHP, který nabízí sadu opakovaně použitelných komponent PHP pro urychlení vývoje aplikací PHP. Symfony je také používán některými z nejznámějších webových CMS jako Drupal a Magento.

Symfony je jedním z nejbezpečnějších rámců, se kterými můžete pracovat, nabízí širokou škálu funkcí pro psaní bezpečného kódu. Pokud však tyto funkce nejsou dobře používány, získáte zranitelný kód. Zde je příklad zranitelného kódu:

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > '. $request->query->get('price')
);

nyní analyzujeme tento zranitelný kód. Podle dokumentace Symfony je createQuery() funkce, která ve výchozím nastavení používá připravené příkazy. Objekt vyplývající z takové funkce vám tedy poskytuje přístup k funkci setParameter (). Tento filtruje všechna uživatelská data, aby se zabránilo injekci SQL. Zde je příklad, jak jej používat :

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', $price);

jak zabránit útoku SQL injection v Codeigniter ?

CodeIgniter jeden z nejvýkonnějších, nejlehčích a nejoblíbenějších rámců PHP s velmi malou stopou. Byl postaven pro vývojáře, kteří potřebují jednoduchý a elegantní nástroj pro vytváření plnohodnotných webových aplikací. Stejně jako Symfony a Laravel, Codeigniter také přichází s některými bezpečnostními systémy, které pomáhají vývojářům vytvářet bezpečnější aplikace.

nicméně také s Codeigniter někteří vývojáři dělají stejnou chybu a vkládají uživatelská data bez jakéhokoli filtrování. Zde je příklad takových chyb, které vedou k útoku SQL injection:

$query = 'SELECT * FROM users WHERE username = '. $this-> input->post('username'). ' AND password= '. $this-> input->post('password');
$this->db->query($query);

Chcete-li tento problém vyřešit, musíte se podívat na dokumentaci Codeigniter. Podle něj má funkce query() dva parametry. První je dotaz sql a druhý je parametry, které chcete svázat.

Příklad 1

ve výchozím nastavení Tato funkce filtruje všechny vázané parametry, aby se zabránilo injekcím SQL. Zde je příklad správného způsobu použití této funkce:

$query = 'SELECT * FROM users WHERE username = ? AND password = ? ';
$this->db->query($query, array($this->input->post('username'), $this-> input-> post('password')));

příklad 2:

Codeigniter poskytnout jiný způsob, jak provést SQL požadavek zabrání SQL injekce. Zde je příklad použití třídy Active Record k zabránění injekcí SQL:

$this->db->get_where('users',array('username'=>$this->input->post('username') ,'password' => $this->input->post('password')));

funkce get_where () již ve verzi 4 Codeigniter neexistuje.

Poznámka 1

Codeigniter nabízejí také funkci, která vypadá, mysql_real_escape_string () funkce s názvem escape (). Ale jak jsem poprvé řekl mysql_real_escape_string () může být v některých případech vynechán a musíte se vyhnout jeho použití. Skutečnost, že funkce escape() dělá přesně to samé, pak by bylo možné ji také obejít. Proto nepodporuji vývojáře, aby používali funkci escape ().

Poznámka 2

jak zabránit útoku SQL injection v CakePHP ?

CakePHP je rychlý vývojový rámec pro PHP, který používá běžně známé návrhové vzory, jako je asociativní mapování dat, přední řadič a MVC. CakePHP nabízí spoustu vestavěných komponent, které usnadňují vývoj webových aplikací a učiní je bezpečnějšími.

aplikace CakePHP jsou však také zranitelné vůči injekcím SQL, pokud nejsou dobře používány. Zde je příklad zranitelného kódu k útoku SQL injection v tomto rámci:

$results = $connection-> execute('SELECT * FROM users WHERE username = '. $this->request->getParam('username').' AND password='. $this->request->getParam('password'))->fetchAll('assoc');

funkce execute() ve výchozím nastavení používá připravené příkazy. Předchozí příklad je však stále zranitelný vůči útoku SQL injection, protože uživatelská data jsou vložena přímo do legitimního požadavku sql. Zde je příklad správného způsobu použití této funkce:

$results = $connection->execute('SELECT * FROM users WHERE username = :username AND password=:password', )->fetchAll('assoc');

rámec CakePHP nabízí také systém nazvaný Query Builder, který nutí filtrování uživatelských dat, aby se zabránilo útokům SQL injection. Podle dokumentace CakePHP používá Tvůrce dotazů pod kryty připravené příkazy.

zde je příklad toho, jak můžete tento systém používat správným způsobem:

use Cake\ORM\Locator\LocatorAwareTrait;
$users = $this->getTableLocator()->get('users');
// Start a new query.
$query = $users->find();
$query->where();

jak zabránit útoku SQL injection v FuelPHP ?

FuelPHP je jedním z nejnovějších php framework, který se narodil na základě nejlepších nápadů každého rámce na trhu. Byl vyvinut s PHP 5 a je plně objektově orientovaný. Ve FuelPHP byla bezpečnost přední a středem zájmu,což přimělo její přispěvatele k implementaci mnoha bezpečnostních mechanismů pro filtrování uživatelských dat. SQL injection útočí na jeden z důvodů implementace takových mechanismů ve FuelPHP.

nicméně, i s tak silným rámcem, jednoduché zneužití těchto mechanismů ohrozilo celý kód pro útok SQL injection. Zde je příklad takové chyby kódu:

$query = "SELECT * FROM article WHERE id = ". Input::get('id');
$result = DB::query($query)->execute();

Chcete-li to opravit, existují také dvě techniky, jako jsou ostatní rámce. První z nich je skutečně správně pomocí funkce query (), vázáním parametrů, jako je následující příklad :

$query = "SELECT * FROM article WHERE id = :id"; // our query
$result = DB::query($query)->bind('id', Input::get('id'))->execute();

druhým řešením je použití mechanismu ORM implementovaného v rámci FuelPHP pro komunikaci s databází. Zde je příklad, jak jej používat:

$user = DB::select()->from('article')->where('id', Input::get('id'))->execute();

jak zabránit útoku SQL injection v rámci Zend ?

Zend framework (změněn na projekt Laminas) je jedním z nejznámějších frameworků ve světě PHP. Byl vyvinut s ohledem na ladění výkonu, což vysvětluje, proč je každá nová verze mnohem rychlejší než ta stará. Zend byl také vyvinut s nejlepšími bezpečnostními postupy, což přimělo jeho vývojáře k implementaci dalších bezpečnostních mechanismů pro řešení známých kybernetických hrozeb.

některé z těchto mechanismů byly implementovány pro řešení útoků SQL injection. Ale jako vždy zneužití těchto mechanismů vede k zranitelnému kódu. V této části článku ukážu příklad takové chyby:

$adapter->query('SELECT * FROM `article` WHERE `id` = '. $this->getRequest()->getPost('id'));

zde je návod, jak tuto chybu zabezpečení opravit:

$adapter->query('SELECT * FROM `article` WHERE `id` = ?', );

Leave a Reply