Undersøg almindelige kodesikkerhedsproblemer
Forståelse af almindelige sikkerhedssårbarheder er afgørende for at identificere og løse kodesikkerhedsproblemer effektivt. Denne enhed dækker udbredte sikkerhedsproblemer i kode, deres virkninger, og hvorfor det er afgørende for applikationssikkerheden at løse dem hurtigt.
Hvorfor fokusere på sikkerhedsspørgsmål?
Sikkerhedssårbarheder repræsenterer en af de mest kritiske kategorier af softwarefejl. En enkelt sårbarhed kan resultere i:
- Databrud: Eksponering af følsomme kunde- eller forretningsdata.
- Økonomiske tab: Direkte omkostninger fra overtrædelser, lovpligtige bøder og afhjælpningsudgifter.
- Skade på omdømme: Tab af kundernes tillid og virksomhedens troværdighed.
- Driftsforstyrrelser: Systemkompromittering kan stoppe forretningsdriften.
Funktionelle fejl kan være pinlige for en udvikler, men sikkerhedsfejl kan have alvorlige konsekvenser for en organisation og brugere. Alle udviklere skal være sikkerhedsbevidste, uanset deres rolle eller specialisering.
Åbn OWASP (Web Application Security Project)
Open Web Application Security Project (OWASP) er en nonprofitorganisation, der fokuserer på at forbedre softwaresikkerheden. OWASP vedligeholder den bredt anerkendte "OWASP Top 10" - en regelmæssigt opdateret liste over de mest kritiske sikkerhedsrisici for webapplikationer baseret på data fra sikkerhedsorganisationer over hele verden.
OWASP Top 10 fungerer som en sikkerhedsbaseline for udviklere og organisationer, der hjælper med at prioritere, hvilke sårbarheder der skal løses først. Placeringerne skifter over tid, efterhånden som angrebsmønstrene udvikler sig. Det kan f.eks. være:
- 2017 OWASP Top 10: Injektionsfejl holdt #1-pladsen.
- 2021 OWASP Top 10: Injektion flyttede til #3, da nye trusler som brudt adgangskontrol dukkede op.
OWASP Top 10 afspejler angrebsdata fra den virkelige verden, ikke teoretiske bekymringer. Kodesårbarheder som SQL-injektion og svag kryptering rangerer konsekvent blandt branchens mest kritiske sikkerhedsproblemer.
Injektionsanfald
Injektionsangreb opstår, når data, der ikke er tillid til, sendes til en tolk som en del af en kommando eller forespørgsel. Angriberens fjendtlige data narrer fortolkeren til at udføre utilsigtede kommandoer eller få adgang til uautoriserede data.
SQL-indsprøjtning
SQL-injektion er et af de farligste og mest almindelige injektionsangreb. Det opstår, når et program inkorporerer input, der ikke er tillid til, direkte i SQL-forespørgsler uden korrekt validering eller parameterisering.
Overvej følgende kodeeksempel:
// 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();
En hacker kan indtaste ' OR '1'='1 som brugernavn og omdanne forespørgslen til:
SELECT * FROM Users WHERE Username = '' OR '1'='1' AND Password = ''
Da '1'='1' det altid er sandt, returnerer denne forespørgsel alle brugere og omgår godkendelse helt.
Effekter fra den virkelige verden
SQL-injektionsangreb har forårsaget adskillige højt profilerede brud. Angribere kan:
- Omgå godkendelsesmekanismer.
- Udtræk hele databaser, der indeholder følsomme oplysninger.
- Rediger eller slet data.
- Udfør administrative handlinger på databasen.
Sikker implementering
Den sikre måde at håndtere SQL-forespørgsler på er at bruge parameteriserede forespørgsler (også kaldet forberedte sætninger).
Det kan f.eks. være:
// 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();
Parameteriserede forespørgsler adskiller kode fra data. Databasen behandler kun parameterværdier som data, aldrig som eksekverbar SQL-kode, hvilket forhindrer injektionsangreb.
Andre injektionstyper
SQL-injektion er kun én form for injektionsangreb, og udviklere skal være opmærksomme på andre injektionssårbarheder, der kan kompromittere applikationssikkerheden.
Mens SQL-injektion er mest almindelig, findes der andre injektionssårbarheder:
- Kommandoinjektion: Indsættelse af systemkommandoer i applikationsinput, der udfører shell-kommandoer.
- LDAP-injektion (Lightweight Directory Access Protocol): Manipulation af LDAP-forespørgsler for at få adgang til uautoriserede biblioteksoplysninger.
- NoSQL-injektion: Udnyttelse af NoSQL-databaser gennem ondsindede forespørgsler.
- XML-injektion: Indsættelse af skadeligt XML-indhold for at få adgang til eller ændre data.
Det universelle mønster: Hver gang du indsætter input, der ikke er tillid til, i en kommando eller forespørgsel, der bliver fortolket, risikerer du injektion. Løsningsmønsteret er altid ens: rense, validere eller parameterisere for at adskille kode fra data.
Svag kryptering af følsomme data
Lagring eller overførsel af følsomme data uden korrekt kryptering udsætter dem for uautoriseret adgang. Denne kategori omfatter både utilstrækkelige krypteringsmetoder og fuldstændig mangel på kryptering.
Usikker opbevaring af adgangskoder
Adgangskoder kræver særlig beskyttelse, fordi de fungerer som den primære godkendelsesmekanisme for de fleste applikationer.
Forkert lagring af adgangskoder er en kritisk sårbarhed.
Klartekstlagring (aldrig acceptabelt)
// DANGEROUS: Storing passwords in plaintext
string password = userInput;
database.SavePassword(username, password);
Hvis databasen kompromitteres, afsløres alle brugeradgangskoder med det samme.
Svag hashing (utilstrækkelig)
// 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 og SHA1 er kryptografisk brudt. Moderne GPU'er kan teste milliarder af adgangskodekombinationer i sekundet mod disse hurtige hashes. Derudover kan angribere uden salt bruge forudberegnede regnbuetabeller til at knække adgangskoder med det samme.
Sikker hashing (anbefales)
// 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);
Sikker adgangskodehashing kræver:
- Salt: Tilfældige data tilføjet til adgangskoder før hashing, hvilket forhindrer regnbuetabelangreb.
- Langsom algoritme: Funktioner som bcrypt, scrypt eller Argon2 er beregningsmæssigt dyre og begrænser brute-force-forsøg til hundreder eller tusinder pr. sekund i stedet for milliarder.
Kryptering af inaktive data
Ud over adgangskodesikkerhed skal alle følsomme data, der er gemt på disk eller i databaser, beskyttes gennem korrekt kryptering.
Følsomme data, der er gemt uden kryptering, er sårbare, hvis lagermedier kompromitteres.
Sårbart scenarie
// VULNERABLE: Writing sensitive data in plaintext
File.WriteAllText("customer_data.txt", sensitiveInformation);
Hvis en bærbar computer, der indeholder denne fil, bliver stjålet, eller hvis en hacker får adgang til filsystemet, kan dataene læses med det samme.
Sikker tilgang
// 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);
}
}
}
Korrekt kryptering giver et forsvarslag, selvom lageret er kompromitteret, forudsat at krypteringsnøgler administreres korrekt separat.
Logføring og fejlhåndteringsproblemer
Forkert logning og fejlhåndtering kan utilsigtet afsløre følsomme oplysninger eller systemdetaljer, der hjælper angribere.
Logning af følsomme data
Selvom logning er afgørende for fejlfinding og overvågning, kan det blive en sikkerhedssårbarhed, når følsomme oplysninger registreres.
Programmer må aldrig logge følsomme oplysninger i klartekst.
Farlig skovhugst
// DANGEROUS: Logging sensitive information
logger.LogInformation($"User {username} logged in with password: {password}");
logger.LogInformation($"Credit card processed: {cardNumber}");
logger.LogInformation($"API Key: {apiKey}");
Denne kode afslører følsomme data i logfiler, som kan være tilgængelige for uautoriserede brugere eller lækket gennem logstyringssystemer.
Sikker logføring
// 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");
Bedste fremgangsmåder
- Log aldrig adgangskoder, godkendelsestokens eller API-nøgler.
- Masker eller rediger følsomme oplysninger som kreditkortnumre eller CPR-numre.
- Logfør hændelser og resultater, ikke følsomme dataværdier.
Overdreven offentliggørelse af fejloplysninger
Fejlmeddelelser tjener et vigtigt fejlfindingsformål, men de skal udformes omhyggeligt for at undgå at afsløre systemets interne funktioner for potentielle angribere.
Detaljerede fejlmeddelelser kan afsløre systemarkitektur, filstier, databaseskemaer og andre oplysninger, der er nyttige for angribere.
Problematisk fejlhåndtering
// PROBLEMATIC: Exposing detailed error information to users
catch (Exception ex)
{
return $"Error: {ex.Message}\nStack Trace: {ex.StackTrace}\nConnection String: {connectionString}";
}
Dette afslører interne systemdetaljer, som angribere kan bruge til at lave mere sofistikerede angreb.
Sikker fejlhåndtering
// 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.";
}
Brugere modtager venlige, minimale fejlmeddelelser, mens udviklere får detaljerede fejloplysninger gennem sikre logfiler.
Angreb på stigennemgang
Stigennemgang (også kaldet mappegennemgang) opstår, når et program bruger brugerleveret input til at konstruere filstier uden korrekt validering. Angribere kan bruge specialtegnsekvenser til at få adgang til filer uden for den tilsigtede mappe.
Overvej følgende sårbare kode:
// 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);
En angriber kunne give input som ../../../Windows/System32/config/SAM at få adgang til følsomme systemfiler eller ../../web.config at læse programkonfigurationer, der indeholder hemmeligheder.
Sårbar kode aktiverer følgende angrebsmekanisme:
-
..sekvenser navigerer op ad mappeniveauer. - Angribere kan undslippe den tilsigtede mappesandkasse.
- Få adgang til følsomme filer, konfigurationsfiler eller systemfiler.
- Overskriv potentielt kritiske programfiler.
Overvej følgende sikre implementering:
// 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);
Sikre implementeringer demonstrerer følgende forsvarsstrategier:
- Bruges
Path.GetFileName()til at fjerne biblioteksoplysninger. - Tilladte filer eller mønstre på tilladelseslisten i stedet for at blokere farlige tegn.
- Valider, at løste stier forbliver inden for de tilsigtede mapper.
- Implementer strenge filadgangstilladelser på operativsystemniveau.
Andre sikkerhedsovervejelser
Ud over de sårbarheder, der er dækket i de foregående afsnit, kræver flere andre sikkerhedsproblemer, at udvikleren er opmærksom.
Scripting på tværs af websteder (XSS)
Cross-site scripting gør det muligt for angribere at injicere ondsindet kode i webapplikationer, hvilket potentielt kompromitterer brugerdata og sessioner.
Selvom det ikke gælder for konsolapplikationer, skal webudviklere validere og kode alle brugerinput, før de vises i browsere.
Hårdkodede hemmeligheder
Legitimationsoplysninger og følsomme konfigurationsværdier, der er integreret direkte i kildekoden, udgør en kritisk sikkerhedsrisiko, der kan udsætte hele systemer.
Indlejring af API-nøgler, adgangskoder eller tokens direkte i kildekoden eksponerer dem for alle med lageradgang. Hemmeligheder skal være:
- Opbevares i sikre konfigurationssystemer eller bokse.
- Aldrig forpligtet til versionskontrol.
- Roteret regelmæssigt.
- Administreret med korrekt adgangskontrol.
Ressourceudmattelse og denial of service
Angribere udnytter ofte programmer, der ikke administrerer ressourcer korrekt. Angreb kan forårsage tjenesteafbrydelser eller systemnedbrud.
Dårlig ressourcestyring kan muliggøre denial-of-service-angreb. Eksempler omfatter:
- Læsning af hele store filer i hukommelsen (forårsager fejl ved manglende hukommelse).
- Ikke begrænsning af anmodningsstørrelser eller frekvenser.
- Ineffektive algoritmer, der bruger for meget CPU.
- Undladelse af at bortskaffe ressourcer korrekt.
Sådan identificerer du sikkerhedsproblemer i kode
Identifikation af sikkerhedssårbarheder i kode kræver en systematisk tilgang.
Analysere håndtering af brugerinput
Brugerinput repræsenterer den primære angrebsvektor for de fleste sikkerhedssårbarheder, hvilket gør det vigtigt at undersøge, hvordan din kode behandler eksterne data.
Hvert punkt, hvor din kode accepterer brugerinput, er et potentielt indgangspunkt for angreb:
- Se efter: Input, der bruges i SQL-forespørgsler, filstier, systemkommandoer eller kritisk logik.
- Spørg: "Stoler jeg for meget på dette input?"
- Overvej: Injektionssårbarheder, stigennemgang, kommandoinjektion.
Gennemse kryptografiske handlinger
Sikkerhedsimplementeringer, der involverer kryptering, hashing og godkendelse, kræver ekstra kontrol, fordi svag kryptografi kan kompromittere hele systemer.
Kryptografisk kode kræver særlig kontrol:
-
Se efter:
MD5.Create(),SHA1.Create(), adgangskodelagring i klartekst. - Spørg: "Betragtes denne kryptografiske metode stadig som sikker?"
- Overvej: Brug af bcrypt, scrypt eller Argon2 til adgangskoder; SHA-256 eller bedre til integritetskontrol.
Undersøg logføringssætninger
Logfiler kan utilsigtet blive sikkerhedssårbarheder, når de fanger følsomme oplysninger, der skal forblive beskyttet.
Scan din kodebase for følsomme data i logfiler:
- Se efter: Logsætninger, der indeholder variabler med navnene password, secret, token, apiKey, cardNumber.
- Spørg: "Hvilke oplysninger afslører jeg i logfiler?"
- Overvej: Hvad sker der, hvis disse logfiler kompromitteres eller ved et uheld eksponeres?
Undersøg filhandlinger
Filhåndteringskode giver unikke sikkerhedsudfordringer, fordi den kan eksponere systemressourcer uden for dit programs tilsigtede omfang.
Filhåndteringskode kræver omhyggelig validering:
-
Se efter:
Path.Combinemed brugerinput, filhandlinger baseret på brugerleverede stier. - Spørg: "Kan en bruger undslippe den tilsigtede mappe?"
- Overvej: Stigennemgangsangreb og mappeescape-teknikker.
Brug automatiserede værktøjer
Selvom manuel kodegennemgang er afgørende, kan automatiserede værktøjer effektivt scanne store kodebaser og identificere almindelige sårbarhedsmønstre, der kan overses under manuel inspektion.
Kombiner manuel kodegennemgang med automatiseret analyse:
- Statisk analyse: Værktøjer som GitHub CodeQL scanner kode for kendte sårbarhedsmønstre.
- GitHub Copilot: Brug Spørg-tilstand til at analysere kodesektioner: "Er der sikkerhedsproblemer i denne kode?"
- Sikkerhedslintere: Sprogspecifikke værktøjer kan markere åbenlyse sikkerhedsfejl.
GitHub Copilot kan identificere mange almindelige sikkerhedsproblemer, når du beder den om at analysere kode. Den trækker på mønstre fra millioner af kodebaser for at genkende sårbarheder.
Shift-left-tilgang
Princippet om at "skifte til venstre" betyder, at der tages fat på sikkerhed tidligere i udviklingens livscyklus:
- Designfase: Overvej sikkerhedsmæssige konsekvenser af arkitektoniske beslutninger.
- Udviklingsfase: Skriv sikker kode fra starten. Fang problemer under kodegennemgang.
- Testfase: Medtag sikkerhedstest sammen med funktionel test.
- Udrulningsfase: Scan for sårbarheder før udgivelse.
Det er langt billigere at fange sikkerhedsproblemer under udviklingen end at opdage dem i produktionen. Omkostningerne ved at rette sårbarheder stiger eksponentielt for hver fase, de går igennem.
Oversigt
Almindelige sikkerhedssårbarheder som injektionsangreb, svag kryptering, forkert logføring og stigennemgang udgør alvorlige trusler mod applikationssikkerheden. Forståelse af disse sårbarheder hjælper dig med at genkende dem i kode og prioritere deres afhjælpning. Ved at kombinere viden om almindelige sårbarhedsmønstre med værktøjer som GitHub Copilot kan du identificere og løse sikkerhedsproblemer mere effektivt.