En av de viktigste beregningene i serverapplikasjoner er sikkerhet. Dette er en type ikke-funksjonelle krav .
Sikkerhet 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å.
Også, 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:
Og etter en stund forberedte Snyk-bot flere pull-forespørsler i prosjekter der avhengigheter må oppdateres:
Og også:
Dette er et flott verktøy for å finne sårbarheter og overvåke oppdateringer for nye versjoner.

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:- Sterk skriving. Java er et statisk skrevet språk. Dette gjør det mulig å fange typerelaterte feil under kjøring.
- Tilgangsmodifikatorer. Disse lar oss tilpasse tilgang til klasser, metoder og felt etter behov.
- 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.
- Bytekodebekreftelse : Java kompileres til bytekode, som kontrolleres av kjøretiden før den kjøres.
- Unngå å serialisere sikkerhetssensitive klasser. Serialisering avslører klassegrensesnittet i den serialiserte filen, for ikke å nevne dataene som er serialisert.
- 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.
- 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.
- Og så videre…
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:
- Vi kan passere det som forventes - et brukernavn. Da vil databasen returnere alle brukere med det navnet.
- Vi kan sende en tom streng. Da vil alle brukere bli returnert.
- 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.
// 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 :D
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.



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:
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?- Når du arbeider med en database, bruk én type POJO (en enhet).
- Når du arbeider med forretningslogikk, konverter en enhet til en modell.
- Når du arbeider med omverdenen og sender HTTP-forespørsler, bruk forskjellige entiteter (DTOer).
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
- Guru99: SQL Injection Tutorial
- Oracle: Java Security Resource Center
- Oracle: retningslinjer for sikker koding for Java SE
- Baeldung: Grunnleggende om Java-sikkerhet
- Medium: 10 tips for å styrke Java-sikkerheten din
- Snyk: 10 beste fremgangsmåter for Java-sikkerhet
- GitHub: Kunngjøring av GitHub Security Lab: sikring av verdens kode sammen
GO TO FULL VERSION