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.<p>"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.<p>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?<p>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<p><pre><code> if (Permissions.isAdmin(userContext)) {
doOP(op);
} else {
// handle permission denied
}
</code></pre>
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.<p>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.<p><pre><code> // 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");
}
</code></pre>
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.<p>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.<p>The format of storing the perms as described in the article are <i>not</i> 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.
I started working on a Datalog-based authorization library called EACL, short for Enterprise Access Control Library: <a href="https://github.com/theronic/eacl" rel="nofollow">https://github.com/theronic/eacl</a>