In this short article, I would like to recap 4 popular mechanisms for implementing authentication. There will be comparison and consideration to choose the most appropriate method as well.
In a standard Java web application, there are two kinds of meta-data that normally included in request, session and authentication. The session information help to make HTTP protocol stateful while the authentication information help to identify user.
Usually, there is one header contains authentication information, another header contains session information or a single header contains both. It is also possible for a request to have only one of the two.
When HTTP is first introduced as a stateless protocol, the official authentication mechanism is basic access authentication. With this authentication method, every request must include a header that contains encrypted username and password. Apparently, basic authentication is only appropriate for a secured channel.
Still, many people would feel that basic authentication is too unsecured. Surprisingly, the basic authentication requests client to encrypt instead of hashing the password. Even worse, the encrypted password is included in every request. Therefore, if hackers manage to intercept a single request, the user password is revealed.
To strengthen the authentication mechanism, digest access authentication is introduced. With the new authentication method, client send the hashed value of combination of username, password, realm and server nonce. While realm is fixed, the nonce can be randomly generated value issued by server. Later modification of digest authentication allow client to add its nonce as well. Kindly notice that the digest authentication send the plain values of username, nonces and realm in the request. Here is one example
Authorization: Digest username="some_user", realm="Some Realm", nonce="MTQyNjE1MDE5Njc0MjoxZjdkYjIzZjI0YjZjNDExMzU2OTk3MWIyNWQzYmYwNg==", uri="/some_website/index.html", response="fcc3a3cd69c93c76e65c845263c3d4f4", qop=auth, nc=00000001, cnonce="c61b667053c03c31"
In the above request, the response value is the hashed value of username, password, realm, server nonce and client nonce combined. The server calculated its own response and authenticate user if the responses are matched.
Digest authentication make it almost impossible to figure-out the password by intercepting requests and it is also helps to prevent the replay-attack. For that to happen, the server nonce should be changed regularly. Therefore, re-sending a request after the server nonce expired will fail.
Most of readers may notice that these two methods are not that popular anymore. The HTTP based authentication mechanisms are supported by the browsers and may not provide user a beautiful login form. Worse, it provides no easy way to logout unless user close the browser or the tab. Therefore session-based login is the preferred authentication mechanism nowadays. Basic and digest authentication are still used but mostly for B2B communication where maintaining a session is not necessary.
Session-based login is applicable when HTTP is stateful. For this to happen, there is a session cookie that embedded into every request (normally, it is named JSESSIONID). The server will create a session storage for each session it created. When a request arrive, the server will try to locate user profile in user session. If the user profile is not available, a login form will be shown to user. Upon successful authentication, the server store user profile into session storage.
This method works well but it requires the stickiness load balancer if the web application is deployed in a cluster environment. Otherwise, a server will not recognize the session if it was generated by another server.
For the cloud application, it is better to build a client-side session with a cookie that contains user information. This session cookie is secured with a signature. To avoid reading database frequently, we can use a common key to sign all the sessions instead of using password.
After knowing each mechanism, let compare them:
|Features||Basic Authentication||Digest Authentication||Server-side Session||Client-Side Session|
|Logout||Close Browser||Close Browser||Kill session||Kill session|
|Support Cluster||Yes||Yes||Require stickiness LB||Yes|
|Prevent Replay Attack||No||Yes||Optional (session timeout)||Optinal (include timestamp in session signature)|
|Client Storage||username & password||username & password||session cookie||session cookie|
|Server Storage||zero||zero||session storage||zero|
|Handshake||Not required||at least 2 requests||at least 2 requests||at least 2 requests|
|Network overhead||short header||long header||short header||long header|
You can see the above comparison and make the choice yourself. For me, Basic Authentication and Digest Authentication seem to be the better choice to build B2B authentication. However, they do not fit customer facing application well.
Choosing between Basic and Digest Authentication is merely depend on how secured is your channel. Most of the time, it is worth safeguarding our application with Digest Authentication as the network, handshake and computation overhead is not very much.
For customer facing UI, I would prefer Client-side session over Server-side session because they scale better. You always can throw in new servers to share the workload. For server-side session, the additional server can only serve new sessions, not existing sessions, unless you can clone session storages.
The problem with client-side session is the expose of user information in the session cookie. Because of that, you can only put primitive and not confidential values in the session. If you can live with that limit then client-side session is surely the better choice.