Einführung

Cloudflight hat kürzlich eine Meetup-Kooperation mit SBA Research gestartet. Der erste von uns gehaltene Vortrag fand am 25. März 2020 online statt und demonstrierte einige häufige Sicherheitsfallen in Webanwendungen, die mit dem Spring-Framework entwickelt wurden. Falls ihr den Meetup-Vortrag verpasst habt, werden wir einige der dort präsentierten Inhalte in diesem Blogbeitrag behandeln.

Spring ist ein Open-Source-Anwendungsframework für die Java-Plattform. In mehreren Jahren Penetrationstests haben wir einige häufige Fallstricke bei der Verwendung von Spring entdeckt. Wir sprechen hier nicht von Bugs im Framework selbst, sondern von möglicherweise unerwarteten Standardkonfigurationen, „versteckten” Features und Framework-Interna, die Einsteigern unbekannt sind.

Content Negotiation

Zunächst betrachten wir ein recht häufiges Problem. Gegeben eine REST-API, möchten wir die Felder eines Objekts einschränken, die an den Client gesendet werden. Machen wir deutlich, dass das zu verbergende Feld wichtig ist, indem wir es „secret” nennen:

public class State {
    private Integer id;
    private String secret ;
    // Constructors, getters, setters, other fields, ...
}

Folgende Methode in einem REST-Controller behandelt die Anfragen:

@RequestMapping(value="/state/{id}", method=RequestMethod.GET)
@ResponseBody
public State get(@PathVariable("id") Integer id) {
    return repository.get(id);
}

Um es als XML an den Client zu senden, fügen wir der Klasse State folgende Annotationen hinzu:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class State {
    private Integer id;
    private String secret ;
    // Constructors, getters, setters, other fields, ...
}

Natürlich möchten wir nicht, dass „secret” an den Client gesendet wird, also fügen wir dem Feld die Annotation @XmlTransient hinzu:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class State {
    private Integer id;
    @XmlTransient
    private String secret ;
    // Constructors, getters, setters, other fields, ...
}

Diese Lösung funktioniert tatsächlich einwandfrei, wenn der Client XML anfordert. Aber… was ist, wenn das nicht der Fall ist?

Spring MVC unterstützt standardmäßig auch die Serialisierung als JSON. Es gibt mehrere (konfigurierbare) Möglichkeiten, einen anderen Content-Type von einem Spring-REST-Controller anzufordern – eine davon ist der Accept-HTTP-Header. Wird er auf application/json gesetzt, wird das State-Objekt bereitwillig als JSON zurückgegeben. Da die Annotation @XmlTransient zum Verbergen des „secret”-Feldes XML-spezifisch ist, greift sie nicht, wenn Daten im JSON-Format angefordert werden – und das „secret”-Feld wird an den Client gesendet.

Eine Möglichkeit, die Situation zu verbessern, besteht darin, dem Feld eine weitere Annotation wie @JsonIgnore für JSON hinzuzufügen. Das ist jedoch keine wirklich zuverlässige Lösung. Wenn die zugrunde liegende Serialisierungsbibliothek ausgetauscht wird, könnte die Annotation von der geänderten Implementierung ignoriert werden. Eine sicherere, wenn auch ausführlichere Methode zur Vermeidung dieses Problems besteht darin, eine separate Klasse ohne das „secret”-Feld zu erstellen, die dann zum Senden des Objekts an den Client verwendet wird. Schließlich ist es möglich, den Content-Type entweder pro Controller-Klasse, pro Controller-Methode oder global für die gesamte Anwendung auf den gewünschten Typ einzuschränken.

Ungeschützte Dependency-Endpunkte

Es gibt mehrere großartige Bibliotheken, die die Entwicklung und Überwachung von Anwendungen unterstützen. JavaMelody beispielsweise erfordert lediglich das Hinzufügen einer einzigen Abhängigkeit zum Projekt, um einsatzbereit zu sein. Es bietet dann eine praktische webbasierte Übersicht über den Zustand der Anwendung (z. B. Ressourcennutzung, HTTP-Sessions, Prozesse auf dem Server, …).

Bei dieser Einfachheit ist es jedoch verlockend, die Sicherheit der automatisch exponierten Endpunkte zu übersehen. Diese Bibliotheken können sensible Informationen preisgeben oder gefährliche Debug-Funktionalitäten bereitstellen, ohne dass sich die Entwickler dessen bewusst sind. Konkrete Angriffsszenarien hängen naturgemäß von der jeweiligen Bibliothek und ihrer Funktionalität ab.

Um das unbeabsichtigte Exponieren von Endpunkten zu vermeiden, sollten regelmäßig direkte, aber auch transitive Abhängigkeiten überprüft werden. Leider ist das bei großen Projekten mit potenziell riesigen Abhängigkeitsgraphen kaum praktikabel. Der Spring Boot Actuator stellt einen REST-Endpunkt bereit, der alle URL-Mappings auflistet (/actuator/mappings). Man könnte überrascht sein, den /monitoring-Endpunkt dort nicht zu finden. Da JavaMelody als Filter implementiert ist, wird er in den URL-Mappings nicht aufgeführt. Wir können einen benutzerdefinierten Actuator-Endpunkt hinzufügen, der alle Filter auflistet:

@Component
@Endpoint(id="filters")
public class FilterEndpoint {
    private ServletContext servletContext;
    @Autowired
    public FilterEndpoint(ServletContext servletContext) {
        this.servletContext = servletContext;
    }
    @ReadOperation
    public List<String> getFilters() {
        return new ArrayList<>(servletContext.getFilterRegistrations().keySet());
    }
}

Nach der Aktivierung des Endpunkts in den Anwendungseigenschaften liefert er die Namen aller Filter unter /actuator/filters.

Eine weitere sehr einfache Möglichkeit besteht darin, die Route-Mappings und Filter zu prüfen, die Spring Boot beim Start ausgibt.

Zu beachten ist: Auch wenn die Integration von Spring Security standardmäßig für alle Endpunkte eine Authentifizierung verlangt, können Anwendungen es Benutzern erlauben, selbst Konten anzulegen – und damit sind diese verhältnismäßig leicht authentifiziert.

Fazit

In diesem Blogbeitrag haben wir uns zwei häufige Sicherheitsfallen in Spring-Webanwendungen angesehen.

Spring MVC ist standardmäßig so konfiguriert, dass es Objekte in REST-Controllern sowohl in XML als auch in JSON serialisieren kann. Beim Einsatz formatspezifischer Annotationen in serialisierten Objekten ist Vorsicht geboten.

Abhängigkeiten können zusätzliche Endpunkte exponieren, ohne dass sich die Entwickler dessen bewusst sind. Es ist wichtig, einen Überblick über Abhängigkeiten und Routen zu behalten. Tools wie der Spring Boot Actuator können dabei sehr hilfreich sein.

Abschließend möchten wir betonen, dass Spring ein großartiges Framework für die Anwendungsentwicklung ist. Lasst euch von ein paar Fallstricken nicht davon abhalten, tiefer in das Framework einzutauchen.

Das war’s – bis zum nächsten Meetup!