Na konci srpna byla v oblíbeném PHP frameworku Nette objevena a obratem opravena zákeřná chyba. Přestože autor frameworku, David Grudl, udělal snad všechno možné i nemožné, někteří se o ní nedozvěděli včas a nestihli tak aktualizovat své weby a webové aplikace. Prozradím vám pár tipů nejen pro PHP, díky kterým dostanete echo o podobných problémech mezi prvními.
Chybu, kterou objevil Cyku Hong z Taiwanu, může útočník za určitých okolností využít ke vzdálenému spuštění kódu na některých webech pomocí speciálně sestaveného URL. Takové chyby spadají do kategorie Remote Code Execution (RCE) a zažijete s nimi spousty legrace, nebo možná taky ne, to podle toho, na které straně barikády stojíte.
David Grudl, tvůrce Nette, s chybou zatočil správně: opravené verze balíčků vzhledem k závažnosti té chyby vydal nejdříve „potichu“, přičemž opravil i již dávno nepodporované vykopávky typu Nette 2.0, poslal e-mail podporovatelům, měsíc a něco počkal a teprve poté informaci uveřejnil na blogu, na diskuzním fóru a v seznamu Common Vulnerabilities and Exposures, ve kterém dostala číslo CVE-2020-15227. (A když si něčím nebyl jistý, tak se mě zeptal 🤓)
Část upozornění, které podporovatelé dostali už 25. srpna
Týden po zveřejnění se začaly objevovat nejen nástroje na otestování i demonstraci zranitelnosti („Proof-of-Concept“, PoC), ale i vývojáři, kteří chybu do té doby z různých důvodů zatím nezaregistrovali. Když jsem to viděl, tak mě napadlo, že by řešení problému mohl pomoci jednoduchý skript, který na serveru najde zranitelné soubory a příp. je rovnou opraví náhradou jednoho řádku kódu za jiný.
Cílem byly převážně různé webhostingové firmy, které by mohly vytipovat své zákazníky a přímo je informovat, nebo jim to rovnou opravit. Napsal jsem 2 prográmky v shellu (fungují v Bashi v Linuxu i FreeBSD), David to později reimplementoval i v PHP. Lukáš V. mi dokonce za ty skripty jen tak, sám od sebe, poslal nějaké to kilčo, dík!
V Active24 chybu našli a opravili na více než 2500 webů, podobně reagoval i Blueboard, WEDOS a další
Tohle samo o sobě už by vám mohlo dát pár nápadů jak postupovat poté, co vám někdo ve vašem frameworku, balíčku nebo knihovně najde nějakou docela závažnou bezpečnostní chybu. Neméně důležité je ale informaci o chybě a nutnosti aktualizovat dostat také k uživatelům vašeho výtvoru. A jak se o tom mohou dozvědět co nejrychleji?
Pokud používáte nějaké knihovny nebo frameworky jako např. Nette nebo cokoliv jiného, tak máte několik možností, jak se dozvědět, že fakt musíte aktualizovat a tohle je asi ta nejdůležitější.
Podporujte vývojáře nástrojů, které používáte. Vy budete mít dobrý pocit a ti vývojáři, kromě jiného, budou na vás mít nějaký kontakt a mohou vám napsat, že byste měli aktualizovat. Win-win situace. Když jsme začali chybou v Nette, tak osobně Nette podporuji, v seznamu přispěvovatelů mě nejspíš nepřehlédnete. Najdete tam i Scotta Helmeho, protože Nette mj. používáme i na Report URI, na kterém s ním pracuji. David Grudl podporovatelům poslal info o tom, že by měli aktualizovat už 25. srpna, pár hodin po vydání opravených verzí verze, a o měsíc a něco dříve, než informace o chybě zveřejnil.
Sledujte twittery a blogy vývojářů a diskuzní fóra (viz příspěvek na blogu Nette a na diskuzním fóru). Pokud navíc vývojáři používají GitHub, tak ten vám může poslat notifikaci klidně i jen při vydání nové verze nebo při objevení bezpečnostního problému:
Můžete „watchovat“ jen nové verze nebo bezpečnostní upozornění (co všechno sledujete)
Pokud vyvíjíte na GitHubu a spravujete závislosti pomocí Composeru, tak můžete využít Dependabota – robota, který hlídá a automaticky aktualizuje všechny vaše závislosti. Dependabot zvládá nejen PHP, ale i jiné jazyky a správce balíčků. Nastavíte ho ve vašem repozitáři v menu Settings > Security & analysis:
Prozkoumejte i sekci Security > Dependabot alerts, ve které najdete všechna otevřená i vyřešená upozornění, která Dependabot vytvořil:
Tohle upozornění jsem kliknutím na Dismiss pustil z hlavy, kód nikde neběží
Jsou tam i odkazy na automaticky vytvořené pull requesty, viz příklad. Tenhle opuštěný projekt naštěstí nikde neběží, balíček nette/application zde tedy nemusím aktualizovat, pull request chci nechat v tomto stavu, abyste viděli, jak to případně bude vypadat u vás.
Automaticky vytvořený pull request s aktualizací závislosti
Dependabot také umí kontrolovat nové verze samotných actions, viz moje konfigurace, automaticky vytvořený pull request pro update jedné takové akce a dokumentace.
Kdybyste někdy potřebovali získat číslo CVE i pro nějaký váš výtvor a informaci o chybě tak dostat „do oběhu“, tak se vám bude hodit sekce Security Advisories. V seznamu zveřejněných upozornění balíčku nette/application najdete i ono CVE-2020-15227. Podobný záznam je i v databázi všech GitHubových upozornění.
GitLab také nabízí skenování závislostí, ale pouze v nejdražší placené verzi. Alternativami k Dependabotu jsou např. Snyk nebo Renovate.
Dalším nástrojem, který vám může pomoci odhalit neaktualizované PHP knihovny s bezpečnostními chybami je balíček Roave Security Advisories. Pojďme si ho ukázat v praxi, opět na mém starém a nepoužívaném projektu. Nejdříve nainstalujeme závislosti uvedené v souboru composer.lock
, přičemž vhodnější by bylo použít composer update
, ale tím bychom nainstalovali již opravenou verzi a to v tomto případě nechceme:
$ composer install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file [...] - Installing nette/application (v2.3.7): Downloading (100%) [...]
Verze 2.3.7 obsahuje bezpečnostní chybu (a to v řadě 2.3 až do verze 2.3.13 včetně), takže instalace balíčku Roave Security Advisories pomocí
composer require --dev roave/security-advisories:dev-latest
selže s tím, že jakože konflikt, protože nette/application máme ve verzi 2.3.7:
$ composer require --dev roave/security-advisories:dev-latest ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Your requirements could not be resolved to an installable set of packages. Problem 1 - roave/security-advisories dev-latest conflicts with nette/application[v2.3.7]. - roave/security-advisories dev-latest conflicts with nette/application[v2.3.7]. - roave/security-advisories dev-latest conflicts with nette/application[v2.3.7]. - Installation request for roave/security-advisories dev-latest -> satisfiable by roave/security-advisories[dev-latest]. - Installation request for nette/application (locked at v2.3.7, required as ~2.3.1) -> satisfiable by nette/application[v2.3.7]. Installation failed, reverting ./composer.json to its original content.
Museli bychom nejdříve aktualizovat nette/application na 2.3.14 nebo novější a pak půjde nainstalovat i roave/security-advisories:dev-latest
. Od této chvíle nepůjde pomocí composer require
nebo composer update
nainstalovat žádný balíček se známou bezpečnostní chybou.
Roave Security Advisories nemá žádný kód, veškeré jeho know-how spočívá v sekci conflict
v souboru composer.json
, ve které jsou uvedené verze PHP balíčků s nějakou známou bezpečnostní chybou. Ty se tam dostávají z PHP Security Advisories Database (viz Contributing), která jde používat několika dalšími způsoby. Jde používat dokonce i jako GitHub Action (viz můj příklad), takže se kontrola bude provádět například při každém pushi (a v mém příkladu i jednou za hodinu).
Od Composeru 2.4 lze využít i příkaz composer audit
.
Bylo by skvělé, kdyby tuto databázi uměly pro upozorňování svých zákazníků nějak využít i klasické sdílené hostingy.
disable_functions
Bránit se proti chybám, jako je právě vzdálené spuštění kódu, asi nejde jinak než včasnou aktualizací. Jasně, můžete si někde na firewallu zablokovat nějakou konkrétní URL adresu, ale to je spíš reaktivní opatření. Můžete ale aspoň zásadně snížit dopad útoku aneb je blbý, když někdo ve vaší aplikaci spustí nějaký PHP kód, ale je ještě mnohem blbější, když pomocí PHP spustí nějaký prográmek z příkazové řádky. Důrazně bych tedy doporučoval zakázat v PHP volání funkcí, které takové prográmky mohou spouštět. Jsou to tyto:
exec
– spustí externí programpassthru
– spustí externí program a zobrazí výstup včetně binárních datproc_open
– spustí program a vrátí ukazatele na vstup a výstupshell_exec
– spustí program pomocí shellu a vrátí výstup jako řetězecsystem
– spustí externí program a zobrazí výstuppcntl_exec
– spustí program v prostoru aktuálního procesupopen
– vrátí ukazatel na spuštěný procesZakázat je můžete jen v php.ini
(a to dokonce jen pro určitý server nebo cestu) pomocí direktivy disable_functions
nebo v konfiguraci FPM poolu pomocí php_admin_value[disable_functions]
, viz příklad.
Pro kontrolu, jestli náhodou tyhle funkce nepotřebujete volat ve vašem kódu, použijte analyzátor PHP kódu PHPStan a má pravidla pro hledání volání nepovolených funkcí a metod, stačí ve vaší konfiguraci inklůdnout přibalený soubor disallowed-execution-calls.neon
.
Nezapomeňte také sledovat můj Twitter, o závažných problémech se tam určitě dozvíte také. A rád vás přivítám i na mém školení webové bezpečnosti (nejbližší termín: termín zatím nevypsán).
A ještě jedna věc: pokud jste doposud Nette neaktualizovali, tak už asi můžete počítat s tím, že do vašeho webu nebo aplikace někdo nakladl nějaké ty shelly nebo zadní vrátka a měli byste spustit proces obnovy, vyčištění a případně i pátrání po uniklých datech. A pokud už máte aktualizováno, tak se můžete pobavit třeba nad Easter eggy na mém webu.