WordPress prý používá 27 % webu. Na následujících slajdech bych chtěl naznačit, co bychom ve WordPressu mohli zlepšit z pohledu bezpečnosti,protože když to uděláme, tak se zvýší zabezpečení poměrně hodně webů. Já vím, ne všichni aktualizují, ale o tom někdy jindy.
Bezpečnost webových aplikací Web Inkognito VŠE 05/2013
Jak zlepšit zabezpečení čtvrtiny celého webu
1. Michal ŠpačekMichal Špaček www.michalspacek.comwww.michalspacek.com @spazef0rze@spazef0rze
WordPress
Not WordPress
Jak zlepšit zabezpečení čtvrtiny celého webu.
WordPress prý používá 27 % webu. Na následujících slajdech bych chtěl
naznačit, co bychom ve WordPressu mohli zlepšit z pohledu bezpečnosti,protože
když to uděláme, tak se zvýší zabezpečení poměrně hodně webů. Já vím, ne
všichni aktualizují, ale o tom někdy jindy. Slajdy obsahují poznámky, které v
původní prezentaci nejsou.
2. V roce 2016 jsem na konferenci Passwords v Las Vegas spustil mini-projekt,
seznam firem, které nějak zveřejnily, jak ukládají hesla svých uživatelů. Chci tak
veřejně chválit firmy, které to dělají bezpečně a pokud zatím ne, tak aby začaly.
3. Způsob ukládání hesel zveřejnil i Facebook, v roce 2014 o tom přednášel Alec
Muffett. Odkaz na přednášku je samozřejmě uveden i v mém mini-projektu.
4. $this iteration_count_log2→
+ ((PHP_VERSION >= '5') ? 5 : 3;
$count = 1 << $count_log2;
Zajímalo mě, jak bezpečně WordPress hesla uživatelů ukládá. Standardně se
používá 8192 iterací osolené MD5, a to i na PHP 7. To není zrovna nejlepší
a nejbezpečnější způsob ukládání. Vlastnost $this->iteration_count_log2 je
nastavena na 8, $count je tedy ve finále 8k.
5. bcrypt
password_hash
password_verify
Bylo by fajn, kdyby WordPress na novějších verzích používal bcrypt. Od PHP 5.5 je
dostupný pomocí funkcí password_hash() a password_verify(). Další vhodné
funkce pro ukládání uživatelských hesel jsou Argon2i, scrypt, PBKDF2. Jestli
chcete vědět více, podívejte se na mojí přednášku o ukládání uživatelských hesel.
6. WordPress funguje i na PHP 5.2.4 a novější funkce tedy nelze používat (samotný
bcrypt je použitelný od PHP 5.3.7, ve starších verzích měl problém s hesly s
diakritikou). Bohužel prý existují lidé, kteří přechází z novějších verzí PHP na starší
(eh, proč?), takže nejde používat bcrypt pouze na novějších verzích a pro ty 10 let
staré verze PHP nechat nevhodnou MD5. Naštěstí existují pluginy, které umožní
WordPressu používat bcrypt, např. ten od Roots.
7. HTTPS
=
How To Transfer Private Shit
O HTTPS toho bylo již napsáno docela dost, ale evidentně to stále nestačí. Bylo by
parádní, kdyby WordPress HTTPS vyžadoval. Minimálně pro administraci, jinak se
může stát, že se do ní dostane nějaký mizera, když se v kavárně třeba nevědomky
připojíte na zákeřnou Wi-Fi a začnete editovat příspěvek. True story, bro.
8. Borek napsal na Twitter, že zakladatel WordPressu publikoval článek na Medium. Na
Valentýna. A Daniel tomu dal . Ten❤ článek je o podepisování aktualizací, to
WordPressu chybí. Operační systémy své aktualizace mají podepsané již dlouho.
9. Internet je nebezpečné místo a mohlo by se stát, že aktualizace při stahování někdo
změní a WordPress začne používat nějaký nebezpečný upravený kód. Pokud by
byly aktualizace podepsané, tak WordPress změnu detekuje a aktualizace
nenainstaluje. Naštěstí WordPress běžně neaktualizujete přes veřejnou Wi-Fi v
kavárně a tak provést takovýto útok je poměrně nákladné, útočník by totiž musel
ovládnout routery po cestě mezi vaším WordPressem a aktualizačními servery.
Podpisy navíc nepomohou pokud někdo napadne samotný aktualizační server, to si
pak upravenou aktualizaci může podepsat sám útočník a WordPress nic nepozná.
10. Zakladatel WordPressu Matt Mullenweg v článku nakonec uvedl, že podepisování
aktualizací někdy v budoucnu přidají, že je to prý dobrý nápad a že může pomoci.
11. SELECT … FROM …
WHERE name = %s
Při posílání dotazů do databáze odděluje WordPress dotaz od dat pomocí
zástupných znaků jako např. %s, data správně oescapuje a zástupné znaky jimi
nahradí. To brání útoku SQL Injection, při kterém se „z dat“ změní i samotný dotaz.
Někdy se takovému způsobu posílání dotazů říká prepared statements.
12. 123
WHERE id = ?
Jenže prepared statements fungují trochu jinak – posílají dotazy „nadvakrát“.
Nejdříve se z PHP na databázový server pošle příkaz PREPARE se zástupnými
znaky, typicky otazníky. Server si připraví vše, co pro spuštění takového dotazu
bude potřebovat a vrátí „dotaz připraven“. Teprve poté se dalším příkazem EXECUTE
odešlou data, která server umístí na správné místo v paměti a provede s nimi danou
operaci. Vzhledem k tomu, že ve chvíli přijímání dat je na serveru původní dotaz již
zparsován a zahozen, tak nelze pomocí dat ovlivnit. Je to naprosto účinná obrana
proti SQL Injection. Nevýhodou je dvojnásobná komunikace s databázovým
serverem, ale ta se setře ve chvíli, kdy chceme spouštět jeden dotaz s různými
hodnotami, stačí pak jen opakovaně volat EXECUTE. Skutečnému oddělení dat od
dotazu na úrovni serveru se občas říká serverové nebo nativní prepared statements.
13. 123WHERE
id = ?
WHERE id = 123
WordPress místo toho používá tzv. emulované prepared statements. Já mám raději
termín vázání proměnných, přesněji vyjadřuje způsob práce s dotazy. Vázání
proměnných funguje tak, že PHP samo správně ošetří data a nalepí je na určené
zástupné místo v dotazu, ve WordPressu jsou to %s, %d a další. Poté se na
databázový server odešle celý dotaz najednou, včetně správně ošetřených hodnot.
Výhoda oproti opravdovým prepared statements je v rychlosti pro neopakující se
dotazy – se serverem stačí komunikovat jen jednou. Programátor ale ani tak nemusí
myslet na ruční escapování, takže je to takový rozumný kompromis.
14. Toto je kus kódu, který se právě o escapování stará. Vázání proměnných je
implementováno v metodě prepare(), najdete ji ve třídě wpdb. Všimněte si volání
vsprintf() na konci, to je ta funkce, která nahradí zástupné znaky jako %s za
vlastní hodnoty. Celý dotaz včetně hodnot se pak odešle metodou query(), volanou
například z get_row() a dalších.
15. xBF' UNION …
mysql_escape_string()
xBFx5C' UNION …Při používání emulovaných prepared statements je i přesto možné provést útok SQL
Injection, pokud se nastaví špatně znaková sada. Escapátor (přesněji databázový
klient, tedy PHP) nemusí pak vědět, v jaké znakové sadě jsou data, která má
ošetřovat. Ve znakové sadě GBK existuje dvoubajtový znak, který končí zpětným
lomítkem (hexadecimálně 5C), takže když se escapováním před apostrof přidá, tak
je na serveru zase požrán předchozím bajtem. Server ho „nevidí“, protože je
součástí toho dvoubajtového znaku. Podívejte se na můj příklad, který ukazuje
správné i špatné nastavení znakové sady. WordPress znakovou sadu nastavuje, ale
i tak by bylo lepší, kdyby byla možnost používat nativní prepared statements.
16. Content Security Policy (CSP) je vcelku nová věc. No, nová… Původní nápad vznikl
v roce 2004, první verze standardu v roce 2012. Pomocí CSP můžete vytvořit
seznam zdrojů, které může prohlížeč do stránky načíst, tzv. whitelist. Prohlížeči
seznam předáte ve formě HTTP hlavičky, kterou odešlete ze serveru.
17. Content-Security-Policy: default-src 'self'
Toto je asi nejjednodušší varianta CSP. Direktiva default-src s hodnotou 'self'
říká, že do stránky se mohou načíst pouze zdroje z aktuální domény, resp. originu.
Tedy i kdyby se útočníkovi do HTML kódu podařilo vložit obrázek nebo značku
<script src=…>, tak browser nic takového vůbec nebude stahovat. CSP je druhá
úroveň obrany proti Cross-Site Scriptingu a dalším podobným útokům. Když něco
zapomenete ošetřit, tak Content Security Policy může vaše uživatele ochránit.
18. Content-Security-Policy:
default-src 'self';
img-src 'self' https://www.google-analytics.com;
script-src 'self' 'unsafe-inline'
Takto poslaná HTTP hlavička browseru říká, že defaultně má zdroje načítat jen z
aktuálního originu. Pomocí img-src rozšíříme možnost načítat obrázky navíc
z https://www.google-analytics.com. JavaScript může načíst zase jen z
aktuálního originu a navíc může spouštět i tzv. inline skripty, tedy vše, co je mezi
značkou <script> a </script>, a vše v atributech onclick a dalších podobných.
19. CSP3
Content Security Policy
Level 3
Jenže vyjmenovávat všechny zdroje je běh na dlouhou trať, navíc při používání
nástrojů jako např. Google Tag Manager dopředu ani neznáme, jaké zdroje do
stránky budeme načítat. Nová verze CSP toto řeší, nasazení na nové i existující
weby je o dost jednodušší. Zvyšuje se tak šance, že to výrobci webů udělají a tím
své návštěvníky zase o něco více ochrání. Bylo by parádní, kdyby WordPress obsah
hlavičky Content-Security-Policy uměl jednoduše vygenerovat nebo nastavit.
20. Content-Security-Policy:
script-src 'strict-dynamic' 'nonce-rAnd0m123'
HTML:
<script src=… nonce="rAnd0m123">
…
</script>
CSP3 přidává podporu pro 'strict-dynamic'. Tato hodnota říká, že když už je
jednou skript „povolený“, tak může do stránky přidávat další skripty a ty to povolení
automaticky zdědí. V případě použití 'strict-dynamic' je ale dostupná jen jediná
možnost, jak skripty povolit: označení pomocí atributu nonce. Jeho hodnota by měla
být jiná pro každé načtení stránky. Tou hodnotou můžeme označit více skriptů v
HTML kódu, do CSP hlavičky ji pak přidáme pomocí 'nonce-…'. Celé si to můžete
vyzkoušet na mé stránce s příklady.
21. Content-Security-Policy:
script-src 'strict-dynamic' 'nonce-rAnd0m123'
'unsafe-inline' http: https:;
object-src 'none';
report-uri https://report-uri.io/r/…
Univerzální hlavička vypadá takto. Browsery s podporou CSP3 použijí 'strict-
dynamic' a 'nonce-…', ty ostatní pak buď jen 'nonce-…' nebo ty další direktivy.
object-src 'none' zakazuje načítání pluginů, Cross-Site Scripting lze někdy
provést i pomocí nich. Prohlížeče s podporou CSP umí navíc posílat informace
o tom, kdy a kde byla jaká politika porušena. Pro zobrazování reportů použijte
report-uri.io, vygenerujte si tam adresu, na kterou reporty budete posílat a tu pak
použijte v direktivě report-uri.
22. csp-evaluator.withgoogle.com
Content Security Policy vypadá celkem složitě, co? Když to nastavíte blbě, tak se
bude taková politika dát obejít. Jenže co to je blbě? Podívejte se na CSP validátor
od Google (mají i extenzi pro Chrome), zadejte tam URL stránek nebo rovnou HTTP
hlavičku a hned se dozvíte, jak jste na tom. Je tam i příklad bezpečného nastavení.
23. Michal ŠpačekMichal Špaček
www.michalspacek.comwww.michalspacek.com @spazef0rze@spazef0rze
Možností, jak by se dal WordPress z hlediska zabezpečení vylepšit je mnoho. Na
stránce projektu CMS Airship naleznete srovnání bezpečnostních fíčur známých
a rozšířených CMS, něco z toho si vyberte a začněte na tom makat. Díky!
Editor's Notes
Kdybychom znakovou sadu nenastavili a databáze byla v GBK, tak by se tohle oescapovalo na tohle, prostě před apostrof se dá zpětné lomítko, což vypadá jako ok, jenže BF 5C je validní čaj, jeden vícebajtovej znak, takže zas nám tam zůstává
Při nastavení správné sady a použití real_escape_string se dá zpětné lomítko i před BF