Java

Login gegen Timing Angriffsvektoren härten

Ein Time Attack ist ein Side-Channel-Angriff in einem kryptographischen Kontext. Es ist möglich, ein kryptographisches System zu kompromittieren, indem die Ausführungszeit kryptographischer Algorithmen analysiert wird.

Die Zeit, die eine Operation benötigt, hängt neben anderen Parametern auch von den Eingabedaten und deren Verarbeitung ab. Mit präzisen Messungen der Zeit, die für jede Operation benötigt wird, ist ein Angreifer in der Lage, sich bis zu den Eingabedaten zurück zuarbeiten.


Die Java-Implementierung aller equals-Methoden, die ich bisher gesehen habe, geben nach einem Fehlvergleich direkt false zurück. Und das ist aus performancetechnischer Sicht auch vollkommen logisch. Allerdings lässt diese Vorgehensweise auch eine Möglichkeit für einen zeitbasierten Angriff offen.

Implementation der equals Methode von der String Klasse
@IntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
    if (value.length == other.length) {
        for (int i = 0; i < value.length; i++) {
            if (value[i] != other[i]) {
                return false;
            }
        }
        return true;
    }
    return false;
}

Solange du keine Kennwörter abgleichen willst, solltest du dir hierüber keine Gedanken machen. Falls doch, empfehle ich das folgende Code-Snippet für das Anmeldeverfahren.

TimingSafeComparator.compare

Anstatt nach einem Fehlvergleich direkt false zurückzugeben, versucht die equals-Methode der Klasse TimingSafe, mit konstanter Zeit zu laufen, indem sie alle Bytes überprüft und das Ergebnis mit einer bitweisen Operation akkumuliert.

package examples.de.michm.timingSafeEqual;

public class TimingSafeComparator {
    /**
     * Compares two byte arrays of the same length in
     * nearly the same duration each call. The result is
     * true if both arrays have an equivalent content.
     *
     * @param a first byte array
     * @param b second byte array
     * @return true if both arrays have an equivalent content, 
     *         false otherwise.
     */
    public static boolean compare(byte[] a, byte[] b) {
        boolean okFlag = true; // result flag

        // Both arrays need to be of the same length.
        if (a.length != b.length)
            throw new ArrayIndexOutOfBoundsException(
              "the two arrays to be compared must be of the same length"
             );

        // Runs with constant time by checking all bytes and 
        // accumulating the result with a bitwise operation.
        // Therefore, the loop should ensure that the execution 
        // time does not differ too much.
        for (int i = 0; i < a.length; i++)
            okFlag &= a[i] == b[i];

        return okFlag;
    }
}

image

Repository
examples/de/michm/timingSafeEqual · main · Manfred Michaelis / java-cheat-sheet · GitLab
GitLab Community Edition
image