Question

When writing an application based on Datomic and Clojure, it seems the peers have unrestricted access to the data. How do I build a multi-user system where user A cannot access data that is private to user B?

I know I can write the queries in Clojure such that only user A's private data is returned... but what prevents a malicious user from hacking the binaries to see user B's private data?

UPDATE

It seems the state of Clojure/Datomic applications is in fact lacking in security based on the answer from @Thumbnail and the link to John P Hackworth's blog.

Let me state more clearly the problem I see, because I don't see any solution to this and it is the original problem that prompted this question.

Datomic has a data store, a transactor, and peers. The peers are on the user's computer and run the queries against data from the data store. My question is: how to restrict access to data in the data store. Since the data store is dumb and in fact just stores the data, I'm not sure how to provide access controls.

When AWS S3 is used as a data store the client (a peer) has to authenticate before accessing S3, but once it is authenticated doesn't the peer have access to all the data!? Limitted only be the queries that it runs, if the user wants to get another user's data they can change the code, binary, in the client so that the queries run with a different user name, right? To be clear... isn't the access control just a condition on the query? Or are there user specific connections that the data store recognizes and the data store limits what data is visible?

What am I missing?

In a traditional web framework like Rails, the server side code restricts all access to data and authenticates and authorizes the user. The user can change the URLs or client side code but the server will not allow access to data unless the user has provided the correct credentials.

Since the data store in Datomic is dumb, it seems it lacks the ability to restrict access on a per user basis and the application (peer) must do this. I do not want to trust the user to behave and not try to acquire other users' information.

A simple example is a banking system. Of course the user will be authenticated... but after that, what prevents them from modifying the client side code/binary to change the data queries to get other users' account information from the data store?

UPDATE - MODELS

Here are two possible models that I have of how Datomic and Clojure work... the first one is my current model (in my head).

  • user's computer runs client/peer that has the queries and complete access to the data store where user was authenticated before the client started thereby restricting users to those that we have credentials for.
  • user's computer has an interface (webapp) that interacts with a peer that resides on a server. The queries are on the server and cannot be modified by the user, thereby access controls are under access control themselves by the security of the server running the peer.

If the second model is the correct one, then my question is answered: the user cannot modify the server code and the server code contains the access controls... therefore, the "peers" which I thought resided on the user's computer actually reside on the application server.

Was it helpful?

Solution

Your second model is the correct one. Datomic is designed so that peers, transactor and storage all run within a trusted network boundary in software and on hardware you control. Your application servers run the peer library, and users interact with your application servers via some protocol like HTTP. Within your application, you should provide some level of user authentication and authorization. This is consistent with the security model of most applications written in frameworks like Rails (i.e. the end user doesn't require database permissions, but rather application permissions).

Datomic provides a number of very powerful abstractions to help you write your application-level auth(n|z) code. In particular, since transactions are first-class entities, Datomic provides the ability to annotate your transactions at write-time (http://docs.datomic.com/transactions.html) with arbitrary facts (e.g. the username of the person responsible for a given transaction, a set of groups, etc.). At read-time, you can filter a database value (http://docs.datomic.com/clojure/index.html#datomic.api/filter) so that only facts matching a given predicate will be returned from queries and other read operations on that database value. This allows you to keep authz concerns out of your query logic, and to layer your security in consistently.

OTHER TIPS

As I understand it ... and that's far from completely ... please correct me if I'm wrong ...

The distinguishing feature of Datomic is that the query engine, or large parts of it, reside in the database client, not in the database server. Thus, as you surmise, any 'user' obtaining programmatic access to a database client can do what they like with any of the contents of the database.

On the other hand, the account system in the likes of Oracle constrains client access, so that a malicious user can only, so to speak, destroy their own data.

However, ...

Your application (the database client) need not (and b****y well better not!) provide open access to any client user. You need to authenticate and authorize your users. You can show the client user the code, but provided your application is secure, no malicious use can be made of this knowledge.

A further consideration is that Datomic can sit in front of a SQL database, to which constrained access can be constructed.


A web search turned up Chas. Emerick's Friend library for user authentication and authorization in Clojure. It also found John P Hackworth's level-headed assessment that Clojure web security is worse than you think.

You can use transaction functions to enforce access restrictions for your peers/users. You can put data that describes your policies into the db and use the transaction function(s) to enforce them. This moves the mechanism and policy into the transactor. Transactions that do not meet the criteria can either fail or simply result in no data being transacted.

Obviously you'll need some way to protect the policy data and transaction functions themselves.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top