Prozkoumání běžných problémů se zabezpečením kódu

Dokončeno

Pochopení běžných ohrožení zabezpečení je nezbytné k efektivní identifikaci a řešení problémů se zabezpečením kódu. Tato lekce se zabývá nejčastějšími problémy se zabezpečením v kódu, jejich efektech a důvodech, proč je jejich řešení pro zabezpečení aplikací velmi důležité.

Proč se zaměřit na problémy se zabezpečením?

Ohrožení zabezpečení představují jednu z nejdůležitějších kategorií vad softwaru. Jediné ohrožení zabezpečení může mít za následek:

  • Porušení zabezpečení dat: Vystavení citlivých zákaznických nebo obchodních dat
  • Finanční ztráty: Přímé náklady z porušení předpisů, regulační pokuty a nápravné výdaje.
  • Poškození pověsti: Ztráta důvěry zákazníků a důvěryhodnosti podniku.
  • Provozní přerušení: Narušení systému může zastavit obchodní operace.

Funkční chyby můžou být pro vývojáře trapné, ale chyby zabezpečení můžou mít vážné důsledky pro organizaci a uživatele. Každý vývojář musí být při vědomí zabezpečení bez ohledu na svou roli nebo specializaci.

Otevření projektu zabezpečení webových aplikací (OWASP)

Projekt OWASP (Open Web Application Security Project) je nezisková organizace zaměřená na zlepšení zabezpečení softwaru. OWASP udržuje široce uznávaný OWASP Top 10 – pravidelně aktualizovaný seznam nejdůležitějších rizik zabezpečení webových aplikací na základě dat z organizací zabezpečení po celém světě.

OWASP Top 10 slouží jako standardní hodnoty zabezpečení pro vývojáře a organizace a pomáhá určit prioritu ohrožení zabezpečení, která se mají řešit jako první. Pořadí se v průběhu času mění s tím, jak se vyvíjejí vzorce útoku. Například:

  • 2017 OWASP Top 10: Injekční chyby zaujímají první místo.
  • 2021 OWASP Top 10: Injekce se přesunula na #3, protože se objevily nové hrozby, jako je narušení řízení přístupu.

OWASP Top 10 odráží data o útoku z reálného světa, nikoli teoretické obavy. Ohrožení zabezpečení kódu, jako je injektáž SQL a slabé šifrování, se konzistentně řadí mezi nejdůležitější aspekty zabezpečení v oboru.

Útoky injekcí

K injekčním útokům dochází, když se nedůvěryhodná data odesílají do interpreta jako součást příkazu nebo dotazu. Útočníkova nepřátelská data nutí interpreta k provádění nezamýšlených příkazů nebo k přístupu k neautorizovaným datům.

Injektáž SQL

Injektáž SQL je jednou z nejdůležitějších a nejběžnějších útoků prostřednictvím injektáže. K tomu dochází, když aplikace zahrne nedůvěryhodný vstup přímo do dotazů SQL bez správného ověření nebo parametrizace.

Vezměme si následující příklad kódu:

// DANGEROUS: Concatenating user input directly into SQL
string query = "SELECT * FROM Users WHERE Username = '" + userInput + "' AND Password = '" + passwordInput + "'";
SqlCommand command = new SqlCommand(query, connection);
SqlDataReader reader = command.ExecuteReader();

Útočník může zadat ' OR '1'='1 jako vstup uživatelského jména a transformovat dotaz na:

SELECT * FROM Users WHERE Username = '' OR '1'='1' AND Password = ''

Vzhledem k tomu, že '1'='1' je vždy pravdivé, tento dotaz vrátí všechny uživatele a zcela obejde ověřování.

Skutečné efekty

Útoky prostřednictvím injektáže SQL způsobily řadu vysoce profilových porušení zabezpečení. Útočníci můžou:

  • Obejít mechanismy ověřování.
  • Extrahujte celé databáze obsahující citlivé informace.
  • Upravte nebo odstraňte data.
  • Proveďte operace správy v databázi.

Zabezpečená implementace

Bezpečným způsobem zpracování dotazů SQL je použití parametrizovaných dotazů (označovaných také jako připravené příkazy).

Například:

// SECURE: Using parameterized queries
string query = "SELECT * FROM Users WHERE Username = @username AND Password = @password";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@username", userInput);
command.Parameters.AddWithValue("@password", passwordInput);
SqlDataReader reader = command.ExecuteReader();

Parametrizované dotazy oddělují kód od dat. Databáze zpracovává hodnoty parametrů pouze jako data, nikdy jako spustitelný kód SQL, což brání útokům prostřednictvím injektáže.

Jiné typy injektáže

Injektáž SQL je jen jedna forma útoku prostřednictvím injektáže a vývojáři musí vědět o dalších ohroženích zabezpečení injektáže, která můžou ohrozit zabezpečení aplikace.

I když je injektáž SQL nejběžnější, existují další ohrožení zabezpečení injektáže:

  • Injektáž příkazů: Vložení systémových příkazů do vstupů aplikace, které spouští příkazy prostředí.
  • Injektáž protokolu LDAP (Lightweight Directory Access Protocol): Manipulace s dotazy LDAP pro přístup k neoprávněným informacím adresáře
  • Útok NoSQL: Zneužití databází NoSQL prostřednictvím škodlivých dotazů.
  • Injektáž XML: Vložení škodlivého obsahu XML pro přístup k datům nebo jejich úpravu

Univerzální vzor: Kdykoli vložíte nedůvěryhodný vstup do příkazu nebo dotazu, který se interpretuje, riskujete injektáž. Vzor řešení je vždy podobný: sanitizace, ověření nebo parametrizace pro oddělení kódu od dat.

Slabé šifrování citlivých dat

Ukládání nebo přenos citlivých dat bez správného šifrování je zveřejňuje neoprávněnému přístupu. Tato kategorie zahrnuje nedostatečné metody šifrování i úplný nedostatek šifrování.

Nezabezpečené úložiště hesel

Hesla vyžadují speciální ochranu, protože slouží jako primární mechanismus ověřování pro většinu aplikací.

Nesprávné ukládání hesel je kritická chyba zabezpečení.

Úložiště prostého textu (nikdy přijatelné)

// DANGEROUS: Storing passwords in plaintext
string password = userInput;
database.SavePassword(username, password);

Pokud dojde k ohrožení zabezpečení databáze, zobrazí se okamžitě všechna uživatelská hesla.

Slabé hashování (nedostatečné)

// INSUFFICIENT: Using MD5 or SHA1 without salt
using (MD5 md5 = MD5.Create())
{
    byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
    string hashedPassword = Convert.ToBase64String(hash);
}

MD5 a SHA1 jsou kryptograficky poškozené. Moderní gpu můžou testovat miliardy kombinací hesel za sekundu s těmito rychlými hodnotami hash. Kromě toho bez soli můžou útočníci okamžitě používat předpočítané duhové tabulky k okamžitému prolomení hesel.

// SECURE: Using bcrypt with automatic salt generation
string hashedPassword = BCrypt.Net.BCrypt.HashPassword(password);

// Later, for verification:
bool isValid = BCrypt.Net.BCrypt.Verify(userInput, storedHash);

Zabezpečení hash hesel vyžaduje:

  • Sůl: Náhodná data přidaná do hesel před hashováním, brání útokům na duhovou tabulku.
  • Pomalý algoritmus: Funkce jako bcrypt, scrypt nebo Argon2 jsou výpočetně nákladné a omezují pokusy hrubou silou na stovky nebo tisíce za sekundu místo miliard.

Šifrování neaktivních uložených dat

Kromě zabezpečení hesel potřebují všechna citlivá data uložená na disku nebo v databázích ochranu prostřednictvím správného šifrování.

Citlivá data uložená bez šifrování jsou ohrožená, pokud dojde k ohrožení úložiště.

Ohrožený scénář

// VULNERABLE: Writing sensitive data in plaintext
File.WriteAllText("customer_data.txt", sensitiveInformation);

Pokud dojde k odcizení přenosného počítače obsahujícího tento soubor nebo pokud útočník získá přístup k systému souborů, budou data okamžitě čitelná.

Zabezpečený přístup

// SECURE: Encrypting data before storage
using (Aes aes = Aes.Create())
{
    aes.Key = GetEncryptionKey(); // Securely managed key
    aes.GenerateIV();
    
    using (FileStream fileStream = new FileStream("customer_data.enc", FileMode.Create))
    {
        fileStream.Write(aes.IV, 0, aes.IV.Length);
        using (CryptoStream cryptoStream = new CryptoStream(fileStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
        using (StreamWriter writer = new StreamWriter(cryptoStream))
        {
            writer.Write(sensitiveInformation);
        }
    }
}

Správné šifrování poskytuje vrstvu ochrany i v případě ohrožení úložiště za předpokladu, že šifrovací klíče jsou správně spravovány samostatně.

Problémy s protokolováním a zpracováním chyb

Nesprávné protokolování a zpracování chyb může neúmyslně vystavit citlivé informace nebo podrobnosti o systému, které útočníkům pomáhají.

Protokolování citlivých dat

Protokolování je sice nezbytné pro ladění a monitorování, ale při zachycení citlivých informací se může stát ohrožením zabezpečení.

Aplikace nesmí nikdy protokolovat citlivé informace ve formátu prostého textu.

Nebezpečné postupy protokolování

// DANGEROUS: Logging sensitive information
logger.LogInformation($"User {username} logged in with password: {password}");
logger.LogInformation($"Credit card processed: {cardNumber}");
logger.LogInformation($"API Key: {apiKey}");

Tento kód zveřejňuje citlivá data v protokolech, která můžou být přístupná neoprávněným uživatelům nebo unikla prostřednictvím systémů pro správu protokolů.

Postupy zabezpečeného protokolování

// SECURE: Logging without sensitive data
logger.LogInformation($"User {username} logged in successfully");
logger.LogInformation($"Payment processed for order {orderId}");
logger.LogInformation($"API call authenticated successfully");

Osvědčené postupy

  • Nikdy nezahlašujte hesla, ověřovací tokeny ani klíče rozhraní API.
  • Maskujte nebo redigujte citlivé informace, jako jsou čísla platebních karet nebo čísla sociálního pojištění.
  • Protokolování událostí a výsledků, nikoli citlivých hodnot dat.

Nadměrné zpřístupnění informací o chybách

Chybové zprávy slouží k důležitému účelu ladění, ale musí být pečlivě vytvořené, aby se zabránilo odhalení vnitřních informací systému potenciálním útočníkům.

Podrobné chybové zprávy můžou odhalit systémovou architekturu, cesty k souborům, schémata databáze a další užitečné informace pro útočníky.

Problematické zpracování chyb

// PROBLEMATIC: Exposing detailed error information to users
catch (Exception ex)
{
    return $"Error: {ex.Message}\nStack Trace: {ex.StackTrace}\nConnection String: {connectionString}";
}

Tím se odhalí interní podrobnosti o systému, které útočníci můžou použít k vytvoření sofistikovanějších útoků.

Zabezpečené zpracování chyb

// SECURE: User-friendly messages with detailed internal logging
catch (Exception ex)
{
    logger.LogError(ex, "Failed to process user request");
    return "An error occurred while processing your request. Please try again or contact support.";
}

Uživatelé dostávají popisné a minimální chybové zprávy, zatímco vývojáři získají podrobné informace o chybách prostřednictvím zabezpečených protokolů.

Útoky procházení cest

K procházení cesty (označované také jako procházení adresářů) dochází v případě, že aplikace používá vstup zadaný uživatelem k vytvoření cest k souborům bez správného ověření. Útočníci můžou pro přístup k souborům mimo zamýšlený adresář používat speciální sekvence znaků.

Vezměte v úvahu následující ohrožený kód:

// VULNERABLE: Using user input directly in file paths
string filename = Request.Query["file"];
string filePath = Path.Combine(@"C:\uploads\", filename);
string content = File.ReadAllText(filePath);

Útočník může poskytnout vstup, jako je ../../../Windows/System32/config/SAM, aby získal přístup k citlivým systémovým souborům, nebo ../../web.config, aby mohl číst konfiguraci aplikace obsahující citlivé informace.

Ohrožený kód umožňuje následující mechanismus útoku:

  • .. sekvence navigují nahoru po úrovních adresáře.
  • Útočníci mohou uniknout zamýšlenému adresářovému sandboxu.
  • Přístup k citlivým souborům, konfiguračním souborům nebo systémovým souborům
  • Potenciálně přepsat důležité soubory aplikace.

Zvažte následující zabezpečenou implementaci:

// SECURE: Validating and constraining file paths
string filename = Request.Query["file"];

// Remove path traversal sequences
filename = Path.GetFileName(filename);

// Construct full path
string uploadsDirectory = Path.GetFullPath(@"C:\uploads\");
string filePath = Path.GetFullPath(Path.Combine(uploadsDirectory, filename));

// Verify the resulting path is still within the uploads directory
if (!filePath.StartsWith(uploadsDirectory))
{
    throw new SecurityException("Invalid file path");
}

string content = File.ReadAllText(filePath);

Zabezpečené implementace ukazují následující strategie obrany:

  • Použijte Path.GetFileName() k odebrání informací o adresáři.
  • Zařaďte soubory nebo vzory na seznam povolených místo zařazování nebezpečných znaků na seznam blokovaných.
  • Ověřte, že vyřešené cesty zůstávají v zamýšlených adresářích.
  • Implementujte striktní přístupová oprávnění k souborům na úrovni operačního systému.

Další aspekty zabezpečení

Kromě ohrožení zabezpečení popsaných v předchozích částech vyžaduje několik dalších problémů zabezpečení povědomí o vývojářích.

Skriptování napříč weby (XSS)

Skriptování mezi weby umožňuje útočníkům vkládat škodlivý kód do webových aplikací a potenciálně ohrozit uživatelská data a relace.

I když to neplatí pro konzolové aplikace, musí vývojáři webu před zobrazením v prohlížečích ověřit a kódovat veškerý uživatelský vstup.

Pevně zakódované tajné kódy

Přihlašovací údaje a citlivé hodnoty konfigurace vložené přímo do zdrojového kódu představují kritické bezpečnostní riziko, které může vystavit celé systémy.

Vkládání klíčů rozhraní API, hesel nebo tokenů přímo do zdrojového kódu je zveřejňuje komukoli, kdo má přístup k úložišti. Tajné kódy by měly být:

  • Uložené v zabezpečených konfiguračních systémech nebo trezorech.
  • Nikdy se neschovávejte do správy verzí.
  • Pravidelně se otáčí.
  • Spravováno pomocí správných řízení přístupu.

Vyčerpání prostředků a odepření služby

Útočníci často využívají aplikace, které správně nespravují prostředky. Útoky můžou způsobit přerušení služeb nebo chybové ukončení systému.

Špatná správa prostředků může umožňovat útoky na dostupnost služby. Mezi příklady patří:

  • Čtení celých velkých souborů do paměti (způsobující chyby z nedostatku paměti).
  • Neomezuje velikosti nebo četnosti požadavků.
  • Neefektivní algoritmy, které spotřebovávají nadměrný výkon procesoru.
  • Nepodařilo se vhodným způsobem zlikvidovat prostředky.

Identifikace problémů se zabezpečením v kódu

Identifikace ohrožení zabezpečení v kódu vyžaduje systematický přístup.

Analýza zpracování uživatelských vstupů

Uživatelský vstup představuje primární vektor útoku pro většinu ohrožení zabezpečení, takže je důležité prozkoumat, jak váš kód zpracovává externí data.

Každý bod, ve kterém váš kód přijímá vstup uživatele, je potenciální vstupním bodem pro útoky:

  • Vyhledejte: Vstupy používané v dotazech SQL; cestách k souborům; systémových příkazech; nebo kritické logice.
  • Zeptejte se: "Důvěřuji tomuto vstupu moc?"
  • Zvažte: Ohrožení zabezpečení prostřednictvím injektáže, procházení cest, injektáž příkazů.

Kontrola kryptografických operací

Implementace zabezpečení zahrnující šifrování, hashování a ověřování vyžadují dodatečnou kontrolu, protože slabá kryptografie může ohrozit celé systémy.

Kryptografický kód vyžaduje zvláštní kontrolu:

  • Vyhledejte: MD5.Create(), SHA1.Create(), úložiště hesel v prostém textu.
  • Zeptejte se: "Je tato kryptografická metoda stále považována za zabezpečenou?"
  • Zvažte: Použití bcrypt, scrypt nebo Argon2 pro hesla a SHA-256 nebo lepší pro kontroly integrity.

Prozkoumání příkazů protokolování

Protokoly se můžou neúmyslně stát ohrožením zabezpečení, když zachytají citlivé informace, které by měly zůstat chráněné.

Prohledejte kódovou základnu pro citlivá data v protokolech.

  • Hledejte: Příkazy protokolu obsahující proměnné s názvem password, secret, token, apiKey, cardNumber.
  • Zeptejte se: "Jaké informace zpřístupňujem v protokolech?"
  • Zvažte: Co se stane, když dojde k ohrožení nebo náhodnému zveřejnění těchto protokolů?

Kontrola operací se soubory

Kód pro zpracování souborů představuje jedinečné výzvy zabezpečení, protože může vystavit systémové prostředky nad rámec zamýšleného rozsahu vaší aplikace.

Zpracování souborů vyžaduje pečlivé ověření:

  • Hledejte: Path.Combine s uživatelským vstupem, operacemi se soubory na základě cest zadaných uživatelem.
  • Zeptejte se: "Může uživatel uniknout zamýšlenému adresáři?"
  • Zvažte: Procházení cest a techniky úniku adresářů.

Použití automatizovaných nástrojů

I když je ruční kontrola kódu nezbytná, automatizované nástroje můžou efektivně prohledávat velké základy kódu a identifikovat běžné vzory ohrožení zabezpečení, které by mohly být při ruční kontrole vynechány.

Kombinování ruční kontroly kódu s automatizovanou analýzou:

  • Statická analýza: Nástroje, jako je GitHub CodeQL, kontrolují kód známých vzorů ohrožení zabezpečení.
  • GitHub Copilot: Použití režimu Ask k analýze částí kódu: "Existují problémy se zabezpečením v tomto kódu?"
  • Lintery zabezpečení: Nástroje specifické pro jazyk můžou označit zjevné chyby zabezpečení.

GitHub Copilot dokáže identifikovat řadu běžných problémů se zabezpečením, když ho požádáte o analýzu kódu. Využívá vzory z milionů základů kódu k rozpoznání ohrožení zabezpečení.

Postup zabezpečení typu „shift-left“

Princip "posunu doleva" znamená řešení zabezpečení dříve ve vývojovém životním cyklu:

  • Fáze návrhu: Zvažte vliv na zabezpečení rozhodnutí o architektuře.
  • Fáze vývoje: Napište zabezpečený kód od začátku. Zachyťte problémy během kontroly kódu.
  • Fáze testování: Zahrňte testování zabezpečení spolu s funkčním testováním.
  • Fáze nasazení: Zkontrolujte zranitelnosti před vydáním.

Zachycení problémů se zabezpečením během vývoje je mnohem levnější než jejich zjištění v produkčním prostředí. Náklady na opravu zranitelností se exponenciálně zvyšují s každou fází, kterou projdou.

Shrnutí

Běžná ohrožení zabezpečení, jako jsou útoky prostřednictvím injektáže, slabé šifrování, nesprávné protokolování a procházení cest, představují vážné hrozby zabezpečení aplikace. Pochopení těchto ohrožení zabezpečení vám pomůže rozpoznat je v kódu a určit prioritu jejich nápravy. Kombinací znalostí běžných vzorů ohrožení zabezpečení s nástroji, jako je GitHub Copilot, můžete efektivněji identifikovat a řešit problémy se zabezpečením.