Access Control Demystified

HP Fortify SCA focuses on identifying potential security issues but we are very interested in how we can help customers fix problems automatically. We have collaborated with Penn State researchers and have seen great initial results on how to automatically retrofit code with access control checks. This post outlines an approach for automatically retrofitting code with access control checks to prevent an unauthorized user from gaining access to security-sensitive operations in a program. An access control mechanism typically consists of two parts:

  1. An access control policy which specifies which principals can request specific operations (reads, updates, etc.) on security-sensitive resources (e.g., files, sockets, etc.).
  2. reference monitor which enforces such a policy.  Any reference monitor must satisfy the complete mediation property, meaning that every security sensitive operation must be preceded by a corresponding access control check.

Our focus is on the second part. To date, authorization hook placement in code bases such as the X server and PostgreSQL has largely been a manual procedure, often requiring multi-year long efforts. To automate this task, a solution must both infer the specification of security-sensitive resources in programs and identify locations of sensitive operations in code that must be preceded by an access control check.

To automatically infer the set of security-sensitive objects and operations, our approach requires three inputs: a) The source code b) The set of interfaces/variables where client-request can enter the server program c) the set of getter() routines for object collections such as lists, arrays etc. These may be a combination of language-wide specification for standard libraries, but will require programmer specification if custom collections are used.

The following figure illustrates the approach. The nodes marked A-L make up the control flow graph of a program.

access-control

There is a request interface through which a client request can enter the program. The program stores a container O of objects on behalf of clients.

The process of automatically placing access control hooks around security-sensitive operations is conducted in four major stages:

                                                                              

a)    Identifying tainted variables: We begin by tainting the client-request parameters/variables that we get as input. Through static taint propagation, we can identify the set of all program variables that can be influenced by the client.

b)    Identifying security-sensitive objects: Whenever a tainted variable is used as an identifier to a getter() function for a collection, the returned object(s) are identified as being security-sensitive. In the example above, the Lookup() function uses a tainted identifier. Therefore variable v is marked as a security-sensitive object.

c)    Identifying security-sensitive operations: We could consider the entire program segment that follows the object retrieval to be security-sensitive. So we can put an access check right when an object is retrieved. But this will end either with a client getting too much privilege or being prevented from legitimate functionality. In the example above, we can put an access check at node C. But only one of F-H, I-J or K-L will be taken during the program execution. If they are all doing fundamentally different things, then a client will need to be authorized for operations along all branches for even one branch to succeed (this violates least privilege).   Therefore, it makes sense to wait until we know what operation is actually being performed. Whenever a tainted variable is used in the predicate of control statement (if, switch, etc.), then the client is able to influence program branching. If any of these branches manipulates a security-sensitive object identified in the previous step, then we consider the branch to be a security-sensitive operation. In the above example, the predicate to E is tainted. So, the client is able to choose between F-H, I-J and K-L. Turns out that I-J doesn’t actually access the security-sensitive object v. So we are left with two security-sensitive operations, F-H and K-L that need to be fitted with access control checks. 

d)    Identifying locations for placing hooks: The most obvious place to put access checks would be right before every security-sensitive operation that has been identified in the previous step. However, this might be overkill since the same operation may be performed more than once along the same program path. In the above example, if node D also does a read v operation and we put an access check there, we don’t need to put one again at F-H.  We don’t want to incur performance penalty from checking the same operation twice. We have come up with a novel method for removing redundant access checks using the control dependence relationship between security-sensitive operations. (Please refer to our paper for the algorithm and more details).  

We implemented our approach using the CIL framework and evaluated it on real world programs like X Server, PostgreSQL, memcached, the Linux kernel Virtual File System and others. We found that our approach significantly reduces the programmer effort required to retrofit these programs. For example, in the case of X Server our approach resulted in 532 access checks. So instead of examining 28 kLOC the programmer only has to examine our 532 suggestions. X Server also has access hooks placed manually as a result of the XACE work. We were able to compare the hooks proposed by our automated technique with those placed manually.  We found that in general our tool produces finer-grained hooks compared to those manually placed. I will give an example to illustrate why. The function CopyGC() in the X Server source code contains a switch statement with 23 cases each of which copies a different field from a source variable to a sink variable. While the experts have placed a single hook before the switch statement implying that copying of any field is tantamount to the same operation, our approach places a hook at each of the 23 branches since it considers them to be different. Our future work will explore how we can establish relationships between the inferred security-sensitive operations in order to optimize access check placement.

 

This post was contributed by Divya Muthukumaran during her summer internship with HP Software Security Research.

 

Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
Showing results for 
Search instead for 
Do you mean 
About the Author
Featured


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation.