---
title: 'Deserialisierung absichern mit einem ObjectInputFilter'
url: 'https://tutego.de/blog/javainsel/2017/02/deserialisierung-absichern-mit-einem-objectinputfilter'
markdown: 'https://tutego.de/blog/javainsel/2017/02/deserialisierung-absichern-mit-einem-objectinputfilter.md'
date: '2017-02-19'
description: 'Alles über readObject() einzulesen was dem ObjectInputStream gegeben wird ist ein Sicherheitsrisiko. Ab Java 8 Update 121[1] – und folglich auch Java 9 – lässt sich ein Filter bei der Deserialisierung setzen, der mit Informationen über die deserialisierte Klasse, Anzahl Objektreferenzen, Array-Län…'
taxonomy:
  category:
    - Allgemein
    - Insel
---

# Deserialisierung absichern mit einem ObjectInputFilter

![Illustration einer tropischen Insel mit Palmen und einem hohen Berg](https://tutego.de/blog/javainsel/user/themes/tutego/images/insel-cover.jpg)# Java Blog für Programmierer

[Java Blog für Programmierer](https://tutego.de/blog/javainsel/)

## Deserialisierung absichern mit einem ObjectInputFilter

19.02.2017 · [Christian Ullenboom](https://tutego.de/blog/javainsel/author/admin/)

Alles über `readObject()` einzulesen was dem ObjectInputStream gegeben wird ist ein Sicherheitsrisiko. Ab Java 8 Update 121[\[1\]](#_ftn1) – und folglich auch Java 9 – lässt sich ein Filter bei der Deserialisierung setzen, der mit Informationen über die

- deserialisierte Klasse,
- Anzahl Objektreferenzen,
- Array-Längen,
- aktuelle Tiefe im Objektgraph und
- Größe des serialisierten Objekts in Bytes

versorgt wird, und sein OK geben muss wenn es zu keiner `InvalidClassException` während `readObject()` kommen soll.

Ein Filter ist vom Typ `ObjectInputFilter` und wird auf einem ObjectInputStream über `setObjectInputFilter(ObjectInputFilter filter)` gesetzt; den aktuell gesetzten Filter liefert `getObjectInputFilter()`. Die Deklaration der funktionalen Schnittstelle `ObjectInputFilter` enthält eine Methode `checkInput(…)`, zwei Aufzählungstypen und eine innere Klasse.

```java
@FunctionalInterface

interface ObjectInputFilter {

  Status checkInput( FilterInfo filterInfo );

  interface FilterInfo {

    Class serialClass();

    long arrayLength();

    long depth();

    long references();

    long streamBytes();

  }

  enum Status {

    UNDECIDED, ALLOWED, REJECTED;

  }

  final class Config {

    public static ObjectInputFilter getSerialFilter() {...}

    public static void setSerialFilter(ObjectInputFilter filter) {...}

    public static ObjectInputFilter createFilter(String pattern) {...}

  }

}
```

Programmieren wir ein kleines Beispiel mit dieser API. Beginnen wir damit, einen `java.awt.Point`, ein byte-Array, eine Swing-Komponente und ein leeres `org.w3c.dom.Document` in ein ByteArrayOutputStream zu serialisieren.

```java
ByteArrayOutputStream bos = new ByteArrayOutputStream();

try ( ObjectOutputStream oos = new ObjectOutputStream( bos ) ) {

  oos.writeObject( new Point(10, 20) );

  oos.writeObject( new byte[1000] );

  oos.writeObject( new JLabel() );

  oos.writeObject( DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );

}
```

Im nächsten Schritt können wir einen ObjectInputStream auf das erzeugte Byte-Array anwenden und Deserialisieren. Zusätzlich setzen wir einen Filter, dessen Implementierung aus drei Teilen besteht:

1. Testen, ob ein prozessweiter Filter existiert, der eine Entscheidung schon gefallen hat,
2. Loggen aller Informationen aus dem `FilterInfo`-Objekt,
3. Alle serialisierten XML-Dokumente blockieren.

Zum Code:

```java
ObjectInputStream ois = new ObjectInputStream(

                          new ByteArrayInputStream( bos.toByteArray() ) );

ois.setObjectInputFilter( filterInfo -> {

  ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter();

  if ( serialFilter != null ) {

    ObjectInputFilter.Status status = serialFilter.checkInput( filterInfo );

    if ( status != ObjectInputFilter.Status.UNDECIDED )

      return status;

  }

  System.out.printf( "depth=%s, references=%s, serialClass=%s, arrayLength=%s, streamBytes=%s%n",

                     filterInfo.depth(), filterInfo.references(), 

                     filterInfo.serialClass(), filterInfo.arrayLength(), filterInfo.streamBytes() );

  if ( Document.class.isAssignableFrom( filterInfo.serialClass() ) )

    return Status.REJECTED;

  return Status.ALLOWED;

} );
```

Als letzte versuchen wir die vier geschriebenen Objekte einzulesen.

```java
for ( int i = 0; i < 4; i++ )

  System.out.printf( "%d. gelesenes Objekt: %s%n%n", i + 1, ois.readObject() );
```

Beim letzten Versuch kommt es zu einem Fehler, wie die Ausgabe zeigt:

```text
depth=1, references=1, serialClass=class java.awt.Point, arrayLength=-1, streamBytes=43
```

1. ```

    gelesenes Objekt: java.awt.Point[x=10,y=20]
    ```text
    ```

```
depth=1, references=2, `serialClass`=class [B, `arrayLength`=-1, `streamBytes`=70

depth=1, references=2, `serialClass`=class [B, `arrayLength`=1000, `streamBytes`=74
```text

2. ```
    gelesenes Objekt: [B@45dd4eda
```

```text
depth=1, references=3, serialClass=null, arrayLength=-1, streamBytes=1311

Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED

       at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1276)

...

       ... 10 more
```

Die API-Dokumentation der Typen erklärten weitere paar Details, z. B. was die Bedeutung von `Status`.UNDECIDED ist, und wie die System-Property jdk.`serialFilter` mit einer textuellen Filter-Beschreibung belegt werden kann.

[\[1\]](#_ftnref1) <http://www.oracle.com/technetwork/java/javase/8u121-relnotes-3315208.html>

Kategorien: [Allgemein](https://tutego.de/blog/javainsel/category/allgemein/), [Insel](https://tutego.de/blog/javainsel/category/insel/)

[← Alle Beiträge](https://tutego.de/blog/javainsel/)

---

## Navigation

- Previous: [Die Klassen OutputStream und InputStream](https://tutego.de/blog/javainsel/2017/02/die-klassen-outputstream-und-inputstream.md)
- Next: [Schwache Referenzen und Cleaner](https://tutego.de/blog/javainsel/2017/02/schwache-referenzen-und-cleaner.md)
