CodeGym/Java blogg/Slumpmässig/Säkerhet i Java: bästa praxis
John Squirrels
Nivå
San Francisco

Säkerhet i Java: bästa praxis

Publicerad i gruppen
En av de viktigaste måtten i serverapplikationer är säkerhet. Detta är en typ av icke-funktionella krav . Säkerhet i Java: bästa praxis - 1Säkerhet inkluderar många komponenter. Naturligtvis skulle det krävas mer än en artikel för att helt täcka alla kända säkerhetsprinciper och säkerhetsåtgärder, så vi ska uppehålla oss vid det viktigaste. En person som är väl insatt i det här ämnet kan ställa in alla relevanta processer, undvika att skapa nya säkerhetshål och kommer att behövas i vilket team som helst. Naturligtvis ska du inte tro att din ansökan kommer att vara 100% säker om du följer dessa rutiner. Nej! Men det blir definitivt säkrare med dem. Nu går vi.

1. Tillhandahålla säkerhet på nivån för Java-språket

Först och främst börjar säkerheten i Java precis på nivån för språkets kapacitet. Vad skulle vi göra om det inte fanns några åtkomstmodifierare? Det skulle inte bli annat än anarki. Programmeringsspråket hjälper oss att skriva säker kod och använder även många implicita säkerhetsfunktioner:
  1. Starkt skrivande. Java är ett statiskt skrivet språk. Detta gör det möjligt att fånga typrelaterade fel under körning.
  2. Åtkomstmodifierare. Dessa tillåter oss att anpassa åtkomst till klasser, metoder och fält efter behov.
  3. Automatisk minneshantering. För detta har Java-utvecklare en skräpsamlare som befriar oss från att behöva konfigurera allt manuellt. Ja, ibland uppstår problem.
  4. Bytecode-verifiering : Java kompileras till bytecode, som kontrolleras av körtiden innan den körs.
Dessutom finns det Oracles säkerhetsrekommendationer . Naturligtvis är den inte skriven på högt språk och du kan somna flera gånger medan du läser den, men det är det värt. I synnerhet är dokumentet med titeln Secure Coding Guidelines for Java SE viktigt. Den ger råd om hur man skriver säker kod. Detta dokument förmedlar en enorm mängd mycket användbar information. Om du har chansen bör du definitivt läsa den. För att väcka ditt intresse för detta material, här är några intressanta tips:
  1. Undvik att serialisera säkerhetskänsliga klasser. Serialisering avslöjar klassgränssnittet i den serialiserade filen, för att inte tala om data som serialiseras.
  2. Försök att undvika föränderliga klasser för data. Detta ger alla fördelar med oföränderliga klasser (t.ex. gängsäkerhet). Om du har ett föränderligt objekt kan det leda till oväntat beteende.
  3. Gör kopior av returnerade föränderliga objekt. Om en metod returnerar en referens till ett internt föränderligt objekt, kan klientkoden ändra objektets interna tillstånd.
  4. Och så vidare…
I grund och botten är Secure Coding Guidelines for Java SE en samling tips och tricks om hur man skriver Java-kod korrekt och säkert.

2. Eliminera sårbarheter för SQL-injektion

Detta är en speciell sorts sårbarhet. Det är speciellt eftersom det är både en av de mest kända och en av de vanligaste sårbarheterna. Om du aldrig har varit intresserad av datorsäkerhet kommer du inte att veta om det. Vad är SQL-injektion? Detta är en databasattack som involverar injicering av ytterligare SQL-kod där det inte förväntas. Anta att vi har en metod som accepterar någon sorts parameter för att fråga databasen. Till exempel ett användarnamn. Sårbar kod skulle se ut ungefär så här:
// 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 det här exemplet förbereds en SQL-fråga i förväg på en separat rad. Så vad är problemet, eller hur? Kanske är problemet att det vore bättre att använda String.format ? Nej? Vadå då? Låt oss sätta oss i en testares skor och fundera på vad som kan anses vara värdet av firstName . Till exempel:
  1. Vi kan passera vad som förväntas – ett användarnamn. Då kommer databasen att returnera alla användare med det namnet.
  2. Vi kan skicka en tom sträng. Då kommer alla användare att returneras.
  3. Men vi kan också skicka följande: "'; SLAPP TABELL ANVÄNDARE;". Och här har vi nu huuuuuuge problem. Denna fråga kommer att ta bort en tabell från databasen. Tillsammans med all data. ALLT AV DET.
Kan du föreställa dig vilka problem detta skulle orsaka? Utöver det kan du skriva vad du vill. Du kan ändra namnen på alla användare. Du kan radera deras adresser. Utrymmet för sabotage är enormt. För att undvika detta måste du förhindra injiceringen av en färdig fråga och istället skapa frågan med hjälp av parametrar. Detta borde vara det enda sättet att skapa databasfrågor. Så här kan du eliminera denna sårbarhet. Till exempel:
// 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å så sätt undviks sårbarheten. För dem som vill dyka djupare in i den här artikeln, här är ett bra exempel . Hur vet du när du förstår denna sårbarhet? Om du förstår skämtet i serien nedan, så har du antagligen ett tydligt grepp om vad den här sårbarheten handlar om :DSäkerhet i Java: bästa praxis - 2

3. Skanna beroenden och håll dem uppdaterade

Vad betyder det? Om du inte vet vad ett beroende är så ska jag förklara. Ett beroende är ett JAR-arkiv med kod som är kopplat till ett projekt med hjälp av automatiska byggsystem (Maven, Gradle, Ant) för att kunna återanvända någon annans lösning. Till exempel Project Lombok , som genererar getters, sättare etc. åt oss under körtiden. Stora applikationer kan ha många och många beroenden. Vissa är transitiva (det vill säga varje beroende kan ha sina egna beroenden, och så vidare). Som ett resultat av detta uppmärksammar angripare i allt högre grad beroenden av öppen källkod, eftersom de används regelbundet och många klienter kan få problem på grund av dem. Det är viktigt att se till att det inte finns några kända sårbarheter i hela beroendeträdet (ja, det ser ut som ett träd). Det finns flera sätt att göra detta.

Använd Snyk för beroendeövervakning

Snyk kontrollerar alla projektberoenden och flaggar kända sårbarheter. Du kan registrera dig på Snyk och importera dina projekt via GitHub. Säkerhet i Java: bästa praxis - 3Dessutom, som du kan se från bilden ovan, om en sårbarhet är åtgärdad i en nyare version, kommer Snyk att erbjuda korrigeringen och skapa en pull-begäran. Du kan använda det gratis för projekt med öppen källkod. Projekt skannas med jämna mellanrum, t.ex. en gång i veckan, en gång i månaden. Jag registrerade och la till alla mina offentliga arkiv till Snyk-skanningen (det finns inget farligt med detta, eftersom de redan är offentliga för alla). Snyk visade sedan skanningsresultatet: Säkerhet i Java: bästa praxis - 4Och efter ett tag förberedde Snyk-bot flera pull-förfrågningar i projekt där beroenden behöver uppdateras: Säkerhet i Java: bästa praxis - 5Och även:Säkerhet i Java: bästa praxis - 6Det här är ett utmärkt verktyg för att hitta sårbarheter och övervaka uppdateringar för nya versioner.

Använd GitHub Security Lab

Alla som arbetar på GitHub kan dra nytta av dess inbyggda verktyg. Du kan läsa mer om detta tillvägagångssätt i deras blogginlägg med titeln Announcing GitHub Security Lab . Det här verktyget är naturligtvis enklare än Snyk, men du bör definitivt inte försumma det. Dessutom kommer antalet kända sårbarheter bara att växa, så både Snyk och GitHub Security Lab kommer att fortsätta att expandera och förbättras.

Aktivera Sonatype DepShield

Om du använder GitHub för att lagra dina repositories kan du lägga till Sonatype DepShield, en av applikationerna på MarketPlace, till dina projekt. Den kan också användas för att skanna projekt efter beroenden. Dessutom, om den hittar något, kommer ett GitHub-problem att genereras med en lämplig beskrivning som visas nedan:Säkerhet i Java: bästa praxis - 7

4. Hantera konfidentiell data varsamt

Vi kan alternativt använda frasen "känsliga uppgifter". Att läcka en kunds personliga information, kreditkortsnummer och annan känslig information kan orsaka irreparabel skada. Först av allt, ta en närmare titt på designen av din applikation och avgör om du verkligen behöver den eller den informationen. Kanske behöver du faktiskt inte en del av den data du har – data som har lagts till för en framtid som inte har kommit och som sannolikt inte kommer. Dessutom läcker du många oavsiktligt sådan data genom loggning. Ett enkelt sätt att förhindra att känslig data kommer in i dina loggar är att rensa toString() -metoderna för domänentiteter (som användare, student, lärare, etc.). Detta kommer att förhindra att du av misstag matar ut konfidentiella fält. Om du använder Lombok för att generera toString()metoden kan du använda @ToString.Exclude -anteckningen för att förhindra att ett fält används i utdata från metoden toString() . Var också mycket försiktig när du skickar data till omvärlden. Anta att vi har en HTTP-slutpunkt som visar namnen på alla användare. Det finns inget behov av att visa en användares unika interna ID. Varför? Eftersom en angripare kan använda den för att få annan, känsligare information om användaren. Om du till exempel använder Jackson för att serialisera/avserialisera en POJO till/från JSON , kan du använda @JsonIgnore och @JsonIgnorePropertiesanteckningar för att förhindra serialisering/deserialisering av specifika fält. Generellt sett måste du använda olika POJO-klasser på olika platser. Vad betyder det?
  1. När du arbetar med en databas, använd en typ av POJO (en entitet).
  2. När du arbetar med affärslogik, konvertera en enhet till en modell.
  3. När du arbetar med omvärlden och skickar HTTP-förfrågningar, använd olika entiteter (DTOs).
På så sätt kan du tydligt definiera vilka fält som ska synas utifrån och vilka som inte kommer att synas.

Använd stark kryptering och hashalgoritmer

Kundernas konfidentiella data måste förvaras säkert. För att göra detta måste vi använda kryptering. Beroende på uppgiften måste du bestämma vilken typ av kryptering som ska användas. Dessutom tar starkare kryptering mer tid, så återigen måste du överväga hur mycket behovet av det motiverar den tid som spenderas på det. Naturligtvis kan du själv skriva en krypteringsalgoritm. Men detta är onödigt. Du kan använda befintliga lösningar inom detta område. Till exempel, 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>
Låt oss se vad vi ska göra med det här exemplet som involverar kryptering och 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));
}

Kryptera lösenord

För denna uppgift är det säkrast att använda asymmetrisk kryptering. Varför? Eftersom applikationen egentligen inte behöver dekryptera lösenord. Detta är standardmetoden. I verkligheten, när en användare anger ett lösenord, krypterar systemet det och jämför det med vad som finns i lösenordslagret. Samma krypteringsprocess utförs, så vi kan förvänta oss att de kommer att matcha, om rätt lösenord skrivs in förstås :) BCrypt och SCrypt passar här. Båda är envägsfunktioner (kryptografiska hash) med beräkningsmässigt komplexa algoritmer som tar lång tid. Detta är precis vad vi behöver, eftersom de direkta beräkningarna kommer att ta evigheter (nåja, lång, lång tid). Spring Security stöder en hel rad algoritmer. Vi kan använda SCryptPasswordEncoder och BCryptPasswordEncoder. Det som för närvarande anses vara en stark krypteringsalgoritm kan anses vara svag nästa år. Som ett resultat drar vi slutsatsen att vi regelbundet bör kontrollera de algoritmer vi använder och vid behov uppdatera biblioteken som innehåller krypteringsalgoritmerna.

Istället för en slutsats

Idag har vi pratat om säkerhet och naturligtvis lämnades mycket saker bakom kulisserna. Jag har precis öppnat dörren till en ny värld för dig, en värld som har ett eget liv. Säkerhet är precis som politik: om du inte sysslar med politik, kommer politiken att sysselsätta sig med dig. Jag föreslår traditionellt att du följer mig på GitHub-kontot . Där lägger jag upp mina skapelser som involverar olika tekniker som jag studerar och tillämpar på jobbet.

Användbara länkar

  1. Guru99: SQL Injection Tutorial
  2. Oracle: Java Security Resource Center
  3. Oracle: riktlinjer för säker kodning för Java SE
  4. Baeldung: Grunderna i Java-säkerhet
  5. Medium: 10 tips för att stärka din Java-säkerhet
  6. Snyk: 10 bästa metoder för Java-säkerhet
  7. GitHub: Tillkännager GitHub Security Lab: säkrar världens kod tillsammans
Kommentarer
  • Populär
  • Ny
  • Gammal
Du måste vara inloggad för att lämna en kommentar
Den här sidan har inga kommentarer än