CodeGym/Java blog/Tilfældig/Sikkerhed i Java: bedste praksis
John Squirrels
Niveau
San Francisco

Sikkerhed i Java: bedste praksis

Udgivet i gruppen
En af de vigtigste målinger i serverapplikationer er sikkerhed. Dette er en type ikke-funktionelle krav . Sikkerhed i Java: bedste praksis - 1Sikkerhed omfatter mange komponenter. Selvfølgelig ville det tage mere end én artikel for fuldt ud at dække alle kendte sikkerhedsprincipper og sikkerhedsforanstaltninger, så vi vil dvæle ved det vigtigste. En person, der er velbevandret i dette emne, kan opsætte alle de relevante processer, undgå at skabe nye sikkerhedshuller og vil være nødvendig på ethvert hold. Selvfølgelig skal du ikke tro, at din ansøgning vil være 100 % sikker, hvis du følger denne praksis. Ingen! Men det bliver helt sikkert mere sikkert med dem. Lad os gå.

1. Sørg for sikkerhed på niveau med Java-sproget

Først og fremmest starter sikkerheden i Java lige på niveau med sprogets muligheder. Hvad ville vi gøre, hvis der ikke var nogen adgangsmodifikatorer? Der ville ikke være andet end anarki. Programmeringssproget hjælper os med at skrive sikker kode og gør også brug af mange implicitte sikkerhedsfunktioner:
  1. Stærk skrivning. Java er et statisk skrevet sprog. Dette gør det muligt at fange typerelaterede fejl under kørsel.
  2. Adgangsmodifikatorer. Disse giver os mulighed for at tilpasse adgang til klasser, metoder og felter efter behov.
  3. Automatisk hukommelsesstyring. Til dette har Java-udviklere en garbage collector, der frigør os fra at skulle konfigurere alt manuelt. Ja, nogle gange opstår der problemer.
  4. Bytekodebekræftelse : Java kompileres til bytekode, som kontrolleres af kørselstiden, før den udføres.
Derudover er der Oracles sikkerhedsanbefalinger . Det er selvfølgelig ikke skrevet i et højt sprog, og du falder måske i søvn flere gange, mens du læser det, men det er det værd. Især dokumentet med titlen Secure Coding Guidelines for Java SE er vigtigt. Den giver råd om, hvordan man skriver sikker kode. Dette dokument formidler en enorm mængde meget nyttig information. Hvis du har muligheden, bør du helt sikkert læse den. For at vække din interesse for dette materiale er her et par interessante tips:
  1. Undgå at serialisere sikkerhedsfølsomme klasser. Serialisering afslører klassegrænsefladen i den serialiserede fil, for ikke at nævne de data, der serialiseres.
  2. Prøv at undgå foranderlige klasser for data. Dette giver alle fordelene ved uforanderlige klasser (f.eks. gevindsikkerhed). Hvis du har et foranderligt objekt, kan det føre til uventet adfærd.
  3. Lav kopier af returnerede mutable objekter. Hvis en metode returnerer en reference til et internt foranderligt objekt, kan klientkoden ændre objektets interne tilstand.
  4. Og så videre…
Grundlæggende er Secure Coding Guidelines for Java SE en samling tips og tricks til, hvordan man skriver Java-kode korrekt og sikkert.

2. Eliminer SQL-injektionssårbarheder

Dette er en særlig form for sårbarhed. Det er specielt, fordi det både er en af ​​de mest berømte og en af ​​de mest almindelige sårbarheder. Hvis du aldrig har været interesseret i computersikkerhed, så ved du ikke om det. Hvad er SQL-injektion? Dette er et databaseangreb, der involverer indsprøjtning af yderligere SQL-kode, hvor det ikke forventes. Antag, at vi har en metode, der accepterer en slags parameter til at forespørge databasen. For eksempel et brugernavn. Sårbar kode ville se sådan ud:
// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Compose a SQL database query with our firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;

   // Execute the query
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
I dette eksempel er en SQL-forespørgsel forberedt på forhånd på en separat linje. Så hvad er problemet, ikke? Måske er problemet, at det ville være bedre at bruge String.format ? Ingen? Nå, hvad så? Lad os sætte os selv i en testers sko og tænke over, hvad der kunne bruges som værdien af ​​fornavn . For eksempel:
  1. Vi kan videregive, hvad der forventes - et brugernavn. Så vil databasen returnere alle brugere med det navn.
  2. Vi kan sende en tom streng. Så vil alle brugere blive returneret.
  3. Men vi kan også videregive følgende: "'; SLIP TABELBRUGERE;". Og her har vi nu huuuuuuge problemer. Denne forespørgsel vil slette en tabel fra databasen. Sammen med alle data. DET HELE.
Kan du forestille dig de problemer, dette ville forårsage? Udover det kan du skrive, hvad du vil. Du kan ændre navnene på alle brugerne. Du kan slette deres adresser. Mulighederne for sabotage er enorme. For at undgå dette skal du forhindre indsprøjtning af en færdig forespørgsel og i stedet danne forespørgslen ved hjælp af parametre. Dette burde være den eneste måde at oprette databaseforespørgsler på. Sådan kan du fjerne denne sårbarhed. For eksempel:
// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Create a parameterized query.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Create a prepared statement with the parameterized query
   PreparedStatement statement = connection.prepareStatement(query);

   // Pass the parameter's value
   statement.setString(1, firstName);

   // Execute the query
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
På denne måde undgås sårbarheden. For dem, der ønsker at dykke dybere ned i denne artikel, er her et godt eksempel . Hvordan ved du, hvornår du forstår denne sårbarhed? Hvis du får vittigheden i tegneserien nedenfor, så har du sikkert en klar forståelse af, hvad denne sårbarhed handler om :DSikkerhed i Java: bedste praksis - 2

3. Scan afhængigheder og hold dem opdateret

Hvad betyder det? Hvis du ikke ved, hvad en afhængighed er, vil jeg forklare. En afhængighed er et JAR-arkiv med kode, der er forbundet til et projekt ved hjælp af automatiske byggesystemer (Maven, Gradle, Ant) for at genbruge en andens løsning. For eksempel Project Lombok , som genererer gettere, sættere osv. til os i runtime. Store applikationer kan have mange og mange afhængigheder. Nogle er transitive (det vil sige, at hver afhængighed kan have sine egne afhængigheder og så videre). Som et resultat er angribere i stigende grad opmærksomme på open source-afhængigheder, da de bruges regelmæssigt, og mange klienter kan have problemer på grund af dem. Det er vigtigt at sikre sig, at der ikke er kendte sårbarheder i hele afhængighedstræet (ja, det ligner et træ). Der er flere måder at gøre dette på.

Brug Snyk til afhængighedsovervågning

Snyk tjekker alle projektafhængigheder og markerer kendte sårbarheder. Du kan registrere dig på Snyk og importere dine projekter via GitHub. Sikkerhed i Java: bedste praksis - 3Også, som du kan se fra billedet ovenfor, hvis en sårbarhed er rettet i en nyere version, vil Snyk tilbyde rettelsen og oprette en pull-anmodning. Du kan bruge det gratis til open source-projekter. Projekter scannes med jævne mellemrum, fx en gang om ugen, en gang om måneden. Jeg registrerede og tilføjede alle mine offentlige arkiver til Snyk-scanningen (der er intet farligt ved dette, da de allerede er offentlige for alle). Snyk viste derefter scanningsresultatet: Sikkerhed i Java: bedste praksis - 4Og efter et stykke tid forberedte Snyk-bot flere pull-anmodninger i projekter, hvor afhængigheder skal opdateres: Sikkerhed i Java: bedste praksis - 5Og også:Sikkerhed i Java: bedste praksis - 6Dette er et fantastisk værktøj til at finde sårbarheder og overvåge opdateringer til nye versioner.

Brug GitHub Security Lab

Enhver, der arbejder på GitHub, kan drage fordel af dets indbyggede værktøjer. Du kan læse mere om denne tilgang i deres blogindlæg med titlen Announcing GitHub Security Lab . Dette værktøj er selvfølgelig enklere end Snyk, men du bør bestemt ikke forsømme det. Derudover vil antallet af kendte sårbarheder kun vokse, så både Snyk og GitHub Security Lab vil fortsætte med at udvide og forbedre.

Aktiver Sonatype DepShield

Hvis du bruger GitHub til at gemme dine repositories, kan du tilføje Sonatype DepShield, en af ​​applikationerne på MarketPlace, til dine projekter. Det kan også bruges til at scanne projekter for afhængigheder. Desuden, hvis den finder noget, vil et GitHub-problem blive genereret med en passende beskrivelse som vist nedenfor:Sikkerhed i Java: bedste praksis - 7

4. Håndter fortrolige data med omhu

Vi kan alternativt bruge udtrykket "følsomme data". Lækning af en kundes personlige oplysninger, kreditkortnumre og andre følsomme oplysninger kan forårsage uoprettelig skade. Først og fremmest skal du tage et nærmere kig på designet af din applikation og afgøre, om du virkelig har brug for denne eller hin data. Måske har du faktisk ikke brug for nogle af de data, du har - data, der blev tilføjet for en fremtid, der ikke er kommet, og som sandsynligvis ikke kommer. Derudover lækker du mange utilsigtet sådanne data gennem logning. En nem måde at forhindre følsomme data i at komme ind i dine logfiler er at skrubbe toString()- metoderne for domæneenheder (såsom bruger, elev, lærer osv.). Dette vil forhindre dig i at udsende fortrolige felter ved et uheld. Hvis du bruger Lombok til at generere toString()metode, kan du bruge @ToString.Exclude- annoteringen for at forhindre, at et felt bruges i outputtet af toString()- metoden. Vær også meget forsigtig, når du sender data til omverdenen. Antag, at vi har et HTTP-endepunkt, der viser navnene på alle brugere. Det er ikke nødvendigt at vise en brugers unikke interne ID. Hvorfor? Fordi en angriber kunne bruge det til at få andre, mere følsomme oplysninger om brugeren. For eksempel, hvis du bruger Jackson til at serialisere/deserialisere en POJO til/fra JSON , så kan du bruge @JsonIgnore og @JsonIgnorePropertiesannoteringer for at forhindre serialisering/deserialisering af specifikke felter. Generelt skal du bruge forskellige POJO-klasser forskellige steder. Hvad betyder det?
  1. Når du arbejder med en database, skal du bruge én type POJO (en enhed).
  2. Når du arbejder med forretningslogik, skal du konvertere en enhed til en model.
  3. Når du arbejder med omverdenen og sender HTTP-anmodninger, skal du bruge forskellige entiteter (DTO'er).
På denne måde kan du tydeligt definere, hvilke felter der vil være synlige udefra, og hvilke der ikke vil.

Brug stærk kryptering og hashing-algoritmer

Kunders fortrolige data skal opbevares sikkert. For at gøre dette skal vi bruge kryptering. Afhængigt af opgaven skal du beslutte, hvilken type kryptering du skal bruge. Derudover tager stærkere kryptering mere tid, så igen skal du overveje, hvor meget behovet for det retfærdiggør den tid, der bruges på det. Du kan selvfølgelig selv skrive en krypteringsalgoritme. Men dette er unødvendigt. Du kan bruge eksisterende løsninger på dette område. For eksempel Google Tink :
<!-- https://mvnrepository.com/artifact/com.google.crypto.tink/tink -->
<dependency>
   <groupid>com.google.crypto.tink</groupid>
   <artifactid>tink</artifactid>
   <version>1.3.0</version>
</dependency>
Lad os se, hvad vi skal gøre ved at bruge dette eksempel, der involverer kryptering og dekryptering:
private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Elvis lives!";
   String aad = "Buddy Holly";

   Aead aead = handle.getPrimitive(Aead.class);
   byte[] encrypted = aead.encrypt(plaintext.getBytes(), aad.getBytes());
   String encryptedString = Base64.getEncoder().encodeToString(encrypted);
   System.out.println(encryptedString);

   byte[] decrypted = aead.decrypt(Base64.getDecoder().decode(encrypted), aad.getBytes());
   System.out.println(new String(decrypted));
}

Kryptering af adgangskoder

Til denne opgave er det sikrest at bruge asymmetrisk kryptering. Hvorfor? Fordi applikationen egentlig ikke behøver at dekryptere adgangskoder. Dette er standardtilgangen. I virkeligheden, når en bruger indtaster en adgangskode, krypterer systemet den og sammenligner den med, hvad der findes i adgangskodelageret. Den samme krypteringsproces udføres, så vi kan forvente, at de matcher, hvis det korrekte password indtastes, selvfølgelig :) BCrypt og SCrypt er velegnede her. Begge er envejsfunktioner (kryptografiske hashes) med beregningsmæssigt komplekse algoritmer, der tager lang tid. Det er præcis, hvad vi har brug for, da de direkte beregninger vil tage evigheder (nå, lang, lang tid). Spring Security understøtter en lang række algoritmer. Vi kan bruge SCryptPasswordEncoder og BCryptPasswordEncoder. Hvad der i øjeblikket betragtes som en stærk krypteringsalgoritme, kan blive anset for svag næste år. Som et resultat konkluderer vi, at vi regelmæssigt bør tjekke de algoritmer, vi bruger, og efter behov opdatere de biblioteker, der indeholder krypteringsalgoritmerne.

I stedet for en konklusion

I dag talte vi om sikkerhed, og naturligvis blev der efterladt mange ting bag kulisserne. Jeg har lige åbnet døren til en ny verden for dig, en verden der har sit eget liv. Sikkerhed er ligesom politik: Hvis du ikke beskæftiger dig med politik, vil politik beskæftige sig med dig. Jeg foreslår traditionelt, at du følger mig på GitHub-kontoen . Der poster jeg mine kreationer, der involverer forskellige teknologier, som jeg studerer og anvender på arbejdet.

Nyttige links

  1. Guru99: SQL Injection Tutorial
  2. Oracle: Java Security Resource Center
  3. Oracle: Secure Coding Guidelines for Java SE
  4. Baeldung: Grundlæggende om Java-sikkerhed
  5. Medium: 10 tips til at styrke din Java-sikkerhed
  6. Snyk: 10 bedste praksisser for Java-sikkerhed
  7. GitHub: Annoncering af GitHub Security Lab: sikring af verdens kode sammen
Kommentarer
  • Populær
  • Ny
  • Gammel
Du skal være logget ind for at skrive en kommentar
Denne side har ingen kommentarer endnu