Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
The search for a perfect access control system (goteleport.com)
110 points by aberoham on May 16, 2021 | hide | past | favorite | 11 comments


One of the interesting bits in this article to me is the idea of encoding the origin of a security rule in a security rule.

"Why should I let Bob write to file.txt? Because Alice says it's OK."


This is a nice overview of the types of authorization categories you'd find covered in a book on security engineering. But in my experience, the real issues with authorization systems is not how the rules are encoded but how you make sure the rules are enforced.

The goal is to make the code auditable so that you don't need to audit the entire codebase in order to determine whether a given rule is being enforced, you only need to enforce a small security module, called a "security kernel" and check that. Those definitions were worked out in the first orange book standards. So the problem becomes how do you write a large piece of software to have a small security kernel?

Most software today does not have a security kernel. If you are lucky, there is a central table of security rules somewhere (whether capabilities or ACL lists or some other data structure is not important). And your entrypoints query the table

   if (Permissions.isAdmin(userContext)) {
     doOP(op);
   } else {
     // handle permission denied
   }
That Permissions module could be a query to Zanzibaar or LDAP or a custom DB, the point is that the controller logic is required to check, and if the author of the controller code doesn't check or forgets to check, then access control is not enforced. So even though you have a centralized list of policies, you still need to audit every entrypoint to make sure the policies are being enforced, thus no security kernel.

A better system is to push the authorization checks directly into the resource stores themselves so the controller only handles the access denied error but does not perform any access checks, the access checks are performed in the DB itself (or just above the DB) containing the data/settings that are being protected.

   // controller code
   try {
     doOP(op);
   } catch(PermissionException) {
     // handle permission denied
   }


   // DB/JPA layer
   public string doOp(Context userContext, Op op)
      if (QueryPerm(userContext, op)) {
        private_doOp(op);
      } else {
        raise PermissionException("access denied");
      }
This could be as a stored procedure, hooks directly in your persistence framework, or DB security function. The farther down you push this check the better, and ideally there is only a handful of places that are the gatekeepers to your resources that need to query for permissions.

So you need to find a way of propagating all your security attributes down to your persistence layer, and sign them somehow so that your controller or intervening code can't mess with your context. This is hard to do and imposes some rigidity on the rest of the code and there are a number of different approaches you can take. Then you have to worry about caching permissions if you are using something like an external perm DB versus something like signed capabilities in the session context. These are the hard trade offs when designing authorization systems.

The format of storing the perms as described in the article are not the types of hard quesions you normally deal with, and do not help you design a secure system, but they are useful to get the jargon and understand the possible schemas of your permissions store.


The obvious answer is to store your secret data within the security kernel ACLs themselves, or better yet, encryption keys for the secret data.


Right, that's what I mean by putting the enforcement code directly into the DB or as close to it as possible. But most authoriztion issues are not just for secret data. You may have a rule that only the admin can enroll new users. So then you have to hook the "enroll new user" service into the security kernel, and the easiest way to do that is at the data store level, e.g. right before (or during) the insert user operation.


It’s interesting this doesn’t make any mention of object-capability security.


Some very interesting work is being done on this front, like the Spritely Project by W3C ActivityPub recommendation co-author Christopher Lemmer Webber: https://spritelyproject.org/


to be honest, most object-capability security models I've seen ultimately form a precomputed instance of the models described in the article - they are the implementation detail where simpler capability system replaces the complex matrices of ACLs etc.


That's my observation as well, but I think this is a shame: It has tremendous advantages for principle security (i.e. the security of the owner of the data, as opposed to the security of the owner of the database).

For example:

I have a helpdesk, where I need operators to be able to look up the details of users, but only when the user requests it. My users compute the capability string and give it to the helpdesk operator (who cannot merely give it to other helpdesk operators- they have to compute a new capability string showing the chain of custody).


I started working on a Datalog-based authorization library called EACL, short for Enterprise Access Control Library: https://github.com/theronic/eacl


This looks really great! I was just mulling over doing postgres-style row-level auth in datahike and you've already put it together. I'll give it a spin this week, thanks for mentioning it.


Fair warning: it's pretty much in super-alpha state. The Datalog queries are elegant, but EACL needs a lot of work and tests. Some of the features in the README are vaporware (like bitvector impl.), but I stand by the design.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: