MIT's CTSS was designed in 1961 to provide multiple users independent share of a large computer.The designers soon discovered there was a huge appetite to share programs and data with one another. This sparked some of the earliest conversations around computer security and led to security being an explicit design goal for Multics. Years after Multics' release, Saltzer and Schroeder published The Protection of Information in Computer Systems which took lessons from its design and real-world use. Their work is one of the most cited security papers in history and the first to use many terms we use today including "Least Privilege".
In this article, I will present three access control methods beginning with Access Control List (ACL) - the mechanism implemented by Multics - then explore Role Based Access (RBAC) followed by Attribute Based Access (ABAC). In a later post I will examine Purpose Based Access Control (PBAC) which was introduced in 2008 as the industry was prioritizing privacy on the web. PBAC provides a way to map data access intentions with the intentions found in privacy policies agreed to by users. It relies on mechanisms provided by RBAC and ABAC so to understand PBAC, I'd like to first get comfortable with the mechanisms powering both RBAC and ABAC. I often feel that implementing something in code helps me fully grok how it works. I will attempt to cement some of these access control concepts in the reader's mind by writing some Python.
We’re going to work up to RBAC by first exploring ACLs since RBAC was designed to account for ACL's drawbacks. Let's begin by setting up some code to support our examples.
The components defined here will be used in each of three following access control examples.
Using these terms, we can state the purpose of any access control system as to answer the question: "Is this Principal authorized to perform this Action on this Record?" ACL, RBAC and ABAC have different capabilities and qualities around scalability and flexibility but they will each allow us to get to the answer.
Let’s add one more class. We need to implement a System that can perform Actions on our Records.
Notice that records and an Authorizer are passed into to create a new System. It's here where we'll implement the specifics of each access control scheme. In ACL and ABAC we'll attach different types of metadata to our records and our RBAC implementation won't see any Record metadata. But the biggest difference will exist in the logic and context brought by the authorizer function and it's closure.
Before coding up an implementation of access control lists, we should define the success criteria for all of our forthcoming implementations. The design goal of System was to enable the same tests to be run on different implementations of Authorizer. Before we define those tests let's set up some common test data.
Now let's create a test plan that will test that a given access control system can enforce the following scenario:
| Principal | Alyssa's Patient Record | Ben's Patient Record || ----------- | ----------------------- | -------------------- || Alice | READ, WRITE | READ, WRITE || Bob | --- | READ |
We can use these assertions to verify each System we are about to create. One note: we are not supporting explicit denies which is a common capabiltiy in modern access control. It's worth learning how explicit denies can effect interations of RBAC roles and ABAC rules but they are not in scope for the concepts we'll cover today. All permissions we define below will have the effect of allowing the provided Action.
ACLs were first implemented to protect the Multics filesystem in 1965. Today, they are commonly used in file systems, networking security, and cloud services. The [first access controls for S3 were in fact ACLs]( {% post_url /2022-06-24-aws-whats-new-iam %} ).
If you take another look at the table of patient records above, each column can be thought to represent an ACL. ACLs are resource-oriented controls, which is they are defined in the context of a specific resource (in our case an individual Record). So there is a one-to-one relationship between Records and ACLs.
For this reason, we're going to attach our ACLs directly to the Records rather than include a reference (e.g. record_id: RecordId) as a property of the ACL. Though specific ACLs implementations may defer, this approach helps understand how ACLs are logically structured from the system administrator's point of view. This will help us understand the strengths and weaknesses of ACLs and help us highlight their differences from the other access controls.
Great! This passes our tests and helps us understand how a simple ACL-based system could work. The System uses our implementation of Authorizer to inspect the list of AccessControls attached to a Record to determine of the Principal can perform the Action on that Record.
This also helps us understand it could be difficult to scale a team and number of records with a simple implementation like this. Some policy updates could require you to update every single Record. Two features found in modern ACL implementations that help manageability include:
If we were to replace the reference to Principal with a reference to a new Group object containing a list of Principals (or other Groups), system administrators could move Principals in and out of Groups without impacting ACLs. The Groups can be configured in hierarchies for added flexibility and have names that map to business functions to help reason about who should get access to what.
Further, if we also allowed Records to be grouped (perhaps via Tables or Folders), we could add and remove references to Records without updating the ACLs themselves. ACL-based systems that support both capabilties approach a minimal RBAC implementation. The missing piece would be to externalize Actions so they don't need to be defined for every Record and we could get rid of ACLs all together. RBAC provides the model we need to do just that.
RBAC was introduced in 1992 to make it easier to define relationships between Principals and the Records we're trying to protect. Roles contain references to both Users and Objects or in our case Principals and Records. They also contain the Actions the Principals are able to perform on those Records.
From the 1992 paper introducing RBAC
Let's build a naive RBAC implementation to get a feel for its capabilities.
Notice that our records no longer have metadata which is where we were storing our ACLs. That metadata has been promoted to its own Role object which means we need to include references back to the Records in the form of RecordIds. Consider an example where we have a common permission set and many Records. RBAC makes our life easier by allowing us to create one Role with that permission set rather than putting the same permissions in a list of AccessControls for each Record.
Features not expressed in this example include role hierarchies in which a Role can inherit permissions from its ancestors. This is handy since organizations often have HR hierarchies that roughly map to gradually more permissive permissions schemes. Another difference is that this code has no concept of an "active" Role. It just checks all Roles that refer to the Principal and Record in question. Finally, another detail that could be considered missing from this example: most modern RBAC systems support Principal groups so a Principal hierarchy can be defined independent of the Role hierarchy.
RBAC is extremely common in software systems and has brought us a long way since it was formalized in 1992. The downsides become apparent as least privilege gains priority in an organization. Whittling Roles down to only provide the minimum number of permissions needed can yield a large number of permutations resulting in an explosion of Roles to manage. Consider that there are 243 possible Actions for AWS' S3 alone. Modern security policies are also incorporating new attributes that can offer more protection such as a user's IP address or whether this person is attempting access during business hours. Pure RBAC is missing capabilities to support these requirements. This is where ABAC comes into the picture.
RBAC and ACLs enable you to define relationships between subjects (Principals) and resources (Records). ABAC allows you to make those relationships conditional based on attributes. Those attributes can come from the Principal, the System or the data itself. The ability to define access logic vs strict relationships as well as the ability to incorporate such a rich set of information makes ABAC very powerful.
ABAC is also known as policy-based access control. To model it out, we'll create a Policy class that contains a set of Rules as well as the Principals and Records those rules pertain to. Let's see if we can implement a simple attribute-based control system that can help us pass the tests we've been using.
We did the minimum to get our tests to pass by creating rules that referred directly to RecordIds. This is not a likely or practical application of ABAC but it demonstrates how the mechanics differ from RBAC and ACLs. Most ABAC-based access control systems work along with RBAC so you can assign Rules to Roles intead of a static list of PrincipalIds. If we were using a hybrid ABAC/RBAC system, we'd probably just set up our Roles to pass our tests and not bother with Rules.
So what is a better example to get sense of the power of ABAC? Well, let's take a minute to imagine all the types of attributes we could possibly have available to us so that we can craft extremely secure policies.
Examples of attributes that an ABAC system could have available to it
Let's add code to the same method to help us further explore the capabilities of an ABAC-based system.
Hopefully these examples provide a glimpse into the power and flexibility of ABAC. Modern ABAC systems include RBAC capabilities so you can get the power of conditional policies combined with the flexibility of hierarchies. They also include much more expressive policy languages than I've created here. Check out XACML to get an idea of how expressive ABAC policies can be.
If you've spent anytime managing access for software systems, you almost certainly used some version of ABAC and RBAC so these mechanisms should be familiar to you. The point to coding some of this logic is to set the foundation for exploring Purpose Based Access Control which requires capabilities of ABAC to create Conditional Roles. Look for a code-heavy exploration of PBAC in a future post.