DataSnap 2010 authentication throught TCP/IP Transport
One of the common questions that I get asked about DataSnap is related to authentication in a DataSnap Server using TCP/IP as the transport (TDSTCPServerTransporter component). If you are considering the HTTP transporter it is not a problem, the authentication component is there and provides an event for this.
It makes sense to ask about that, since without authentication anyone will be able to connect to your DataSnap Server and execute all Server Methods available. This post will demonstrate how to implement the authentication very easily.
I’m assuming you already know the basic stuff in DataSnap, if you already don’t know I recommend you to read some articles or watch some videos published in EDN.
Let starts talking about the client side, which needs to send the credentials (username and password) to be validated through the server side. The TSQLConnection is the way to connect client in to server, beside hostname, port, driver and other parameters, also it will contain the user credentials which will be part of the Params property.
In general we use the parameters UserName and Password, which is correct, but in this case I recommend you to use the parameters DSAuthenticationUser and DSAuthenticationPassword. DataSnap consider these parameters when you use HTTP as transporter, and if you would like to execute the server methods from DataExplorer you will be able to connect and your server will be able to verify the credentials, also these are the standard parameters.
Our code on the client side will be like this:
With SQLConnection1 do begin Params.Values['HostName'] := Server; Params.Values['Port'] := Port; Params.Values['DSAuthenticationUser'] := 'user'; Params.Values['DSAuthenticationPassword'] := 'password'; end;
On the server side we will use the component and event DSServer.OnConnect for the authentication process.
The event OnConnect has the parameter DSConnectEventObject which is using the property ConnectProperties provides all connection information, it means all parameters passed from the client side will be available including the parameters I mentioned before.
In the follow example the login and password get the respective values from the properties DSAuthenticationUser and DSAuthenticationPassword, after that occur the validation through the class TUser, which is a class I created to use in this sample.
In case of invalid credentials the server raises a Exception, any exception raised on the event OnConnect stop the connection, in other words, the client application will not be connected on the server.
With DSConnectEventObject.ConnectProperties do begin login := Properties.Values['DSAuthenticationUser']; password := Properties.Values['DSAuthenticationPassword']; end; userConn := TUser.Create; try if not userConn.IsValidUser(login, password) then raise Exception.Create('Invalid Login/Password'); finally userConn.Free; end;
My DataSnap samples was updated to reflect this authentication process and is already available, download here.
Lame form of authentication:
1) You’re transmitting plain text
2) You’re transmitting the password!
3) With a single call even using parameters may not be possible to use proper security and authentication.
4) Most authentication scheme require to exchange binary data, using “params” they should be encoded in strings.
The connection handshacking may require more than a single call to establish the security context. Datasnap lacks those kind of hooks. It really looks Embarcardero is unable to understand what security means in today world, and sticks to very minimal implementations useless in any real situation.
Go back to the drawing board, please, and design a modern solution, please – not one of the ’80s.
In 2010 authentication is still nothing more than how to send a user/password(!) in cleartext at Embarcadero?
As you already know you can encrypt the communication between client and server in DataSnap using filter. Daniele Tele created a very nice set of filters including encryption and compression – http://www.danieleteti.it/?p=168
So, you decide to send cleartext or encrypt the communication.
Luigi you again, you also already know about the Daniele filters. I can implement many others stuffs to improve security, but the idea of this post is to explain how they can handle the authentication.
Also, you have to understand that we wanna provide a flexible architecture the allow the developers to plugin and extend DataSnap, filters is one example.
I apoligize for the double post – they don’t appear imemdiately and I didn’t know the posts are moderated.
As already commented, Teti’s encryption filters have a basic flaw due to the Datasnap architecture: they can’t exchange a session key properly because of lack of proper hooks. If you store the key inside the application or PC you have just false security. And if you never change the session key you have false security again.
Moreover no matter how you encrypt the session, is a basic flaw to send the password itself. Even basic examples should teach proper security practices.
Above all, still Embarcadero makes simple what already is – sending a user/password – but doesn’t help developers to tackle actual. real issues to develop commercial grade appplications easily. For example how do you fit Kerberos/GSSAPI authentication or TLS/SSL authentication in Datasnap using TCP/IP transport? Those are industry-standard authentication methods, and should have been available in the box for an expensive tool like Delphi that aims to be called “Enterprise”.
Authentication may require more than one simple roundtrip to establish the authentication context. Has Datasnap hooks to allow for it? I didn’t find them, maybe it’s just I did not understand how it works fully yet, or maybe they are missing. All that SQL-related way of working does not help at all, IMHO.
A flexible architecture is welcome, but IMHO Datasnap design is not enough flexible to provide to plug-in the needed authentication, authorization and encryption.
For example I would like to see a connection phase were the client and the server are allowed to keep on exchanging data until both parties agree the authentication phase is completed. Same for exchanging the session encryption key. A single server side event or a simple stream filter is not enough.
And I’d wish to see an authorization plug-in hook that could check methods calls against ACLs instead of having to hard code that within each method call. Is there any facility to store the user context on the server? Or again everything is left to the developer? How do I store that context in each call to ensure the server knows who is calling it, especially in a stateless design?
Before designing this new Datasnap did someone give a look to what DCOM and WCF provide? It looks it was designed with database stored procedures in mind, not system-wide RPC. But stored procedures are called from uses already authenticated by the database communication layer, and calls are authorized by the DB ACLs, that’s why they are simpler from a user point view and a DB library does not have to care about much. But RCP is not that simple – all those features must be added.
Is there a way to require a client to use a filter?
I’ve noticed that if the server has filters set and the client that connects to the server has no filters defined, it still works fine.
But I want to require the client to use RSA filter to connect to the server.