I'm building a JSON API. For authentication I plan to use a random-token based approach, with the random token having 256 bits of entropy and generated using a CSRNG. A hash of the token will be stored in server-side in the database. For most clients, I will send the authentication token to them in an Authorization header. All communction will be over HTTPS.

The general pattern that I plan to implement is described in the first part of this SO answer.

My question is about clients of the API which happen to be single-page-applications running in a web browser. My understanding is that it is not safe for them to store the authentication token in local storage or in memory due to the risk of XSS attacks and compromised JavaScript dependencies. It's also not safe to have them store the authentication token in a HttpOnly cookie due to the risk of CRSF.

The solution seems to be to send the authentication token to the client in a HttpOnly cookie, AND also implement a Synchronizer Token Pattern where we generate an additional token that is sent to the client in a header when they first authenticate. The client can store this additional token in local storage, and pass it back to the API in a header with subsequent requests.

Reasonable enough. But what I would like to understand is whether there is any risk in not generating a new additional token to implement the Synchronizer Token Pattern, and instead just splitting the original 256-bit authentication token so that the client receives half of it in a HttpOnly cookie, and half of it in an header that they can keep in local storage? The API server then reconstructs the full authentication token when receiving the request.

Granted, if one of the halves was exposed, there would only be 128-bits of entropy remaining for an attacker to guess (but I think that is probably still sufficiently difficult).

Otherwise, does this achieve essentially the same thing from a security point of view? Are there any subtleties that I'm missing here?

  • you can store something in memory in JS, and use it, without any other scripts having access to it. all you need to do is control the reference to the variable, often via a wrapping function that knows the token and anything can call, but nothing can introspect or alter. Without storage, you have to re-auth if the page gets reloaded. – dandavis Jun 17 '19 at 17:00
  • @dandavis Thanks, that's really interesting. I'm not a really a JS developer, and the fact it's possible is new to me. Do you know of any examples you could point me in the direction of? – Alex Edwards Jun 17 '19 at 19:17

Here's the simplest example possible to show how to store values securely in JS code:

var api = (function(){

    let secret = prompt("enter a secret phrase"); // Only the api object can reach this

    return { // a method to compare guesses to the secret
     auth: phrase=> phrase == secret

}()); // end privacy wrapper

// test it in console by entering bingo at the prompt:
[api.auth("one"), api.auth("bingo"), api.auth("cheese")];
// evaluates as [false, true, false]

In a real application, the secret is fetch()ed by ajax, a cookie, or whatever else you use. The crux is just that the only access to the secret is via the methods defined within the wrapping function that defines the "private" variable. This pattern is called a "privileged method" in JS, being made available to code outside only through the terms and conditions that your "api" object stipulates.

| improve this answer | |
  • While the secret value is "private" in that it cannot be accessed by outside code, if it exists hard-coded in the JS, a simple examination of the source code will reveal the secret value. If the secret value is fetched by an AJAX call, examination of network calls in the browser would reveal the secret. I don't believe it is possible to securely store secrets client-side. – shimizu Oct 4 at 20:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.