Authentication

How It Works

To authenticate that a user and their client are who they say they are, a few steps need to occur:

  1. User sends credentials (e.g. username and password) to server
  2. Server verifies these credentials are accurate by checking their data store, usually via a test of the password's hash in their database[3]
  3. Server returns to the client whether or not these credentials are accurate

This is easy enough, but becomes unwieldy if the application needs you to verify your identity for various actions, as it will have to repeat these three steps for every request, which is very costly.

Token

One solution is to use a token protocol.

  1. User sends credentials to server
  2. Server verifies the credentials are accurate by checking against the existing data in the database
  3. Server creates a unique token with an optional expiration timestamp and stores it in a table of session rows
  4. Server returns new token to client either via JSON body, as a Bearer token, or via cookie

Cookie

If using a cookie for authentication, it is best to set an HttpOnly flag to avoid being accessed via remote or malicious Javascript.

Cookies have a particular format for their expires field, using the HTTP-date or RFC5322[10]. This can also be accessed on a Javascript Date object via toUTCString(). The date is formatted as follows:

foo=bar; expires=Sun, 06 Nov 1994 08:49:37 GMT; HttpOnly

JWT[4]

Another option is something like JWT, but note that this does have its issues[6]. This method stores three elements within it:

  1. Header - Identifies which algorithm is used to generate the signature
  2. Payload - Contains data to be used for verification (e.g. username)
  3. Signature - Securely validates the token

This token takes a JSON structure and puts it into a string via base64 encoding. So now instead of having to send the username and password and check with the database every single request, we can verify that the token is accurate and contains the necessary information for the user to act (e.g. their username). The steps that occur now are a little different:

  1. User sends credentials (e.g. username and password) to server
  2. Server verifies these credentials are accurate by checking their data store, usually via a test of the password's hash in their database
  3. If accurate, server creates a JWT that proves this user is who they say they are
  4. Server returns the JWT to the client

For all further interactions:

  1. User sends JWT
  2. Server verifies JWT is valid
  3. Server performs requested action

This is much simpler and less costly, as we only talk to the database one time. We don't need the password for every interaction since we verified it once that it is accurate and created a token that proves that on the server side.

Cross Site Request Forgery (CSRF)

In a CSRF attack, the attacker's goal is to cause an innocent victim to unknowingly submit a maliciously crafted web request to a website that the victim has privileged access to.[7]

This is done by utilizing a user's credentials or authorization tokens for site X while user is browsing site Y. Since the user has credentials for site X, site Y could have a form that will delete all privileged data that the user has access to, and if the user submits the form, then the request will be perceived as legitimate by the server, because the credentials are valid.

Some ways to avoid a CSRF attacki[8]:

CSRF Tokens[8]

How do CSRF tokens work?

  • Server sends the client a token.
  • Client submits a form with the token.
  • The server rejects the request if the token is invalid.

An attacker would have to somehow get the CSRF token from your site, and they would have to use JavaScript to do so. Thus, if your site does not support CORS, then there's no way for the attacker to get the CSRF token, eliminating the threat.

Make sure CSRF tokens can not be accessed with AJAX! Don't create a /csrf route just to grab a token, and especially don't support CORS on that route!

The token just needs to be "unguessable", making it difficult for an attacker to successfully guess within a couple of tries. It does not have to be cryptographically secure. An attack is one or two clicks by an unbeknownst user, not a brute force attack by a server.

References

  1. https://kevin.burke.dev/kevin/things-to-use-instead-of-jwt/
  2. https://frontegg.com/blog/token-based-authentication
  3. https://auth0.com/blog/hashing-passwords-one-way-road-to-security/
  4. https://jwt.io/
  5. https://en.wikipedia.org/wiki/JSON_Web_Token
  6. https://redis.com/blog/json-web-tokens-jwt-are-dangerous-for-user-sessions/
  7. https://en.wikipedia.org/wiki/Cross-site_request_forgery
  8. https://github.com/pillarjs/understanding-csrf
  9. https://github.com/pillarjs/csrf
  10. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString

Last modified: 202401040446