In our REST API we write a log entry for every call made, including things like the query string, response code, duration etc... along with a SHA256 hash of the access token used. The thought process behind this is that the hashed values still lets us correlate API calls (e.g. these 5 calls are all made with the same access token), however even if an attacker obtains the logs there is no way for them to reverse the hash and obtain a usable access token. Is this reasoning sound?
SHA256 is still a secure hash. As a result the main concern is someone trying to bruteforce your access token. SHA256 isn't a slow hash so depending on how much entropy your access token has, this may be a valid concern. You can calculate the cracking time from the amount of entropy versus the hashing rate the attacker can achieve. As it turns out, SHA256 is what bitcoin uses so hashing rates for various setups are readily available. For non-specialized GPU hardware you can roughly estimate a hash rate of 1GH/s (1,000,000,000 guesses per second). Let's take that and run through two extreme examples of the entropy of your access token:
1. Access token is a simple permutation of creation time + user id.
Ever seen something like
access_token = md5(time() + $user->id)? Things like this are surprisingly common. If an attacker knows the id of the user they are trying to attack (which is likely since it may also live in the logs) and know how your access tokens are created, then they just need to guess the account creation time. Even if the creation time includes microseconds, this is still extremely easy to guess. At a rate of 1GH/s and given a fixed user id, you could brute force all creation times over a decade in a second (
(86,400,000 possible hashes/year) * (10 years) / (1,000,000,000 hashes/second) = ~1 second
This access token has no security from brute force at all.
2. Access token is a GUID
Alternatively, your access token may be a GUID. These typically have 128 bits of entropy or roughly
3.4e38 possible combinations. This means that bruteforcing one would take:
3.4e38/1e9 = 3.4e29 seconds, which is approximately 10,000,000 times the age of the universe.
This is absolutely safe from brute force.
Finally as a different suggestion (which may not be applicable) is to store the id of the access token, presuming that it is stored in the database and therefore has an associated id. After all, the access token is the sensitive part, but the id of the access token is just as useful for tracking and (normally) not sensitive. Of course if you aren't storing your access token in a database (for instance, if you are using a JWT) this isn't possible.
Have you considered using JSON web tokens instead? The advantage there is that you could encrypt them with the users ID, or some other unique signifiers and when you get a request decrypt them to retrieve that data.
The upsides would be that no sensitive data whatsoever would be stored in the logs and you could tie a request to a specific user, the down side is that you'd have to spend a bit more horse power on decryption
I see from your question that the only purpose of storing the hash of access is for correlation of the API requests. I would recommend you to use some other identifier (i.e. random GUID hash encoded with Base64) as correlation ID and NOT store access token in any form in your logs.
The main recommendation is not to store access tokens in the any storage in unencrypted form. The SHA256 hash, as for the first sight, is possibly secure, it is not encryption. It is still just hashing. What more, if you will not store access token in logs, you will avoid thinking of secure hashing mechanism which you would need to use (or maybe implement). You just remove the problem from the equation. :)
The general rule I try to follow (and would recommend), is to use proper things to proper purposes.