How to implement RESTful authentication
Commonly, it can be achieved, in the SOA over HTTP world via:
- HTTP basic auth over HTTPS;
- Cookies and session management;
- Query Authentication with additional signature parameters.
We'll have to adapt, or even better mix those techniques, to match our framework architecture at best.
Each authentication scheme has its own PROs and CONs, depending on the purpose of your security policy and software architecture.
1. REST authentication methods
|Criteria||HTTPS basic auth||Cookies+Session||Query Auth.|
|Web Service use (rough estimation)||95%||4%||1%|
|Session managed by||Client||Server||N/A|
|Password on Server||Yes||Yes/No||N/A|
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).
TSQLite3HttpClientWinINet clients classes are able to connect
using HTTPS, and the
THttpApiServer server class can send
Session via CookiesTo 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 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=Qwerty2010should be transmitted as such:
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
GetJSONObjectAsSQL, the URI to SQL conversion is
very fast). So adding this extra parameter doesn't break the cache
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.
On the Server side, a dedicated RESTful Service, accessible
ModelRoot/Auth URI is to be called to register an User,
and create a session. On the Server side, two tables, implemented by
TSQLAuthUser will handle
respectively per-group access rights, and user authentication.
AuthUser are not available
on the Server
TSQLModel (i.e. if the
aHandleUserAuthentication parameter was set to
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
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
This will implement both Query Authentication together with a group-oriented per-user right management.
A dedicated RESTful service, available from the
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
- Client sends a
GET ModelRoot/auth?UserName=... request to the
- Server answers with an hexadecimal nonce contents (valid for about 5 minutes);
- Client sends a
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
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
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.
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
More details are available in the SAD document of the framework official
Feedback and comments are welcome on our forum.