Die Grundlagen der Java-Sicherheit

Java Top

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs

1. Übersicht

In diesem Tutorial werden die Grundlagen der Sicherheit auf der Java-Plattform erläutert. Wir werden uns auch darauf konzentrieren, was uns zum Schreiben sicherer Anwendungen zur Verfügung steht.

Sicherheit ist ein großes Thema, das viele Bereiche umfasst . Einige davon sind Teil der Sprache selbst, z. B. Zugriffsmodifikatoren und Klassenlader. Darüber hinaus sind andere als Dienste verfügbar, die Datenverschlüsselung, sichere Kommunikation, Authentifizierung und Autorisierung umfassen, um nur einige zu nennen.

Daher ist es nicht praktisch, in diesem Tutorial einen aussagekräftigen Einblick in all dies zu erhalten. Wir werden jedoch versuchen, zumindest einen aussagekräftigen Wortschatz zu erlangen.

2. Sprachfunktionen

Die Sicherheit in Java beginnt vor allem auf der Ebene der Sprachfunktionen . Dies ermöglicht es uns, sicheren Code zu schreiben und von vielen impliziten Sicherheitsfunktionen zu profitieren:

  • Statische Datentypisierung: Java ist eine statisch typisierte Sprache, die die Möglichkeiten zur Laufzeiterkennung typbezogener Fehler verringert
  • Zugriffsmodifikatoren: Mit Java können wir verschiedene Zugriffsmodifikatoren wie öffentlich und privat verwenden, um den Zugriff auf Felder, Methoden und Klassen zu steuern
  • Automatische Speicherverwaltung: Java verfügt über eine auf Speicherbereinigung basierende Speicherverwaltung , die Entwickler von der manuellen Verwaltung befreit
  • Bytecode-Überprüfung: Java ist eine kompilierte Sprache. Dies bedeutet, dass Code in plattformunabhängigen Bytecode konvertiert wird. Die Laufzeit überprüft jeden Bytecode, den es zur Ausführung lädt

Dies ist keine vollständige Liste der Sicherheitsfunktionen, die Java bietet, aber es ist gut genug, um uns eine gewisse Sicherheit zu geben!

3. Sicherheitsarchitektur in Java

Bevor wir uns mit bestimmten Bereichen befassen, sollten wir uns einige Zeit mit der Kernarchitektur der Sicherheit in Java befassen.

Die Kernprinzipien der Sicherheit in Java basieren auf interoperablen und erweiterbaren Provider- Implementierungen . Eine bestimmte Implementierung des Anbieters kann einige oder alle Sicherheitsdienste implementieren.

Einige der typischen Dienste, die ein Anbieter implementieren kann, sind beispielsweise:

  • Kryptografische Algorithmen (wie DSA, RSA oder SHA-256)
  • Funktionen zur Schlüsselgenerierung, -konvertierung und -verwaltung (z. B. für algorithmische Schlüssel)

Java wird mit vielen integrierten Anbietern ausgeliefert . Außerdem kann eine Anwendung mehrere Anbieter mit einer bevorzugten Reihenfolge konfigurieren.

Folglich sucht das Provider-Framework in Java nach einer bestimmten Implementierung eines Dienstes in allen Providern in der Reihenfolge, in der sie bevorzugt werden.

Darüber hinaus ist es in dieser Architektur immer möglich, benutzerdefinierte Anbieter mit steckbaren Sicherheitsfunktionen zu implementieren.

4. Kryptographie

Kryptographie ist der Eckpfeiler der Sicherheitsfunktionen im Allgemeinen und in Java. Dies bezieht sich auf Werkzeuge und Techniken für eine sichere Kommunikation in Gegenwart von Gegnern .

4.1. Java-Kryptographie

Die Java Cryptographic Architecture (JCA) bietet ein Framework für den Zugriff auf und die Implementierung von kryptografischen Funktionen in Java, einschließlich:

  • Digitale Signaturen
  • Nachrichtenübersichten
  • Symmetrische und asymmetrische Chiffren
  • Nachrichtenauthentifizierungscodes
  • Schlüsselgeneratoren und Schlüsselfabriken

Am wichtigsten ist, dass Java Provider- basierte Implementierungen für kryptografische Funktionen verwendet.

Darüber hinaus enthält Java integrierte Anbieter für häufig verwendete kryptografische Algorithmen wie RSA, DSA und AES, um nur einige zu nennen. Wir können diese Algorithmen verwenden, um Daten in Ruhe, in Gebrauch oder in Bewegung mehr Sicherheit zu verleihen .

4.2. Kryptographie in der Praxis

Ein sehr häufiger Anwendungsfall in Anwendungen ist das Speichern von Benutzerkennwörtern. Wir verwenden dies zur Authentifizierung zu einem späteren Zeitpunkt. Nun ist es offensichtlich, dass das Speichern von Nur-Text-Passwörtern die Sicherheit gefährdet.

Eine Lösung besteht also darin, die Passwörter so zu verschlüsseln, dass der Vorgang wiederholbar ist, jedoch nur in eine Richtung. Dieser Prozess ist als kryptografische Hash-Funktion bekannt, und SHA1 ist ein solcher beliebter Algorithmus.

Mal sehen, wie wir das in Java machen können:

MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hashedPassword = md.digest("password".getBytes());

Hier ist MessageDigest ein kryptografischer Dienst, an dem wir interessiert sind. Wir verwenden die Methode getInstance (), um diesen Dienst von einem der verfügbaren Sicherheitsanbieter anzufordern .

5. Infrastruktur für öffentliche Schlüssel

Public Key Infrastructure (PKI) bezieht sich auf das Setup, das den sicheren Informationsaustausch über das Netzwerk mithilfe der Verschlüsselung mit öffentlichem Schlüssel ermöglicht . Dieses Setup basiert auf Vertrauen, das zwischen den an der Kommunikation beteiligten Parteien aufgebaut wird. Diese Vertrauensstellung basiert auf digitalen Zertifikaten, die von einer neutralen und vertrauenswürdigen Behörde ausgestellt wurden, die als Certificate Authority (CA) bezeichnet wird.

5.1. PKI-Unterstützung in Java

Die Java-Plattform verfügt über APIs, die die Erstellung, Speicherung und Validierung digitaler Zertifikate erleichtern:

  • KeyStore: Java provides the KeyStore class for persistent storage of cryptographic keys and trusted certificates. Here, KeyStore can represent both key-store and trust-store files. These files have similar content but vary in their usage.
  • CertStore: Additionally, Java has the CertStore class, which represents a public repository of potentially untrusted certificates and revocation lists. We need to retrieve certificates and revocation lists for certificate path building amongst other usages.

Java has a built-in trust-store called “cacerts” that contains certificates for well known CAs.

5.2. Java Tools for PKI

Java has some really handy tools to facilitate trusted communication:

  • There is a built-in tool called “keytool” to create and manage key-store and trust-store
  • There is also another tool “jarsigner” that we can use to sign and verify JAR files

5.3. Working with Certificates in Java

Let's see how we can work with certificates in Java to establish a secure connection using SSL. A mutually authenticated SSL connection requires us to do two things:

  • Present Certificate — We need to present a valid certificate to another party in the communication. For that, we need to load the key-store file, where we must have our public keys:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); char[] keyStorePassword = "changeit".toCharArray(); try(InputStream keyStoreData = new FileInputStream("keystore.jks")){ keyStore.load(keyStoreData, keyStorePassword); }
  • Verify Certificate — We also need to verify the certificate presented by another party in the communication. For this we need to load the trust-store, where we must have previously trusted certificates from other parties:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); // Load the trust-store from filesystem as before

We rarely have to do this programmatically and normally pass system parameters to Java at runtime:

-Djavax.net.ssl.trustStore=truststore.jks -Djavax.net.ssl.keyStore=keystore.jks

6. Authentication

Authentication is the process of verifying the presented identity of a user or machine based on additional data like password, token, or a variety of other credentials available today.

6.1. Authentication in Java

Java APIs makes use of pluggable login modules to provide different and often multiple authentication mechanisms to applications. LoginContext provides this abstraction, which in turn refers to configuration and loads an appropriate LoginModule.

While multiple providers make available their login modules, Java has some default ones available for use:

  • Krb5LoginModule, for Kerberos-based authentication
  • JndiLoginModule, for username and password-based authentication backed by an LDAP store
  • KeyStoreLoginModule, for cryptographic key-based authentication

6.2. Login by Example

One of the most common mechanisms of authentication is the username and password. Let's see how we can achieve this through JndiLoginModule.

This module is responsible for getting the username and password from a user and verifying it against a directory service configured in JNDI:

LoginContext loginContext = new LoginContext("Sample", new SampleCallbackHandler()); loginContext.login();

Here, we are using an instance of LoginContext to perform the login. LoginContext takes the name of an entry in the login configuration — in this case, it's “Sample”. Also, we have to provide an instance of CallbackHandler, using the LoginModule that interacts with the user for details like username and password.

Let's take a look at our login configuration:

Sample { com.sun.security.auth.module.JndiLoginModule required; };

Simple enough, it suggests that we're using JndiLoginModule as a mandatory LoginModule.

7. Secure Communication

Communication over the network is vulnerable to many attack vectors. For instance, someone may tap into the network and read our data packets as they're being transferred. Over the years, the industry has established many protocols to secure this communication.

7.1. Java Support for Secure Communication

Java provides APIs to secure network communication with encryption, message integrity, and both client and server authentication:

  • SSL/TLS: SSL and its successor, TLS, provide security over untrusted network communication through data encryption and public-key infrastructure. Java provides support of SSL/TLS through SSLSocket defined in the package “java.security.ssl“.
  • SASL: Simple Authentication and Security Layer (SASL) is a standard for authentication between client and server. Java supports SASL as part of the package “java.security.sasl“.
  • GGS-API/Kerberos: Generic Security Service API (GSS-API) offers uniform access to security services over a variety of security mechanisms like Kerberos v5. Java supports GSS-API as part of the package “java.security.jgss“.

7.2. SSL Communication in Action

Let's now see how we can open a secure connection with other parties in Java using SSLSocket:

SocketFactory factory = SSLSocketFactory.getDefault(); try (Socket connection = factory.createSocket(host, port)) { BufferedReader input = new BufferedReader( new InputStreamReader(connection.getInputStream())); return input.readLine(); }

Here, we are using SSLSocketFactory to create SSLSocket. As part of this, we can set optional parameters like cipher suites and which protocol to use.

For this to work properly, we must have created and set our key-store and trust-store as we saw earlier.

8. Access Control

Access Control refers to protecting sensitive resources like a filesystem or codebase from unwarranted access. This is typically achieved by restricting access to such resources.

8.1. Access Control in Java

We can achieve access control in Java using classes Policy and Permission mediated through the SecurityManager class. SecurityManager is part of the “java.lang” package and is responsible for enforcing access control checks in Java.

When the class loader loads a class in the runtime, it automatically grants some default permissions to the class encapsulated in the Permission object. Beyond these default permissions, we can grant more leverage to a class through security policies. These are represented by the class Policy.

During the sequence of code execution, if the runtime encounters a request for a protected resource, SecurityManager verifies the requested Permission against the installed Policy through the call stack. Consequently, it either grants permission or throws SecurityException.

8.2. Java Tools for Policy

Java has a default implementation of Policy that reads authorization data from the properties file. However, the policy entries in these policy files have to be in a specific format.

Java ships with “policytool”, a graphical utility to compose policy files.

8.3. Access Control Through Example

Let's see how we can restrict access to a resource like a file in Java:

SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { securityManager.checkPermission( new FilePermission("/var/logs", "read")); }

Here, we're using SecurityManager to validate our read request for a file, wrapped in FilePermission.

But, SecurityManager delegates this request to AccessController. AccessController internally makes use of the installed Policy to arrive at a decision.

Let's see an example of the policy file:

grant { permission java.security.FilePermission <>, "read"; };

We are essentially granting read permission to all files for everyone. But, we can provide much more fine-grained control through security policies.

It's worth noting that a SecurityManager might not be installed by default in Java. We can ensure this by always starting Java with the parameter:

-Djava.security.manager -Djava.security.policy=/path/to/sample.policy

9. XML Signature

XML signatures are useful in securing data and provide data integrity. W3C provides recommendations for governance of XML Signature. We can use XML signature to secure data of any type, like binary data.

9.1. XML Signature in Java

Java API supports generating and validating XML signatures as per the recommended guidelines. Java XML Digital Signature API is encapsulated in the package “java.xml.crypto“.

The signature itself is just an XML document. XML signatures can be of three types:

  • Detached: This type of signature is over the data that is external to the Signature element
  • Enveloping: This type of signature is over the data that is internal to the Signature element
  • Enveloped: This type of signature is over the data that contains the Signature element itself

Certainly, Java supports creating and verifying all the above types of XML signatures.

9.2. Creating an XML Signature

Now, we'll roll up our sleeves and generate an XML signature for our data. For instance, we may be about to send an XML document over the network. Hence, we would want our recipient to be able to verify its integrity.

So, let's see how we can achieve this in Java:

XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM"); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); Document document = documentBuilderFactory .newDocumentBuilder().parse(new FileInputStream("data.xml")); DOMSignContext domSignContext = new DOMSignContext( keyEntry.getPrivateKey(), document.getDocumentElement()); XMLSignature xmlSignature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo); xmlSignature.sign(domSignContext);

To clarify, we're generating an XML signature for our data present in the file “data.xml”. Meanwhile, there are a few things to note about this piece of code:

  • Firstly, XMLSignatureFactory is the factory class for generating XML signatures
  • XMLSigntaure requires a SignedInfo object over which it calculates the signature
  • XMLSigntaure also needs KeyInfo, which encapsulates the signing key and certificate
  • Finally, XMLSignature signs the document using the private key encapsulated as DOMSignContext

As a result, the XML document will now contain the Signature element, which can be used to verify its integrity.

10. Security Beyond Core Java

As we have seen by now, the Java platform provides a lot of the necessary functionality to write secure applications. However, sometimes, these are quite low-level and not directly applicable to, for example, the standard security mechanism on the web.

For example, when working on our system, we generally don't want to have to read the full OAuth RFC and implement that ourselves. We often need quicker, higher-level ways to achieve security. This is where application frameworks come into the picture – these help us achieve our objective with much less boilerplate code.

And, on the Java platform – generally that means Spring Security. The framework is part of the Spring ecosystem, but it can actually be used outside of pure Spring application.

In simple terms, it helps is achieve authentication, authorization and other security features in a simple, declarative, high-level manner.

Of course, Spring Security is extensively covered in a series of tutorials, as well as in a guided way, in the Learn Spring Security course.

11. Conclusion

In short, in this tutorial, we went through the high-level architecture of security in Java. Also, we understood how Java provides us with implementations of some of the standard cryptographic services.

We also saw some of the common patterns that we can apply to achieve extensible and pluggable security in areas like authentication and access control.

Zusammenfassend gibt uns dies nur einen kleinen Einblick in die Sicherheitsfunktionen von Java. Folglich verdient jeder der in diesem Tutorial behandelten Bereiche eine weitere Untersuchung. Aber hoffentlich sollten wir genug Einsicht haben, um in diese Richtung zu starten!

Java unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs