I'm working on a web app ("main") which acts as a central access hub for several other web apps ("children"). The users are supposed to authenticate in the main app, resulting in a $t_{main}$ token known to the client devices, and they are supposed to authenticate in each separate child app they want to access, resulting in $t_1$, $t_2$, ..., $t_n$ tokens (or simply $t_i, 1 \leq i \leq n$) known only to the main server. Afterwards, the users can access those child apps through the main server acting as a proxy.

Currently, our team is trying to come up with a solution how to store these tokens securely.

$t_{main}$ is used for user authentication, so we store only its hash on the main server. As far as I know, since the token is generated with a CSPRNG, a non-secure hashing algorithm like $\text{SHA-256}$ should be sufficient:

$$ t_{main}hashed = \text{SHA-256}(t_{main}) $$

The child-app tokens, on the other hand, have to be injected into the child-app requests by the main server, so they have to be encrypted instead. The solution we came up with is encrypting the child-app tokens using $t_{main}$ as a key (I'm using $\text{ChaCha20Poly1305}$ here but other algorithms like $\text{AES-GCM}$ are also possible):

$$ t_iencrypted = \text{ChaCha20Poly1305}(\text{key} = t_{main}, \text{data} = t_i, \text{nonce} = nonce) $$

The idea here is that we don't have to generate and store any encryption keys ourselves, thus eliminating a possibility of a hypothetical hacker stealing those (we normally use AWS Secrets Manager for storing credentials, in case this is relevant).

The first question I have is: is this approach secure or there are some issues which make it non-viable?

Assuming everything is fine, we still have a problem which is the fact that the encryption key has to have a specific length (in case of $\text{ChaCha20Poly1305}$ it's 32 bytes) which is not guaranteed to be true for $t_{main}$.

The solution I came up with is to produce a 32-bytes long encryption key by hashing $t_{main}$ using the very same $\text{SHA-256}$ algorithm. Now we can easily encrypt the child-app tokens using

$$ t_iencrypted = \text{ChaCha20Poly1305}(\text{key} = \text{SHA-256}(t_{main}), \text{data} = t_i, \text{nonce} = nonce) $$

at the expense of having to apply another round of hashing to the $t_{main}$ for authentication purposes, i.e.

$$ t_{main}hashed = \text{SHA-256}(\text{SHA-256}(t_{main})) $$

The second question I have is: does this weird algorithm of producing encryption keys compromise the security in any way?

If you have any suggestions how some parts of this system can be implemented in a different, more secure and reasonable way, please note that storing the child-app tokens on the main server instead of the client devices is a non-negotiable requirement, and the workflow described in the first paragraph cannot be changed.

  • $\begingroup$ What is the size of $t_{main}$? in bits? $\endgroup$
    – kelalaka
    Nov 14 at 17:25
  • $\begingroup$ @kelalaka I don't have an exact answer, unfortunately. We're using an internal service for OAuth2 authentication and there's no technical documentation available at hand. The $t_{main}$ tokens we get on login are 57-58 characters long ASCII strings consisting of lowercase letters and digits. These tokens may be random sequences of bytes encoded somehow, but I can't tell for sure. $\endgroup$
    – Alex F
    Nov 15 at 8:12
  • $\begingroup$ The crucial part is the input-output space of $t_{main}$ generator. Make sure that the input of the SHA-256 ( why called non-secure?) has large entropy. Otherwise, attack is possible. Why don't you use a KDF? In the second question, you use double SHA-256 for longer keys, use SHAKE128.. $\endgroup$
    – kelalaka
    Nov 15 at 9:27
  • $\begingroup$ By non-secure I meant that it's a regular hashing algorithm and not a KDF specifically. Regarding KDF usage: from what I've learnt in uni, their point is generating a secure difficult-to-compute (i.e. high time/space complexity) value from a non-secure input, so there is not much benefit in applying it to a token generated using a CSPRNG. Regarding double SHA-256: SHA-256-hashed token is used as an encryption key, so we can't store it in the DB to verify user authentication anymore, and something else is needed - I simply chose applying another round of SHA-256 on top of it for that purpose. $\endgroup$
    – Alex F
    Nov 15 at 10:41


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.