Vstupy a výstupy
Nejrozšířenější bezpečnostní slabinou současných aplikací je, že aplikace správně nezpracuje data přijatá z externích zdrojů, zejména uživatelský vstup. Vždy byste měli každý vstup důkladně zkontrolovat a před jeho použitím ověřit, že je platný. Pokud nebudete uživatelský vstup analyzovat z hlediska možných útoků, může to mít za následek ztrátu nebo odhalení dat, zvýšení oprávnění nebo dokonce spuštění škodlivého kódu na počítačích jiných uživatelů.
Smutné přitom je, že se tento problém dá snadno vyřešit. V této lekci se podíváme, jak s daty po přijetí zacházet, když se zobrazí na obrazovce a kdy jsou uložená pro pozdější použití.
Proč potřebujeme vstup ověřovat?
Představte si, že vytváříte rozhraní, které uživateli umožní vytvořit účet na vašem webu. Naše profilová data obsahují jméno, e-mail a přezdívku, kterou zobrazíme všem uživatelům, kteří web navštíví. Co když nový uživatel vytvoří profil a zadá přezdívku, která bude obsahovat nějaké příkazy SQL? Co když například uživatel se zlými úmysly zadá něco jako následující výňatek:
Eve'); DROP TABLE Users;--
Pokud slepě vložíte tuto hodnotu do databáze, mohlo by to potenciálně pozměnit příkaz SQL a vést ke spuštění příkazů, které rozhodně spustit nechceme! Tento příklad se označuje jako „útok prostřednictvím injektáže SQL“ (SQL Injection) a je to jen jeden z mnoha typů zneužití bezpečnostních slabin, kterými je možné na vás zaútočit, když neošetříte uživatelský vstup správně. Jak tedy můžeme tuto situaci napravit? V této lekci se naučíte, kdy je nutné ověřit vstup, jakým způsobem kódovat výstup a jak vytvářet parametrizované dotazy (které eliminují výše popsanou možnost zneužití). Jsou to tři hlavní obranné techniky proti škodlivému vstupu zadanému do vašich aplikací.
Kdy je potřeba vstup ověřit?
Odpověď je, že vždy. Musíte ověřovat každý vstup pro vaši aplikaci. To zahrnuje parametry v adrese URL, vstup od uživatele, data z databáze, data z rozhraní API a vše, co je předáno jasně, že uživatel může potenciálně manipulovat. Vždy používejte přístup na seznam povolených, což znamená, že přijímáte pouze "známý dobrý" vstup, místo seznamu blokovaných položek (kde konkrétně hledáte špatný vstup), protože není možné si představit úplný seznam potenciálně nebezpečných vstupů. Udělejte to na serveru, ne na straně klienta (nebo na straně klienta), abyste zajistili, že vaše obrana nebude možné obejít. Považovat všechna data za nedůvěryhodná a budete se chránit před většinou běžných ohrožení zabezpečení webových aplikací.
Pokud používáte ASP.NET, poskytuje architektura skvělou podporu ověřování vstupu na straně klienta i serveru.
Pokud používáte jinou webovou architekturu, existuje několik skvělých technik pro ověřování vstupu, které jsou k dispozici v taháku OWASP Input Validation Cheatsheet.
Vždy používejte parametrizované dotazy
Databáze SQL se běžně používají k ukládání dat; Vaše aplikace může například ukládat informace o profilu uživatele do databáze. V kódu byste nikdy neměli vytvářet vložené dotazy SQL nebo jiné databázové dotazy pomocí nezpracovaného uživatelského vstupu a odesílat je přímo do databáze; toto chování je recept na havárii, jak jsme viděli dříve.
Například nevytvávejte kód podobný následujícímu vloženého příkladu SQL:
string userName = Request.QueryString["username"]; // receive input from the user BEWARE!
...
string query = "SELECT * FROM [dbo].[users] WHERE userName = '" + userName + "'";
V uvedeném příkladu spojujeme dohromady textové řetězce, abychom vytvořili dotaz, přičemž použijeme vstup od uživatele a generujeme dynamický dotaz SQL k vyhledání uživatele. A opět – pokud se uživatel se zlými úmysly dovtípí, že děláme právě tohle, nebo bude jen zkoušet různé styly vstupu a sledovat, jestli se neobjeví nějaké slabiny, může to pro nás skončit katastrofálně. Místo toho použijte parametrizované příkazy SQL nebo uložené procedury, jako například:
-- Lookup a user
CREATE PROCEDURE sp_findUser
(
@UserName varchar(50)
)
SELECT * FROM [dbo].[users] WHERE userName = @UserName
Pomocí této metody můžete proceduru vyvolat z kódu bezpečně a předat jí userName
řetězec, aniž byste se museli starat o to, že se považuje za součást příkazu SQL.
Vždy kódujte výstup
Jakýkoli výstup, který prezentujete vizuálně nebo do souboru, by měl být vždy kódován a řádně uvozen řídicími znaky. To vás může ochránit v případě, že při průchodu sanitizace došlo k chybě nebo kód omylem vygeneruje něco, co se dá zneužít se zlými úmysly. Tento princip návrhu zajistí, že se všechno zobrazí jako výstup a že to nebude nechtěně považováno za něco, co by se mělo provést, což je další běžná technika útoku, která se označuje jako „skriptování mezi weby“ (XSS).
Protože prevence XSS je běžným požadavkem na aplikaci, je tato technika zabezpečení další oblastí, kde ASP.NET odvede práci za vás. Ve výchozím nastavení je veškerý výstup již kódován. Pokud používáte jinou webovou architekturu, můžete ověřit možnosti kódování výstupu na webech pomocí taháku OWASP XSS Prevention.
Shrnutí
Sanitizace a ověřování vstupů je nutný předpoklad k zajištění toho, že vaše vstupní hodnoty budou platné a bude je možné bezpečně používat a ukládat. Většina moderních webových architektur nabízí integrované funkce, které umožňují automatizovat některé z těchto úloh. Můžete zkontrolovat dokumentaci k vaší preferované architektuře a zjistit, jaké funkce nabízí. I když jsou webové aplikace nejčastějším terčem útoků, mějte na paměti, že jiné typy aplikací mohou být stejně zranitelné. Nemyslete si, že jste v bezpečí jen proto, že vaše nová aplikace je počítačová aplikace. Stále budete muset správně zpracovat uživatelský vstup, abyste zajistili, že někdo vaši aplikaci nepoužívá k poškození dat nebo poškození pověsti vaší společnosti.