2011-05-23

XML encryption and padding oracle attacks



Recently I received an interface specification of a security related product. It sensibly did not use its own encryption format. As most of the interface was XML, it decided to employ XML encryption to provide confidentiality of the data. They decided to fully keep within the standard - they even added restrictions on the format of the standard. Instead of ISO 10126 padding they decided to use PKCS#5 padding. The difference is tiny: ISO 10126 padding is basically RR RR RR 04 (in hexadecimals, of course) where RR is a random number and PKCS#5 padding is 04 04 04 04 if you need to pad 4 bytes. Please just keep this in mind for now.

It is well known within the cryptographic community that encryption without some kind of integrity protection is a bad idea. This is especially the case if you think that a normal block cipher in CBC mode provides integrity all by itself. It does not: if you decrypt a random block, you'll get random data. Actually, if you look at the security chapter of the XML Encryption Requirements you will find that you need to address the following vulnerability:

Processing of invalid decrypted data if an integrity checking mechanism is not used in conjunction with encryption. {List: Lambert, FTF1}
That's nice but it is not enough of a warning. Actually, in the XML encryption standard you will find that they opt in their examples for signatures before encryption.

After that small intermezzo, we return to the padding and a attack called a padding oracle attack. To successfully perform a padding oracle attack, you need just two things:
  1. an oracle that tells you if your plain text is correctly padded, i.e. if decryption succeeded or not
  2. an encryption scheme that uses CBC encryption and a known padding
Now the first requirement - the oracle - is obviously present if you are talking about an interface specification: it is the service provided. The second was not completely provided for: the ISO 10126 padding only validates the last byte. PKCS#5 padding, of course, validates all padding bytes. Oops.

How bad is this? Well, without going into details: you need 128 tries on average to reveal one byte of plain text, regardless of the block cipher used. So if you have 1K of cipher data, you need 128K attacks to retrieve all plain text data. That's pretty serious - basically all your plain text data is available to anyone that can apply this attack. It took me all of four hours to implement a padding oracle demonstration. It will take me another day at most to implement it for  the specific service (performing the base64 encoding, wrapping the base64 encoded data in XML and implement the HTTP client).

So if you own a service: make sure your data is integrity checked before doing any decryption. If that is not possible: don't give more information to any attacker than absolutely necessary (e.g. use ISO 10126 over PKCS#5, and make sure timing attacks are not possible). Even better: use TLS to protect your data in transit; only use XML encryption if you want to keep your data safe on the server after reception. Finally: always keep to the standards and use the defaults if you are unsure.

Hopefully the next XML encryption standard will default to an encryption scheme that does provide an integrity check (e.g. a MAC).