Internet Info, s.r.o. Lupa Měšec Podnikatel Root Zdroják DigiZone Slunečnice Vitalia TopDrive KupDnes Navrcholu NovýTarif Dobrý web Weblogy Woko Jagg Computer.cz SK: MojeLinky

Hlavní navigace

(Ne)bezpečí databázových aplikací v PHP

Jeden řádek kódu, a co dokáže udělat za paseku! Modelový příklad potvrzující, že kontrolu uživatelského vstupu nelze brát na lehkou váhu. A v Internetu hemžícím se nekalými živly to platí dvojnásob, NE, strašně-moc-násob.

Tweetni to Twitter Jaggni to! Jagg Del.icio.us Delicious

K napsání tohoto článku mě vedlo shlédnutí jednoho příšerného kusu kódu, který jeden člověk na jednom nejmenovaném serveru prezentoval jako ukázku řešení jistého problému. Nic proti autorovi, zřejmě to myslel dobře a třeba si i byl vědom bezpečnostní díry, která by nasazením kódu v reálném projektu vznikla. Nicméně se mi zdá nepatřičné začínajícím programátorům v PHP namlouvat, že „takhle se to skutečně dělá“. O co vlastně šlo?

Příklad ukazoval, jak s pomocí databáze řešit vícejazyčnost webovské aplikace. Řekněme v SŘBD MySQL se vytvořila tabulka s nějakým číselným ID zprávy a textovými sloupci langcz pro českou, langen pro anglickou a třeba langde pro německou verzi hlášení:

CREATE DATABASE pokus;
USE pokus;

CREATE TABLE msg (
  id INT NOT NULL AUTO_INCREMENT,
  langcz TEXT,
  langen TEXT,
  langde TEXT,
  PRIMARY KEY(id)
);

INSERT INTO msg (langcz, langen, langde)
  VALUES ("Dobrý den!", "Hello!", "Guten Tag!");


Vtip řešení vícejazyčných textů v aplikaci spočíval ve vytvoření speciální funkce, řekněme msg(), která se starala o vypisování hlášení z databáze v patřičném jazyku. Jako první parametr se jí předávalo ID hlášení a jako druhý zvolený jazyk. Kód aplikace (řekněme nějaký soubor index.php) pak vypadal zhruba takto:

<?
// funkce pro výpis vícejazyčných hlášení
function msg($id, $lang) {
  $result = mysql_query("SELECT lang$lang
                           FROM msg
                           WHERE id=$id");
  if ($result) {
    $row = mysql_fetch_array($result);
    mysql_free_result($result);
    echo $row[0];
  }
}

// připojení do databáze
mysql_connect("localhost", "michal", "heslo");
mysql_select_db("pokus");

// zjištění request proměnné "lang"
$lang = $_REQUEST['lang'];
if (!$lang)
  $lang = 'cz';

// použití vícejazyčné hlášky
msg(1, $lang);

// atd...
?>

Zkusíte-li si uvedený příklad doma a v prohlížeči se podíváte na výsledek skriptu index.php, mělo by se vám objevit hlášení:

Dobrý den!

Použijete-li jako URL řekněme řetězec index.php?lang=en, zobrazí se naprosto správně english message:

Hello!

Achillova pata uvedeného kódu je samozřejmě v proměnné lang. Kdo vám zaručí, že se skript index.php bude volat výhradně s hodnotami „cz“, „en“ nebo „de“ request proměnné lang? Nečiní nejmenší problém do proměnné lang dostat jakýkoliv řetězec, který se naprosto nedotčen propašuje až do SQL dotazu.

Představme si modelovou situaci, totiž že kromě tabulky msg jsou v databázi pokus také jiná data. Třeba tabulka s loginy a hesly uživatelů naší webovské aplikace:

CREATE TABLE user (
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(50),
  login VARCHAR(20),
  password VARCHAR(20),
  PRIMARY KEY(id)
);

INSERT INTO user (name, login, password)
  VALUES ("Rumcajs z Řáholce", "rumcajs", "manka");

(Jako správní zvědavci hesla neukládáme v kódovaném tvaru.) Teď už je malér na spadnutí. Stačí se trochu zamyslet a jako správný škaredý hoch/dívka formulovat následující URL pro náš děravý skript:

index.php?lang=.name%20FROM%20user%20lang%20WHERE%20id=1%20/*

Aplikace nám poslušně odpoví:

rumcajs

Vida, login prvního uživatele. Zadáme-li pro změnu něco takového:

index.php?lang=.password%20FROM%20user%20lang%20WHERE%20id=1%20/*

skriptík promptně dodá i heslo:

manka

Co přesně se stalo? V případě druhého URL se do proměnné lang uložil řetězec .password FROM user lang WHERE id=1 /*. Ve funkci msg() se tedy provedl následující SQL dotaz (pro lepší přehlednost jsem jej pouze rozdělil na více řádků):

SELECT lang.password
  FROM user lang
  WHERE id=1
/*FROM msg WHERE id=1

Praví se v něm, že chceme vrátit hodnotu atributu password relace lang z tabulky user, kterou si vtipně nazveme lang, abychom nějak „vybruslili“ se zapeklitou předponou z původního SQL dotazu. Žádáme pouze záznam s id rovným jedné. Dvojice znaků „/*“ na konci řetězce v proměnné lang zajistí, že zbytek původní části SQL dotazu bude chápán jako komentář.

A je vymalováno. Průměrnému programátorovi už nebude dělat problém napsat si skriptík, který bude v cyklu generovat URL, v nich zkoušet všechna čísla id a z výsledků „vykusovat“ loginy a hesla uživatelů.

Lék je přitom tak jednoduchý. Bohatě stačí v kódu trochu změnit podmínku konstrukce if:

if (!in_array($lang, array('en', 'de', 'cz')))
  $lang = 'cz';

Jaké z toho plyne poučení? Když se v manuálu PHP píše: „nevěřte datům přicházejícím od uživatele“, nezbývá než poslechnout a důsledně kontrolovat, přetypovávat a zajišťovat uživatelské vstupy. V tomto případě jsme získali loginy a hesla všech uživatelů. Znásilněním jiných typů SQL dotazů (UPDATE, INSERT, DELETE) můžeme docílit dalších škod. (O uživatelských vstupech a funkci eval() raději ani nemluvím… ;-)

Michal Burda

Michal Burda

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.

Školení Google+ pro firmy

DW - Školení PPC
  • Jak využít Google+ pro firemní komunikaci a marketing.
  • Čím se liší Google+ od Twitteru a Facebooku z pohledu firemního využití.
  • Jak využít Google+ v souladu s pravidly užívání.
  • Založení Google+ Page (Stránky) krok po kroku, včetně praktických tipů.

Detailní informace o školení Google+ »

Ohodnoťte jako ve škole:
Průměrná známka 3,17

Přehled názorů

Staaaaaaaraaaaa poliiiifka
Kadel 27. 11. 2003 00:38
Nový
├ 
Re: Staaaaaaaraaaaa poliiiifka
Petr Chloupek 27. 11. 2003 01:37
Nový
│
└ 
Re: Staaaaaaaraaaaa poliiiifka
Kadel 28. 11. 2003 19:55
Nový
└ 
pokazeny odkaz
fpul 27. 11. 2003 09:03
Nový
Sifrovana hesla
ogee 27. 11. 2003 05:10
Nový
├ 
Re: Sifrovana hesla
Michal Kara 27. 11. 2003 07:52
Nový
└ 
Re: Sifrovana hesla
lachtan 27. 11. 2003 07:54
Nový
 
└ 
Re: Sifrovana hesla
miro 27. 11. 2003 08:38
Nový
 
 
├ 
Re: Sifrovana hesla
Pichi 27. 11. 2003 08:54
Nový
 
 
│
├ 
Re: Sifrovana hesla
seth 27. 11. 2003 09:11
Nový
 
 
│
└ 
Re: Sifrovana hesla
Petr Mach 27. 11. 2003 14:42
Nový
 
 
│
 
└ 
Re: Sifrovana hesla
Pichi 28. 11. 2003 09:06
Nový
 
 
│
 
 
└ 
Re: Sifrovana hesla
Petr Mach 28. 11. 2003 10:54
Nový
 
 
└ 
Re: Sifrovana hesla
Daniel 27. 11. 2003 09:36
Nový
 
 
 
└ 
Re: Sifrovana hesla
Wejn 27. 11. 2003 10:23
Nový
 
 
 
 
└ 
Re: Sifrovana hesla
Petr Mach 27. 11. 2003 14:42
Nový
Sloupec pro kazdy novy jazyk??????
seth 27. 11. 2003 08:52
Nový
├ 
Re: Sloupec pro kazdy novy jazyk??????
Pichi 27. 11. 2003 08:56
Nový
├ 
Re: Sloupec pro kazdy novy jazyk??????
Lubos 27. 11. 2003 09:55
Nový
└ 
Re: Sloupec pro kazdy novy jazyk??????
zeli 27. 11. 2003 15:26
Nový
 
└ 
Re: Sloupec pro kazdy novy jazyk??????
benzin 9. 3. 2005 16:42
Nový
rozlisujte vyuku od prevadzky
hardcoder*ke 27. 11. 2003 08:59
Nový
├ 
Re: rozlisujte vyuku od prevadzky
mrak 27. 11. 2003 09:51
Nový
├ 
Re: rozlisujte vyuku od prevadzky
Wejn 27. 11. 2003 10:26
Nový
│
└ 
Re: rozlisujte vyuku od prevadzky
Petr Mach 27. 11. 2003 14:42
Nový
│
 
└ 
Re: rozlisujte vyuku od prevadzky
Adam 27. 11. 2003 16:06
Nový
│
 
 
└ 
Re: rozlisujte vyuku od prevadzky
Kaprik 27. 11. 2003 22:18
Nový
└ 
Re: rozlisujte vyuku od prevadzky
mkili 27. 11. 2003 12:48
Nový
 
└ 
Re: rozlisujte vyuku od prevadzky
Petr Mach 27. 11. 2003 14:45
Nový
 
 
└ 
Re: rozlisujte vyuku od prevadzky
Michal Kara 27. 11. 2003 15:21
Nový
Jde to i jinak
Michal Kubeček 27. 11. 2003 15:27
Nový
└ 
Re: Jde to i jinak
Jerry III 29. 11. 2003 10:51
Nový
na hovno
MQ 27. 11. 2003 16:57
Nový
├ 
Re: na hovno
Michal Kubeček 27. 11. 2003 17:27
Nový
│
└ 
Re: na hovno
MQ 27. 11. 2003 18:22
Nový
│
 
└ 
Re: na hovno
Vita 6. 12. 2003 12:41
Nový
└ 
Re: na hovno
Michal Burda 30. 11. 2003 21:18
Nový
 
├ 
Re: na hovno
MQ 6. 12. 2003 10:12
Nový
 
└ 
Re: na hovno
razor 15. 1. 2004 13:24
Nový
bez titulku
4 28. 11. 2003 02:00
Nový
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem