Search code examples
spring-mvccookiesunicodespring-securityremember-me

Spring Security remember me cookie Unicode characters wrong


I'm using spring boot, spring mvc and spring security to make a simple website. In the login page, I've got a remember me checkbox. When it's ticked and login, a remember-me cookie is created for the browser. I've tried both chrome and firefox.

The cookie is below.

Name:   remember-me
Content:    Pz8/Pz8/OjE0NjQ5ODcxMjQxMDk6YTc1MDMzNTM0ZmNhNjc3YmUwOTljZGNjN2EyYTk1NjM
Domain: localhost
Path:   /gy
Send For:   Any kind of connection
Accessible to Script:   No (HttpOnly)
Created:    Friday, 20 May 2016 at 21:52:04
Expires:    Friday, 3 June 2016 at 21:52:04

The content above is base64 format. The decoded string is below.

??????:1464987124109:a75033534fca677be099cdcc7a2a9563

The first part is username that is chinese characters but shown as ??????.

I know to save Unicode characters to cookie, we should use URLEncoder class to encode it first, and then decode it after read it back. I'm new to Java and Spring, so have no idea how to customise the remember me cookie to handle the Unicode characters.

Any clue to solve the issue would be appreciated. Thanks!


Solution

  • This is a bug in Spring Security. It calculates the MD5 like this:

    Hex.encode(digest.digest(data.getBytes()));
    

    using the 0-argument version of getBytes which encodes Unicode characters to bytes using the default encoding. The default encoding varies, but clearly on your server it is an encoding that cannot contain Chinese characters.

    The default encoding should never be relied upon. It very often isn't a UTF, so can't contain all Unicode characters. Also since it's not fixed, it can break your tokens when you move from one server to another. Spring should have set an explicit UTF encoding, eg getBytes("UTF-8").

    This is not the only property of remember-me that worries me:

    • it's using MD5, a hashing algorithm which is long past its prime, and increasingly vulnerable to attack. It's not even using HMAC-MD5 which would mitigate this (HMAC is designed for exactly this purpose)

    • all non-supported characters get silently squashed into the same character, ?, which means that the hash for user 你好 would be the same as the hash for user ☃☃. So it could in theory be possible to log in as any victim user with a non-supported character in their username (or a question mark itself!), by creating another user with a different non-supported character in their username, and then altering the cookie to include the victim username with the new user's hash. (In practice, however (a) the presence of the ‘password’ field in the hash complicates matters, although that too is vulnerable to Unicode character squashing, and (b) luckily the cookie itself is also incorrectly decoded using the default encoding, preventing one from getting arbitrary Unicode characters into the checker in the first place).

    • it throws strings together without any escaping for delimiters, so weirdness happens when components have colons in. You probably can't really do much of an attack with that directly, but it's shonky and the attempt to work around it is gloriously half-arsed.

    I wouldn't trust this code to protect my web application; it is disappointing to see this kind of thing in a high profile security library.