1. REST authentication methods

Criteria HTTPS basic auth Cookies+Session Query Auth.
Browser integration Native Native Via JavaScript
User Interaction Rude Custom Custom
Web Service use (rough estimation) 95% 4% 1%
Session handling Yes Yes No
Session managed by Client Server N/A
Password on Server Yes Yes/No N/A
Truly Stateless Yes No Yes
Truly RESTful No No Yes
HTTP-free No No Yes

HTTP basic auth over HTTPS

This first solution, based on the standard HTTPS protocol, is used by most web services. It's easy to implement, available by default on all browsers, but has some known draw-backs, like the awful authentication window displayed on the Browser, which will persist (there is no LogOut-like feature here), some server-side additional CPU consumption, and the fact that the user-name and password are transmitted (over HTTPS) into the Server (it should be more secure to let the password stay only on the client side, during keyboard entry, and be stored as secure hash on the Server).

The supplied TSQLite3HttpClientWinHTTP and TSQLite3HttpClientWinINet clients classes are able to connect using HTTPS, and the THttpApiServer server class can send compatible content.

Session via Cookies

To be honest, a session managed on the Server is not truly Stateless.
One possibility could be to maintain all data within the cookie content. And, by design, the cookie is handled on the Server side (Client in fact don’t even try to interpret this cookie data: it just hands it back to the server on each successive request). But this cookie data is application state data, so the client should manage it, not the server, in a pure Stateless world.

The cookie technique itself is HTTP-linked, so it's not truly RESTful, which should be protocol-independent. In our framework, since we don't provide HTTP-like headers within each request, we can not handle cookies natively, for all transmission protocol used. So we tried not to use the Cookie technique.

Query Authentication

Query Authentication consists in signing each RESTful request via some additional parameters on the URI.
See this reference about this technique

It was defined as such in this article:

All REST queries must be authenticated by signing the query parameters sorted in lower-case, alphabetical order using the private credential as the signing token. Signing should occur before URL encoding the query string.

For instance, here is a generic URI sample, extracted from the link above:

 GET /object?apiKey=Qwerty2010
should be transmitted as such:
 GET /object?timestamp=1261496500&apiKey=Qwerty2010&signature=abcdef0123456789

The string being signed is "/object?apikey=Qwerty2010×tamp=1261496500" and the signature is the SHA256 hash of that string using the private component of the API key.

This technique is perhaps the more compatible with a Stateless architecture, and can also been implemented with a light session management.

Server-side data caching is always available. In our framework, we cache the responses at the SQL level, not at the URI level (thanks to our optimized implementation of GetJSONObjectAsSQL, the URI to SQL conversion is very fast). So adding this extra parameter doesn't break the cache mechanism.

2. SQLite3 Framework authentication

Even if, theoretically speaking, Query Authentication sounds to be the better for implementing a truly RESTful architecture, our framework tries to implement a Client-Server design.

In practice, we may consider two way of using it:
- With no authentication nor user right management (for instance for local access of data);
- With per-user authentication and right management via using defined security groups, and a per-query authentication.

According to RESTful principle, handling per-session data is not to be implemented in such an Architecture. A minimal "session-like" feature was introduced only to handle user authentication with very low overhead on both Client and Server side. The main technique used for our security is therefore Query Authentication, i.e. a per-URL signature, over a light per-User session authentication.

Per-User authentication

On the Server side, a dedicated RESTful Service, accessible via the ModelRoot/Auth URI is to be called to register an User, and create a session. On the Server side, two tables, implemented by TSQLAuthGroup and TSQLAuthUser will handle respectively per-group access rights, and user authentication.

If both AuthGroup and AuthUser are not available on the Server TSQLModel (i.e. if the aHandleUserAuthentication parameter was set to false for the TSQLRestServer. Create constructor), no authentication is performed. All tables will be accessible by any client. For security reasons, the ability to execute INSERT / UPDATE / DELETE SQL statement via a RESTful POST command is never allowed with remote connections: only SELECT can be executed via this command.

If authentication is enabled for the Client-Server process (i.e. if both AuthGroup and AuthUser are available in the Server TSQLModel, i.e. aHandleUserAuthentication=true), each REST request will expect an additional parameter, named session_signature, to every URL. Using the URL instead of cookies allows the signature process to work with all communication protocols, not only HTTP. 

This will implement both Query Authentication together with a group-oriented per-user right management.

Session handling

A dedicated RESTful service, available from the ModelRoot/Auth URI, is to be used for user authentication, handling so called sessions.

Here are the typical steps to be followed in order to create a new user session:
- Client sends a GET ModelRoot/auth?UserName=... request to the remote server;
- Server answers with an hexadecimal nonce contents (valid for about 5 minutes);
- Client sends a GET ModelRoot/auth?UserName=...&PassWord=...&ClientNonce=... request to the remote server, in which ClientNonce is a random value used as Client nonce, and PassWord is computed from the log-on and password entered by the User, using both Server and Client nonce as salt;
- Server checks that the transmitted password is valid, i.e. that its matches the hashed password stored in its database and a time-valid Server nonce - if the value is not correct, authentication failed;
- On success, Server will create a new in-memory session (sessions are not stored in the database, for lighter and safer process) and returns the session number and a private key to be used during the session;
- On any further access to the Server, a &session_signature= parameter is added to the URL, and will be checked against the valid sessions in order to validate the request;
- When the Client is about to close (typically in TSQLRestClientURI. Destroy), the GET ModelRoot/auth?UserName=...&Session=... request is sent to the remote server, in order to explicitly close the corresponding session in the server memory (avoiding most re-play attacks);
- Each opened session has an internal TimeOut parameter (retrieved from the associated TSQLAuthGroup table content): after some time of inactivity, sessions are closed on the Server Side.

Note that with this design, it's up to the Client to react to an authentication error during, and ask for the User pseudo and password at any time. For multiple reasons (server restart, session timeout...) the session can be closed at any time by the Server.

URI signature

Query Authentication is handled at the Client side in TSQLRestClientURI. SessionSign method, by computing the session_signature parameter for a given URL.

In order to enhance security, the session_signature parameter will contain, encoded as 3 hexadecimal 32 bit cardinals:
- The Session ID (to retrieve the private key used for the signature);
- A Client Time Stamp (in 256 ms resolution) which must be greater or equal than the previous time stamp received;
- The URI signature, using the session private key, the user hashed password, and the supplied Client Time Stamp as source for its crc32 hashing algorithm.

Such a classical 3 points signature will avoid most man-in-the-middle (MITM) or re-play attacks.

For better Server-side performance, the URI signature will use fast crc32 hashing method, and not the more secure (but much slower) SHA-256. Since our security model is not officially validated as a standard method (there is no standard for per URI authentication of RESTful applications), the better security will be handled by encrypting the whole transmission channel, using standard HTTPS with certificates signed by a trusted CA, validated for both client and server side. The security involved by using crc32 will be enough for most common use. Note that the password hashing and the session opening will use SHA-256, to enhance security with no performance penalty.

In our implementation, for better Server-side reaction, the session_signature parameter is appended at the end of the URI, and the URI parameters are not sorted alphabetically, as suggested by the reference article quoted above. This should not be a problem, either from a Delphi Client either from a AJAX / JavaScript client.

More details are available in the SAD document of the framework official documentation.
Feedback and comments are welcome on our forum.