0
Jak jsme zlepšili
zabezpečení Slevomat.cz
Michal Špaček
@spazef0rze
www.michalspacek.cz
Tyto slajdy obsahují poznámky neje...
$this context→
Slevomat má ~1M zákazníků, v databázi ~1.8M uživatelských účtů a web slevomat.cz denně navštíví přes 200k
n...
Dříve bylo možné se přihlásit z jakékoliv stránky – po kliknutí na tlačítko vpravo nahoře se rovnou objevil přihlašovací
f...
Web slevomat.cz zatím neběží celý na HTTPS, takže ačkoliv se případný mizera nedostane k přihlašovacím údajům
změnou formu...
Při relativně velké zátěži webových serverů Slevomatu nechceme přechod na zabezpečené HTTPS udělat najednou, ale
raději po...
HTTP Strict Transport Security
Strict-Transport-Security:
max-age=31536000; includeSubDomains
Až bude celý web přístupný p...
Přenos hesel a session ids tedy bude zabezpečen docela dobře. K uživatelským údajům se ale dá dostat i jinak, než
odposlec...
$dibi
→select('jmeno')
where(→ "id = $_GET[id]");
Ale nemusí to být jenom zálohy na nepatřičných místech. Firemní údaje a ...
8b8f05049b2114ad3d33
0db391e78549670d6b4b
Slevomat dříve heslo před uložením zahashoval pomocí algoritmu SHA-1, aby nebylo...
cHJvZDE=;GwEuCSSgBCL
x0BHHAAb8IA==;kvmDV/
DEJ3H+how7bErLa+/xap
V2282MmISXqVo8xREfnB
KcNmAixIaDDnYkODCu9K
W3+6sbM04W7Tm3w1S...
password_hash(…, PASSWORD_DEFAULT)
ircmaxell/password_compat
Pro pouhé hashování hesel algoritmem bcrypt použijte funkci p...
mcrypt_encrypt()
MCRYPT_RIJNDAEL_128
Pro následné šifrování hashů hesel pomocí AES-256 v PHP použijte funkce mcrypt_encryp...
aes(bcrypt(sha1(…)))
aes(bcrypt(…))
Ve Slevomatu jsme chtěli zabezpečit i hesla uživatelů, kteří se dlouho nepřihlásili. V...
bcrypt(token)
Pomocí funkce password_hash() hashujeme i tokeny pro semi-permanentní přihlášení i pro přihlášení pomocí
Fac...
Funkci Zapomenuté heslo máme vyřešenu tak, že uživateli posíláme odkaz, který obsahuje token, jehož platnost je
časově ome...
Vaše heslo je: •••••
Hesla nově nikdy neposíláme e-mailem, ani při registraci. Veškeré změny logujeme a víme kdo, kdy a z ...
Content Security Policy
Ve Slevomatu používáme šablonovací systém Latte, který je součástí Nette Frameworku. Latte automag...
<script>
var foo = 123;
var color = 'purple';
</script>
Posílat hodnotu 'unsafe-inline' rozhodně chcete, zakázáním spouště...
<script id="conf" type="text/json">
{
"foo" = "123",
"color" = "purple"
}
</script>
<script src="code.js"></script>
Konfig...
$c = stream_context_create(array(
'ssl' => array(
'verify_peer' => true,
'verify_depth' => 9,
'cafile' => $caFile,
'disabl...
JDE TO!
www.michalspacek.cz
V zabezpečování Slevomatu a uživatelských dat budeme samozřejmě pokračovat dále, ještě nás ček...
Upcoming SlideShare
Loading in...5
×

Zabezpečení Slevomatu

3,952

Published on

Jak jsme zlepšili zabezpečení Slevomatu.

Chceš zlepšit zabezpečení webu a nevíš kde začít a kdy skončit? Ukážu ti, co jsme udělali na Slevomatu, co všechno jsme museli vyřešit, čemu jsme se divili a co plánujeme. Třeba tě to trochu taky nakopne.

Published in: Internet
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,952
On Slideshare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
25
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Transcript of "Zabezpečení Slevomatu"

  1. 1. Jak jsme zlepšili zabezpečení Slevomat.cz Michal Špaček @spazef0rze www.michalspacek.cz Tyto slajdy obsahují poznámky nejen pro ty, kteří na přednášce nebyli.
  2. 2. $this context→ Slevomat má ~1M zákazníků, v databázi ~1.8M uživatelských účtů a web slevomat.cz denně navštíví přes 200k návštěvníků. Slevomat v roce 2013 dosáhl miliardového obratu, má vlastní značku zboží i vlastní sklad. Ve Slevomatu pracuje 150 lidí, vývojové oddělení čítá 10 hlav, mezi nimi např. @kukulich, @vasekpurchart nebo @patrikvotocek. Nechceš se k nim přidat? Slevomat používá PHP 5.5, Nette 2.1, Elasticsearch a MySQL resp. Percona Server, který obslouží ~2.5M dotazů za hodinu. Čísla kreditních karet nejsou uložena v databázi Slevomatu, pamatuje si je platební brána. Uživatelé mohou na svých účtech mít kredity, kterými mohou platit, ale nedají se převádět na jiného uživatele.
  3. 3. Dříve bylo možné se přihlásit z jakékoliv stránky – po kliknutí na tlačítko vpravo nahoře se rovnou objevil přihlašovací formulář a to i přesto, že stránka přišla po nezabezpečeném HTTP. Formulář se sice odesílal šifrovaně pomocí HTTPS, ale mohl být po cestě do prohlížeče změněn tak, aby se odesílal někam úplně jinam. Přihlašování jsme tedy přesunuli na samostatnou stránku, která se stahuje po HTTPS, obsah formuláře tedy do prohlížeče dorazí v původním stavu.
  4. 4. Web slevomat.cz zatím neběží celý na HTTPS, takže ačkoliv se případný mizera nedostane k přihlašovacím údajům změnou formuláře po cestě, může se odposlechem dostat k session id cookie, protože ta se přenáší i po nezabezpečeném HTTP. Až bude celý web přístupný pouze po HTTPS, cookie označíme jako Secure a tím zajistíme, že se bude přenášet jen po šifrovaném HTTPS a nepůjde odposlechnout. Direktivy session.cookie_httponly a session.use_only_cookies pro obranu před útoky na session máme samozřejmě zapnuté už docela dlouho. session.cookie_secure
  5. 5. Při relativně velké zátěži webových serverů Slevomatu nechceme přechod na zabezpečené HTTPS udělat najednou, ale raději postupně přesunujeme jednotlivé části webu. Velmi brzy bude celý web přístupný jen a pouze přes HTTPS. A pak zas můžeme vrátit přihlašovací formulář do Lightboxu na každou stránku.
  6. 6. HTTP Strict Transport Security Strict-Transport-Security: max-age=31536000; includeSubDomains Až bude celý web přístupný pouze pomocí HTTPS, budeme posílat hlavičku Strict-Transport-Security, která zajístí to, že se prohlížeč ani nebude pokoušet server kontaktovat po HTTP a rovnou bude po stanovenou dobu všechny odkazy sám převádět na HTTPS. Zabráníme tím man-in-the-middle útokům, které převádějí šifrované HTTPS spojení na obyčejné HTTP. Hlavičku podporuje většina moderních browserů, IE ji bude podporovat od verze 12.
  7. 7. Přenos hesel a session ids tedy bude zabezpečen docela dobře. K uživatelským údajům se ale dá dostat i jinak, než odposlechem. Třeba tak, že jednoho krásného dne najdete na serveru 12 GB záloh cizích databází, protože je tam někdo kdysi nahrál a nesmazal. Opravdu se to stává a je lepší počítat s tím, že i vaše databáze se může někde objevit.
  8. 8. $dibi →select('jmeno') where(→ "id = $_GET[id]"); Ale nemusí to být jenom zálohy na nepatřičných místech. Firemní údaje a hesla uživatelů se z databáze dají vysosat třeba úspěšným provedením útoku SQL Injection. Spousta uživatelů používá jedno heslo na více místech a když z jednoho takového místa to heslo unikne, tak … raději nedomýšlet. A ano, SQL Injection lze provést i v Nette, Dibi a PDO, pokud se tyto nástroje použijí trochu nešikovně. Žádný nástroj automagicky nevyřeší zabezpečení webu.
  9. 9. 8b8f05049b2114ad3d33 0db391e78549670d6b4b Slevomat dříve heslo před uložením zahashoval pomocí algoritmu SHA-1, aby nebylo tak jednoduché ho získat, pokud by se k databázi nějakým způsobem dostal někdo nepovolaný. Heslo nebylo saltované, takže nebylo chráněno proti tzv. narozeninovým útokům, ani proti použití předpočítaných tabulek. Jenže ty se už dnes stejně moc nepoužívají, hesla se crackují hrubou silou, slovníkovými nebo hybridními útoky. Každý obyčejný laptop umí spočítat desítky miliónů SHA-1 hashů za vteřinu a moderní grafické karty jsou stokrát rychlejší. Cracknout takové běžné heslo hashované SHA-1 je tedy otázkou maximálně pár minut a ani salt by crackování nezpomalil. Zkuste si to sami pomocí nástroje oclHashcat.
  10. 10. cHJvZDE=;GwEuCSSgBCL x0BHHAAb8IA==;kvmDV/ DEJ3H+how7bErLa+/xap V2282MmISXqVo8xREfnB KcNmAixIaDDnYkODCu9K W3+6sbM04W7Tm3w1S+NQ== Nyní Slevomat ukládá hesla tak, že heslo zahashuje relativně pomalým algoritmem bcrypt, aktuálně s cost parametrem 10. Výsledný hash se navíc zašifruje pomocí AES-256-CBC a poté se uloží do databáze. Je to klacek pod nohy, pravděpodobnost, že se mizera dostane k databázi a zároveň ke konfiguračnímu souboru aplikace, ve kterém je uložen šifrovací klíč, je mnohem menší, než že se dostane jenom k databázi. A pokud získá databázi i klíč, tak po rozšifrování má před sebou pořád ty bcrypt hashe. Výše je mé aktuální heslo tak, jak je uloženo v produkční databázi Slevomatu. Všimněte si id klíče, inicializačního vektoru a šifrovaného hashe, vše odděleno středníkem a zakódováno do Base64.
  11. 11. password_hash(…, PASSWORD_DEFAULT) ircmaxell/password_compat Pro pouhé hashování hesel algoritmem bcrypt použijte funkci password_hash() a pro ověření password_verify(). Tyto funkce jsou dostupné až od PHP 5.5, ale pro starší PHP (od PHP 5.3.7) existuje knihovna password_compat, která tyto funkce implementuje v čistém PHP. Najdete ji na GitHubu nebo ji můžete nainstalovat pomocí Composeru. Jednoduchý příklad najdete na https://github.com/spaze/encrypt-hash-password-php v souboru example-hash.php.
  12. 12. mcrypt_encrypt() MCRYPT_RIJNDAEL_128 Pro následné šifrování hashů hesel pomocí AES-256 v PHP použijte funkce mcrypt_encrypt() a mcrypt_decrypt() z rozšíření mcrypt. Bloková šifra AES je variantou šifry Rijndael s velikostí bloku 128 bitů, použijeme tedy konstantu MCRYPT_RIJNDAEL_128. Hodnota 256 v názvu znamená velikost použitého šifrovacího klíče v bitech. Pro ukládání hashů je vhodný režim CBC (Cipher-block chaining), ten při volání mcrypt_encrypt() zvolíme pomocí MCRYPT_MODE_CBC. Inicializační vektor vytvoříme funkcí mcrypt_create_iv() s parametrem MCRYPT_DEV_URANDOM. Jednoduchý příklad najdete opět na GitHubu: https://github.com/spaze/encrypt-hash-password-php v souboru example-encrypthash.php. Pro obecné šifrování dat raději použijte knihovnu https://github.com/defuse/php-encryption, která přidává autentizaci zašifrovaných dat pomocí metody Encrypt-then-MAC.
  13. 13. aes(bcrypt(sha1(…))) aes(bcrypt(…)) Ve Slevomatu jsme chtěli zabezpečit i hesla uživatelů, kteří se dlouho nepřihlásili. Vzali jsme tedy stávající SHA-1 hashe, zahashovali bcryptem a pak zašifrovali AES-256. Taková hesla jsme si označili, abychom při ověřování věděli, že z hesla zadaného uživatelem máme nejdříve udělat SHA-1 hash a až pak s ním pracovat dále. Když se takový uživatel úspěšně přihlásí, tak zadané heslo v čitelné podobě zahashujeme jen bcryptem s vynecháním SHA-1, hash zašifrujeme a výsledek uložíme do databáze. To děláme i při nastavení hesla nového. Mezikrok s SHA-1 je lepší, než vynucená změna hesla všech uživatelů, je ale zbytečný a pokud můžeme, tak ho odstraníme.
  14. 14. bcrypt(token) Pomocí funkce password_hash() hashujeme i tokeny pro semi-permanentní přihlášení i pro přihlášení pomocí Facebooku. V čitelné podobě je v databázi nemáme. Token z cookie pak ověřujeme stejně jako heslo při přihlašování, tedy funkcí password_verify(). Hash tokenu v databázi nalezneme podle identifikátoru uživatele z cookie.
  15. 15. Funkci Zapomenuté heslo máme vyřešenu tak, že uživateli posíláme odkaz, který obsahuje token, jehož platnost je časově omezena – momentálně na 30 minut od odeslání odkazu. Tento interval by neměl přesáhnout 60 minut. E-mail nově obsahuje i odkaz pro zneplatnění tokenu. Token hashujeme bcryptem, v databázi není uložen v čitelné podobě. Zákaznické podpoře jsme odebrali možnost nastavovat hesla uživatelům v administraci Slevomatu, ale pro některé domény jsme tuto možnost museli vrátit zpět. Centrum.cz totiž doručuje některé e-maily s mnohahodinovým zpožděním a uživatel tak prošvihne interval, během kterého je token platný a o pomoc pak žádá právě zákaznickou podporu.
  16. 16. Vaše heslo je: ••••• Hesla nově nikdy neposíláme e-mailem, ani při registraci. Veškeré změny logujeme a víme kdo, kdy a z jaké IP adresy změnil heslo nebo e-mailovou adresu. Uživatele po změně informujeme, pro jistotu. Brzy chceme změnit politiku vytváření hesel, současné omezení na min. 5 znaků je dnes již nevyhovující. Nová hesla by měla mít minimálně 8 znaků. Maximální povolená délka hesla je 4096 znaků, delší hesla příliš zatěžují server při hashování bcryptem.
  17. 17. Content Security Policy Ve Slevomatu používáme šablonovací systém Latte, který je součástí Nette Frameworku. Latte automagicky ošetřuje vypisované proměnné, ale i přesto se může stát, že uděláte chybu, třeba při tvorbě vlastních maker, a váš web bude zranitelný pomocí útoku Cross-Site Scripting (XSS). Ke snížení dopadu případného úspěšného útoku bude Slevomat brzy posílat HTTP hlavičku Content-Security-Policy, která vyjmenovává zdroje (whitelisting), odkud může prohlížeč do stránky vkládat obrázky, kaskádové styly, JavaScript a další. Browser nebude spouštět ani inline JavaScript, pokud se pošle i hodnota 'unsafe-inline', JavaScriptový kód pak musí být v externích souborech. Na přesunu pilně pracujeme.
  18. 18. <script> var foo = 123; var color = 'purple'; </script> Posílat hodnotu 'unsafe-inline' rozhodně chcete, zakázáním spouštění JavaScriptu vloženého přímo do stránky zabráníte úspěšnému provedení útoku Cross-Site Scripting i kdyby někdo nějak do stránky nějaký JavaScript vložil. Občas se přímo do stránky úmyslně vkládá nějaká konfigurace, ovšem pokud je to uděláno výše uvedeným způsobem, tak to přestane fungovat. S 'unsafe-inline' momentálně nefunguje ani kód Google Tag Manageru, a to ani s využitím experimentální podpory nonce-source z připravované specifikace CSP 1.1 v Chrome 35. Slevomat používá Google Tag Manager a 'unsafe-inline' je pro nás tedy momentálně nepoužitelné.
  19. 19. <script id="conf" type="text/json"> { "foo" = "123", "color" = "purple" } </script> <script src="code.js"></script> Konfiguraci ve stránce je možné zachovat, jen je potřeba změnit typ obsahu značky SCRIPT na text/json. JSON není kód, nelze spustit a browser ho tedy při 'unsafe-inline' nemusí blokovat. V code.js se pak k hodnotám dostanete takto: var c = document.getElementById('conf'); var d = JSON.parse(c.textContent || c.innerHTML);.
  20. 20. $c = stream_context_create(array( 'ssl' => array( 'verify_peer' => true, 'verify_depth' => 9, 'cafile' => $caFile, 'disable_compression' => true, ) )); Pokud potřebujeme komunikovat s nějakým API, používáme k tomu šifrovaný protokol HTTPS. Pro bezpečnou komunikaci musíme také ověřovat, že opravdu komunikujeme třeba s bankou a že to není nějaký mizera, který se za banku vydává. Vypnutím TLS komprese se pak bráníme proti útoku CRIME. Takto bude defaultně nastaveno i PHP 5.6+.
  21. 21. JDE TO! www.michalspacek.cz V zabezpečování Slevomatu a uživatelských dat budeme samozřejmě pokračovat dále, ještě nás čeká spousta práce, ale když se chce, tak to jde. Snad jsem vás také trochu inspiroval a pokud byste chtěli se zabezpečením webů a aplikací pomoci, napište mi, rád pomohu. Kontakty jsou uvedeny na mém webu. Díky!
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×