3

I'm working on extending a web application with a RESTful HTTP-based API. We decided to require the client to supply an authentication token in each request (instead of using sessions or other multi-step authentication schemes like OAuth). E.g.:

POST /foo HTTP/1.1
Authorization: Bearer <authentication token>
...

The token is an opaque string of characters to the user. The token is only shown to the user once. A single user can generate multiple tokens and associate different permissions and a descriptive name with each one. Each request to the API is recorded in a user-visible log and each log entry is associated with the specific token which was used for the request.

In our current implementation, we store an entity with the following attributes in the database:

  • Random UUID (primary key, used to reference the token from log entries)
  • Authentication Token (indexed)
  • Associated User Account and Permissions
  • User-Supplied Description
  • Revoked (boolean)

We discussed different security implications of this approach. One threat, which I'd like to discuss here is:

What happens if an attacker temporarily gains read access to the database?

Clearly, storing the authentication tokens makes them vulnerable. After reading the database, an attacker can use any of the non-revoked tokens to access the API. We thought about securing the tokens stored in the database from such a threat.

What approaches (and possibly, applicable algorithms or protocols) can be used to protect the authentication tokens in such a scenario?

I was thinking about hashing the token and using the result of that to find the right entry in the database.

1 Answer 1

2

Your tokens are passwords, except you don't let users choose weak passwords. I think they should be hashed in the db. Because they have a lot of entropy (I recommend base64 of 15 bytes read from /dev/urandom), you can use a single round of SHA-256. If you don't think 120 bits of entropy is enough, use 30 bytes. Anyway GPU can't check all combinations.

5
  • Thank you. Simply hashing the tokens is exactly what I'm going to do. It is simple enough that it doesn't feel like I'm inventing my own crypto. :) For those interested in the details: We are using 20 bytes from Python's os.urandom(), encode that using base32 for the user. Then we hash that base32 representation using SHA 256, encode it using base 16 (a.k.a. hexadecimal) and put that into an indexed field in the database. Commented Nov 2, 2016 at 13:15
  • @Feuermurel 1. you use 160 bits in 32 chars, you can use 240 bits in 40 chars. 2. base64 of hash would take less space than hex. 3. Since users provide password without userid, you need unique constraint on that index. Although the chance of collision is 0.
    – Z.T.
    Commented Nov 2, 2016 at 18:06
  • I think that 160 Bits of entropy is plenty and there is a tradeoff with usability as longer tokens become unwieldy and more prone to errors once they start to introduce line-breaks in an editor when there is other stuff on the same line. Commented Nov 3, 2016 at 9:03
  • I decided to use hex encoding because I like the fact that there is no need for padding. Space really is not a concern. We would need to generate hundreds of millions of tokens until we would actually notice a difference. Commented Nov 3, 2016 at 9:04
  • I'm actually using a UNIQUE constraint on that column also to get an index "for free", which I need anyways. Commented Nov 3, 2016 at 9:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .