CodeGym/Java-blogg/Tilfeldig/Sikkerhet i Java: beste praksis
John Squirrels
Nivå
San Francisco

Sikkerhet i Java: beste praksis

Publisert i gruppen
En av de viktigste beregningene i serverapplikasjoner er sikkerhet. Dette er en type ikke-funksjonelle krav . Sikkerhet i Java: beste praksis - 1Sikkerhet inkluderer mange komponenter. Selvfølgelig vil det kreve mer enn én artikkel for å dekke alle kjente sikkerhetsprinsipper og sikkerhetstiltak fullt ut, så vi skal dvele ved det viktigste. En person som er godt kjent med dette emnet kan sette opp alle relevante prosesser, unngå å lage nye sikkerhetshull, og vil være nødvendig på ethvert lag. Selvfølgelig skal du ikke tro at søknaden din vil være 100 % sikker hvis du følger denne praksisen. Nei! Men det blir garantert sikrere med dem. La oss gå.

1. Sørg for sikkerhet på nivå med Java-språket

Først og fremst starter sikkerheten i Java rett på nivået av språkets evner. Hva ville vi gjort hvis det ikke fantes noen tilgangsmodifikatorer? Det ville ikke være annet enn anarki. Programmeringsspråket hjelper oss med å skrive sikker kode og bruker også mange implisitte sikkerhetsfunksjoner:
  1. Sterk skriving. Java er et statisk skrevet språk. Dette gjør det mulig å fange typerelaterte feil under kjøring.
  2. Tilgangsmodifikatorer. Disse lar oss tilpasse tilgang til klasser, metoder og felt etter behov.
  3. Automatisk minnehåndtering. For dette har Java-utviklere en søppelsamler som frigjør oss fra å måtte konfigurere alt manuelt. Ja, noen ganger oppstår det problemer.
  4. Bytekodebekreftelse : Java kompileres til bytekode, som kontrolleres av kjøretiden før den kjøres.
I tillegg er det Oracles sikkerhetsanbefalinger . Selvfølgelig er det ikke skrevet på et høyt språk, og du kan sovne flere ganger mens du leser den, men det er verdt det. Spesielt er dokumentet med tittelen Secure Coding Guidelines for Java SE viktig. Den gir råd om hvordan du skriver sikker kode. Dette dokumentet formidler en enorm mengde svært nyttig informasjon. Hvis du har sjansen, bør du definitivt lese den. For å vekke interessen din for dette materialet, her er noen interessante tips:
  1. Unngå å serialisere sikkerhetssensitive klasser. Serialisering avslører klassegrensesnittet i den serialiserte filen, for ikke å nevne dataene som er serialisert.
  2. Prøv å unngå foranderlige klasser for data. Dette gir alle fordelene med uforanderlige klasser (f.eks. gjengesikkerhet). Hvis du har et objekt som kan endres, kan det føre til uventet oppførsel.
  3. Lag kopier av returnerte mutbare objekter. Hvis en metode returnerer en referanse til et internt mutbart objekt, kan klientkoden endre den interne tilstanden til objektet.
  4. Og så videre…
I utgangspunktet er Secure Coding Guidelines for Java SE en samling tips og triks for hvordan du skriver Java-kode riktig og sikkert.

2. Eliminer SQL-injeksjonssårbarheter

Dette er en spesiell type sårbarhet. Det er spesielt fordi det er både en av de mest kjente og en av de vanligste sårbarhetene. Hvis du aldri har vært interessert i datasikkerhet, vil du ikke vite om det. Hva er SQL-injeksjon? Dette er et databaseangrep som innebærer å injisere ekstra SQL-kode der det ikke er forventet. Anta at vi har en metode som aksepterer en slags parameter for å spørre databasen. For eksempel et brukernavn. Sårbar kode vil se omtrent slik ut:
// 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 eksemplet er en SQL-spørring forberedt på forhånd på en egen linje. Så hva er problemet, ikke sant? Kanskje problemet er at det ville vært bedre å bruke String.format ? Nei? Vel, hva da? La oss sette oss inn i en testers sko og tenke på hva som kan brukes som verdien av fornavn . For eksempel:
  1. Vi kan passere det som forventes - et brukernavn. Da vil databasen returnere alle brukere med det navnet.
  2. Vi kan sende en tom streng. Da vil alle brukere bli returnert.
  3. Men vi kan også sende følgende: "'; DROP TABLE USERS;". Og her har vi nå huuuuuuge problemer. Denne spørringen vil slette en tabell fra databasen. Sammen med alle dataene. ALT.
Kan du forestille deg problemene dette vil føre til? Utover det kan du skrive hva du vil. Du kan endre navnene på alle brukerne. Du kan slette adressene deres. Muligheten for sabotasje er enorm. For å unngå dette må du forhindre injeksjon av en ferdig spørring og i stedet danne spørringen ved hjelp av parametere. Dette bør være den eneste måten å lage databasespørringer på. Slik kan du eliminere denne sårbarheten. 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åten unngås sårbarheten. For de som ønsker å dykke dypere inn i denne artikkelen, her er et godt eksempel . Hvordan vet du når du forstår denne sårbarheten? Hvis du får vitsen i tegneserien nedenfor, så har du sannsynligvis en klar forståelse av hva denne sårbarheten handler om :DSikkerhet i Java: beste praksis - 2

3. Skann avhengigheter og hold dem oppdatert

Hva betyr det? Hvis du ikke vet hva en avhengighet er, skal jeg forklare. En avhengighet er et JAR-arkiv med kode som er koblet til et prosjekt ved hjelp av automatiske byggesystemer (Maven, Gradle, Ant) for å gjenbruke andres løsning. For eksempel Project Lombok , som genererer gettere, settere osv. for oss i løpetid. Store applikasjoner kan ha mange og mange avhengigheter. Noen er transitive (det vil si at hver avhengighet kan ha sine egne avhengigheter, og så videre). Som et resultat tar angripere i økende grad oppmerksomhet til åpen kildekode-avhengigheter, siden de brukes regelmessig og mange klienter kan ha problemer på grunn av dem. Det er viktig å sørge for at det ikke er kjente sårbarheter i hele avhengighetstreet (ja, det ser ut som et tre). Det er flere måter å gjøre dette på.

Bruk Snyk for avhengighetsovervåking

Snyk sjekker alle prosjektavhengigheter og flagger kjente sårbarheter. Du kan registrere deg på Snyk og importere prosjektene dine via GitHub. Sikkerhet i Java: beste praksis - 3Også, som du kan se fra bildet ovenfor, hvis en sårbarhet er fikset i en nyere versjon, vil Snyk tilby reparasjonen og opprette en pull-forespørsel. Du kan bruke det gratis for åpen kildekode-prosjekter. Prosjekter skannes med jevne mellomrom, f.eks. en gang i uken, en gang i måneden. Jeg registrerte og la til alle mine offentlige depoter til Snyk-skanningen (det er ikke noe farlig med dette, siden de allerede er offentlige for alle). Snyk viste deretter skanneresultatet: Sikkerhet i Java: beste praksis - 4Og etter en stund forberedte Snyk-bot flere pull-forespørsler i prosjekter der avhengigheter må oppdateres: Sikkerhet i Java: beste praksis - 5Og også:Sikkerhet i Java: beste praksis - 6Dette er et flott verktøy for å finne sårbarheter og overvåke oppdateringer for nye versjoner.

Bruk GitHub Security Lab

Alle som jobber på GitHub kan dra nytte av de innebygde verktøyene. Du kan lese mer om denne tilnærmingen i deres blogginnlegg med tittelen Announcing GitHub Security Lab . Dette verktøyet er selvfølgelig enklere enn Snyk, men du bør definitivt ikke overse det. Dessuten vil antallet kjente sårbarheter bare vokse, så både Snyk og GitHub Security Lab vil fortsette å utvide og forbedre seg.

Aktiver Sonatype DepShield

Hvis du bruker GitHub til å lagre lagrene dine, kan du legge til Sonatype DepShield, en av applikasjonene på MarketPlace, til prosjektene dine. Den kan også brukes til å skanne prosjekter for avhengigheter. Dessuten, hvis den finner noe, vil et GitHub-problem genereres med en passende beskrivelse som vist nedenfor:Sikkerhet i Java: beste praksis - 7

4. Håndter konfidensielle data med forsiktighet

Vi kan alternativt bruke uttrykket "sensitive data". Å lekke en kundes personopplysninger, kredittkortnumre og annen sensitiv informasjon kan forårsake uopprettelig skade. Først av alt, ta en nærmere titt på utformingen av applikasjonen din og avgjør om du virkelig trenger denne eller den dataen. Kanskje du faktisk ikke trenger noen av dataene du har – data som ble lagt til for en fremtid som ikke har kommet og som neppe kommer. I tillegg lekker mange utilsiktet slike data gjennom logging. En enkel måte å forhindre at sensitive data kommer inn i loggene dine, er å skrubbe toString()- metodene til domeneenheter (som bruker, student, lærer osv.). Dette vil hindre deg i å skrive ut konfidensielle felt ved et uhell. Hvis du bruker Lombok til å generere toString()metoden, kan du bruke @ToString.Exclude -kommentaren for å forhindre at et felt brukes i utdataene til toString()- metoden. Vær også veldig forsiktig når du sender data til omverdenen. Anta at vi har et HTTP-endepunkt som viser navnene på alle brukere. Det er ikke nødvendig å vise en brukers unike interne ID. Hvorfor? Fordi en angriper kan bruke den til å få annen, mer sensitiv informasjon om brukeren. For eksempel, hvis du bruker Jackson til å serialisere/deserialisere en POJO til/fra JSON , kan du bruke @JsonIgnore og @JsonIgnorePropertiesmerknader for å forhindre serialisering/deserialisering av spesifikke felt. Generelt må du bruke forskjellige POJO-klasser på forskjellige steder. Hva betyr det?
  1. Når du arbeider med en database, bruk én type POJO (en enhet).
  2. Når du arbeider med forretningslogikk, konverter en enhet til en modell.
  3. Når du arbeider med omverdenen og sender HTTP-forespørsler, bruk forskjellige entiteter (DTOer).
På denne måten kan du tydelig definere hvilke felt som vil være synlige fra utsiden og hvilke som ikke vil.

Bruk sterk kryptering og hashing-algoritmer

Kunders konfidensielle data skal oppbevares sikkert. For å gjøre dette må vi bruke kryptering. Avhengig av oppgaven må du bestemme hvilken type kryptering du skal bruke. I tillegg tar sterkere kryptering mer tid, så igjen må du vurdere hvor mye behovet for det rettferdiggjør tiden brukt på det. Du kan selvfølgelig skrive en krypteringsalgoritme selv. Men dette er unødvendig. Du kan bruke eksisterende løsninger på dette området. 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>
La oss se hva vi skal gjøre ved å bruke dette eksemplet som 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 av passord

For denne oppgaven er det tryggest å bruke asymmetrisk kryptering. Hvorfor? Fordi applikasjonen egentlig ikke trenger å dekryptere passord. Dette er standardtilnærmingen. I virkeligheten, når en bruker skriver inn et passord, krypterer systemet det og sammenligner det med det som finnes i passordlageret. Den samme krypteringsprosessen utføres, så vi kan forvente at de vil matche, hvis riktig passord er oppgitt, selvfølgelig :) BCrypt og SCrypt passer her. Begge er enveisfunksjoner (kryptografiske hasher) med beregningsmessig komplekse algoritmer som tar lang tid. Dette er akkurat det vi trenger, siden de direkte beregningene vil ta evigheter (vel, lang, lang tid). Spring Security støtter en hel rekke algoritmer. Vi kan bruke SCryptPasswordEncoder og BCryptPasswordEncoder. Det som for øyeblikket anses som en sterk krypteringsalgoritme kan anses som svak neste år. Som et resultat konkluderer vi med at vi regelmessig bør sjekke algoritmene vi bruker og etter behov oppdatere bibliotekene som inneholder krypteringsalgoritmene.

I stedet for en konklusjon

I dag snakket vi om sikkerhet, og naturlig nok ble det lagt igjen mye bak kulissene. Jeg åpnet nettopp døren til en ny verden for deg, en verden som har et eget liv. Sikkerhet er akkurat som politikk: Hvis du ikke er opptatt av politikk, vil politikken være opptatt med deg. Jeg foreslår tradisjonelt at du følger meg på GitHub-kontoen . Der legger jeg ut kreasjonene mine som involverer ulike teknologier som jeg studerer og bruker på jobben.

Nyttige lenker

  1. Guru99: SQL Injection Tutorial
  2. Oracle: Java Security Resource Center
  3. Oracle: retningslinjer for sikker koding for Java SE
  4. Baeldung: Grunnleggende om Java-sikkerhet
  5. Medium: 10 tips for å styrke Java-sikkerheten din
  6. Snyk: 10 beste fremgangsmåter for Java-sikkerhet
  7. GitHub: Kunngjøring av GitHub Security Lab: sikring av verdens kode sammen
Kommentarer
  • Populær
  • Ny
  • Gammel
Du må være pålogget for å legge igjen en kommentar
Denne siden har ingen kommentarer ennå