A Conservative Algorithm for Computing the Flow of Permissions in Java Programs
Gleb NaumovichPolytechnic University
Brooklyn, USA
Presented byDavid Chays
Polytechnic University
Motivation
Security problems may exist on two levelsProtocol level
Attacks exploit weakness of encryption/authentication/transfer protocols
Implementation levelAttacks exploit errors in implementation of
secure protocolsJava developers rely on the built-in
security model to provide protection against some types of attacks
This model is complicated, so application programmers are likely to make mistakes in its use
This work aims at validating the use of the Java security model by developers
Java security model: permissions
Remotely loaded classes may be given permission to perform specific operations
grant signedBy “Poly”, codebase http://cis.poly.edu/gleb/classes/- { permission java.io.FilePermission “/tmp/*”, “read, write”;}
Code dealing with sensitive operations (e.g. reading/writing, opening sockets) has to check that callers have sufficient permissions
AccessController.checkPermission(new java.io.FilePermission( “/tmp/*”, “read, write”));
Permission checking: running examplepublic class Account { private Money balance; private String persistentLocation;
public Account(Money initialAmount, String persistentLocation) { AccessController.checkPermission( new NewAccountPermission ("NewAccountPermission")); this.balance = (Money) initialAmount.clone(); this.persistentLocation = persistentLocation; }
public Money getBalance() { AccessController.checkPermission( new BalancePermission("BalancePermission")); return (Money) this.balance.clone(); }
public void credit(Money amount) { AccessController.checkPermission( new CreditPermission("CreditPermission")); this.balance.add(amount); this.write(); }
public void debit(Money amount) { AccessController.checkPermission( new DebitPermission("DebitPermission")); this.balance.subtract(amount); this.write(); }
private void write() { AccessController.doPrivileged() ( new PrivilegedAction() { public Object run() { FileWriter writer = new FileWriter (this.persistentLocation); writer.write(balance); writer.close(); } } ); }
public void transfer(Money amount, Account toAccount) { this.debit(amount); toAccount.credit(amount); }}
Permission checking: running example (cont.)
public class AccountWithProtection extends Account { private Account protection;
public AccountWithProtection(Money initialAmount, String persistentLocation, Account protection) { super(initialAmount, persistentLocation); this.protection = protection; }
public void debit(Money amount) { AccessController.checkPermission( new CustomerPermission("CustomerPermission"); AccessController.doPrivileged() ( new PrivilegedAction() { public Object run() { Money currentBalance = this.getBalance(); if (currentBalance.compare(amount) == Money.LESS_THAN) { Money toTransfer = amount.clone(); toTransfer.subtract(currentBalance); this.protection.transfer(toTransfer, this); } super.debit(amount); } } ); }}
public class CustomerInterface { public static void main(String[] args) { Account savings = new Account( new Money(3000, 0), "savings"); AccountWithProtection credit = new AccountWithProtection( new Money(5000, 0), "credit", savings); AccountWithProtection overdraft = new AccountWithProtection( new Money(1000, 0), "overdraft", credit); AccountWithProtection checking = new AccountWithProtection( new Money(2000, 0), "checking", overdraft);
checking.debit(new Money(10000, 0)); }}
How checkPermission works
Examines all classes on the call stack and makes sure that all of them have permission to do an operation
class MainClass { public static void main(String [] args) { … MyClass o = new MyClass(); o.m(); }}
class MyClass { … public void m() { … RemoteClass obj = new RemoteClass(); obj.passAccount(new Account(1000)); }}
MainClassmain
MyClassm
RemoteClasspassAccount
Accountdebit
}JVM makessure all classeshave the permissionchecked in Account
Java security model: privileged regions
“Trusted” code can temporarily grant additional privileges to other classes in the call stack
Useful in situations where untrusted classes can be allowed to perform specific actions that would otherwise require privileges
E.g., an application programmer may decide to
Not give untrusted classes permissions to check balance, credit, or debit an account
Let untrusted classes find out whether the account balance is above or below $1000
How privileged regions work
class MainClass { public static void main(String [] args) { … MyClass o = new MyClass(); o.m(); }}
class MyClass { private Account account; … public void debit(int amount) { AccessController.doPrivileged( new PrivilegedAction() { this.account.debit(amount); } } public void m() { … RemoteClass obj = new RemoteClass(); obj.giveAccess(MyClass.this)); }}
MainClassmain
MyClassm
RemoteClassgiveAccess
Accountdebit
MyClassdebit
Privileged{}
JVM checks thatthese classes haveDebitPermission
JVM does not checkthat these classes have DebitPermission
How permissions are defined
public class CreditPermission extends BasicPermission { ...
public boolean implies(Permission p) { return (p instanceof CreditPermission) || (p instanceof FilePermission); }}
What we would like to verify
Often, the developer can easily identify “sensitive” parts of the program
Want to verify that these parts are protected by permissions
Formally: by the time program point S is reached, permission P has been checked
Have to take into account:Interprocedural pathsImplication relationships between
permissions“Insecure entry points” --- code that can
be called by malicious classes
Our approach (high level)
Construct a representation of implies relationships among permissions in the program
Construct a representation of relationships among sets of permissions
Construct a static model of the program
Call graph basedRun data flow analysis to determine for
each point in the program what permissions must be checked on all paths to that point
Permission lattice
Define a partial order among permissions based on their implication relationship
Because our approach is static, have to place several restrictions on how permissions are defined:
Values of permission parameters have to be statically defined strings
The implies method defined in a way that a permission object implies all permission objects of the same class
Permission objects are immutable
Essentially, instead of permission objects, we deal with statically defined permission classes
In practice, such permission definitions are common
Permission lattice for the example
Permission set lattice
To analyze the flow of permissions through the program, need to reason about sets of permissions
Have to define a lattice that describes relationships among sets of permissions, based on the implication relationships
Cannot simply say that permission set P1 implies permission set P2 if any permission in P2 is implied by a permission in P1
Would not be a partial order (e.g. consider sets {p1} and {p1 p2}, where p1 implies p2)
Define canonical permission setsGiven a set of permissions, simply remove all
permissions that are implied by other permissions in this set
Permission set lattice for the example
Program representation: Permission Call Graphs (PCGs) Similar to Jensen et al. [IEEE symposium on
security and privacy, 1999] and Nitta et al. [ACM symposium on access control models and technologies, 2001]
Captures relationships between control flow, permission checking, and privileged regions
Each node represents one of Start of a method execution Call to a method checkPermission call Entrance to a privileged region Exit from a privileged region
Constructed from control flow graphs for all methods in the program
Assume that all public methods of public classes can be called by external classes
An overapproximation
Permission call graph for the example
Data flow algorithm for computing the permission flow Forward-flow all-paths flow-sensitive context-
insensitive analysis For each node n in the PCG, differentiate
between two sets of paths leading to it: Paths on which n is executed inside a privileged region Paths on which n is executed outside privileged regions
The algorithm computes two sets of permissions for each node n:
Permissions that are checked on all executions to n where n is executed inside a privileged region: priv(n)
Permissions that are checked on all executions to n where n is executed outside privileged regions: unpriv(n)
After a fixed point is reached, a conservative estimate of all permissions checked on all paths to n is the intersection of the two permission sets for n
Example of computing priv and unpriv sets for a PCG node
checkPermission(DebitPermission)
m1m2
priv(m1) = {CreditPermission}unpriv(m1) = {CreditPermission}
priv(m2) = {DebitPermission}unpriv(m2) = {WritePermission}
n
INpriv(n) = {WritePermission}INunpriv(n) = {WritePermission}
priv(n) = {WritePermission}unpriv(n) = {DebitPermission}
Results of the analysis for the example
Checking properties about permissions
Given the information from the analysis, can answer questions of the form “is permission p always checked by the time statement s is executed?”
Cannot directly answer questions like “is one of permissions p1, …, pk always checked by the time statement s is executed?”
The algorithm uses the meet operation on the permission set lattice to compute the flow of permissions into a node
Can check such properties if the permission lattice is modified
And consequently, the permission set lattice is modified
Property-modified lattice for the example
• The property checks whether one of permissions NewAccountPermission, CreditPermission, DebitPermission is checked on all executions to node 16
A violation is detected when checking this property!The analyst can follow the path of
permissions through the graph to determine on what type of executions none of permissions NewAccountPermission, CreditPermission, DebitPermission are checked before node 16 is executed
It would make sense for CustomerPermission to imply BalancePermission, CreditPermission, and DebitPermission
After the implies method of CustomerPermission is modified accordingly, the analysis successfully proves the property
Future work
Implementation and experimentsShould scale well, since the analysis is
cubic in the size of the PCG and the number of permission types used in the program
Previous approaches (Jensen et al., Nitta et al.) used exponential-time analyses
Believe that in most practical situations, permissions are defined and used in a static fashion
Investigate whether more complex permission-related properties need to be checked
The approach is context-insensitiveDo not believe that there is much to be
gained by context-sensitivity, but have to check experimentally
Questions?
I’ll try to answer, but if that fails…Email: [email protected]