Pokud se do databáze mají ukládat extrémně citlivé údaje (jako např. čísla kreditních karet), stojí za zvážení nespoléhat se na zabezpečení serveru, kde databáze běží, a přidat další úroveň v podobě šifrování dat. Pokud se ukládají data, u kterých není nutné znát přesnou hodnotu a stačí ověřovat jejich platnost (jako jsou např. hesla), stačí ukládat jejich hash, jinak je potřeba šifrovat.
Šifrovací funkce jsou v PHP k dispozici prostřednictvím knihoven Mcrypt nebo OpenSSL. Pokud místo nich využijeme šifrovací funkce poskytované databází, získáme dvě výhody:
MySQL nabízí tři dvojice funkcí: ENCODE+DECODE, AES_ENCRYPT+AES_DECRYPT a DES_ENCRYPT+DES_DECRYPT. Každá používá jinou šifru, jejich použití je ale v zásadě stejné (až na drobné rozdíly v potřebné délce pro uložení zašifrovaného řetězce).
Zbývá zvolit klíč, kterým budeme data šifrovat. Vzhledem k tomu, že si ho nebude muset nikdo pamatovat, je možné vygenerovat klíč náhodný a dostatečně dlouhý. Možnosti jeho uložení jsou široké:
Za většiny okolností mi přijde nejlepší kombinace prvního a čtvrtého způsobu – půlku hesla mít uloženou ve zdrojácích a půlku v databázi. Uživatelé budou uloženi v tabulce admin(login varchar(20), heslo_sha1 char(40), master_encoded tinyblob)
a jednotlivé operace mohou vypadat třeba takhle:
<?php // přihlášení uživatele $row = mysql_fetch_assoc(mysql_query("SELECT DECODE(master_encoded, '" . mysql_real_escape_string($_POST["heslo"]) . "') AS master FROM admin WHERE login = '" . mysql_real_escape_string($_POST["login"]) . "' AND heslo_sha1 = SHA1('" . mysql_real_escape_string($_POST["heslo"]) . "')")); if ($row) { // přihlášení se podařilo $_SESSION["master"] = $row["master"]; define("MASTER2", "druhá polovina hesla"); } // získání zašifrovaných dat mysql_query("SELECT DECODE(kreditka_encoded, '" . mysql_real_escape_string($_SESSION["master"] . MASTER2) . "') AS kreditka FROM zakaznici WHERE id = " . intval($_GET["id"])); // přidání nového uživatele mysql_query("INSERT INTO admin (login, heslo_sha1, master_encoded) VALUES ('" . mysql_real_escape_string($_POST["login"]) . "', SHA1('" . mysql_real_escape_string($_POST["heslo"]) . "'), ENCODE('" . mysql_real_escape_string($_SESSION["master"]) . "', '" . mysql_real_escape_string($_POST["heslo"]) . "'))"); ?>
Typ tinyblob se pro ukládání zašifrovaných dat používá proto, že byť MySQL umožňuje do typu varchar uložit i binární data, ořezává koncové mezery, což může v tomto případě zásadně vadit. Kvůli ukládání části hesla do session proměnné je vhodné ukládat session data raději pouze do paměti.
Pokud je část klíče uložena v databázi zašifrovaná heslem uživatele, může s citlivými údaji pracovat pouze přihlášený uživatel. Pro čtení je to pochopitelný požadavek, ale zápis by měl být umožněn i anonymním uživatelům, kteří data vyplňují. Ke slovu přichází asymetrická kryptografie. Ta bohužel v MySQL není implementovaná, takže musíme sáhnout po některé knihovně PHP, např. OpenSSL:
<?php // uložení citlivé informace openssl_public_encrypt($_POST["kreditka"], $crypted, PUBLIC_KEY); // přihlášení uživatele $pkey = openssl_pkey_get_private($row["private_key"], $_POST["heslo"]); openssl_pkey_export($pkey, $_SESSION["private_key"]); // přidání nového uživatele nebo změna hesla openssl_pkey_export($_SESSION["private_key"], $private_key, $_POST["heslo"]); // získání citlivé informace openssl_private_decrypt($row["kreditka"], $decrypted, $_SESSION["private_key"]); ?>
Při ukládání do databáze je nutné s daty pracovat jako s binárními. Prvotní soukromý a veřejný klíč lze vygenerovat i pomocí funkcí PHP funkcí openssl_pkey_new a openssl_pkey_get_public.
Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací.
Diskuse je zrušena z důvodu spamu.