<<

Inference of Expressive Declassification Policies

Jeff Vaughan Stephen Chong

IEEE Security and Privacy May 23, 2011 @output "out" @output "out" @output "out" @output "out" @input "authCheckOk" @input "secret" @input "secret" @input "secret" @input "secret"

class Client { return e; return true; throw new IllegalArgumentException(); } } } public static String fakeReadingArgs(int i) } if(i==0){ throws java.util.NoSuchElementException { } if ( == MOLE) { return this.next; if(i<42) { return null; return true; } return "bork bork"; } } return next.removePersistent(i−1); } else { } throw new java.util.NoSuchElementException(); public Role readerOf(String key) { return (s == r); } if (lookup(key) != null) { } public void set(int i, Object o) { } return lookup(key).getReader(); if(i < 0){ } } throw new IllegalArgumentException(); public static void main(String args[]){ return null; import java.util.Iterator; } String ; } import java.util.NoSuchElementException; if(i==0){ String ; this.contents = o; String key; public String secretOf(String key) { public class SimpleArrayList implements Iterable { } else { try{ if (lookup(key) != null) { this.next.set(i−1, o); uname = fakeReadingArgs(0); return lookup(key).getData(); List contents; } pwd = fakeReadingArgs(1); } } key = fakeReadingArgs(2); return null; SimpleArrayList() {this.contents = new Nil();} } } public Object () throws NoSuchElementException { catch (Exception e){ } int size() { return contents.size(); } return this.contents; System.out.println( class DbEntry{ boolean isEmpty() {return contents.isEmpty(); } } private String key; void () { this.contents = new Nil(); } "Useage \"java Client \"" ); private Role reader; public List () throws NoSuchElementException { return; private String data; private static abstract class List { return this.next; } private List() { } } public DbEntry(String key, Role reader, String data){ abstract int size(); } Login.init(); this.key = key; abstract boolean isEmpty(); Role r = Login.doLogin(uname, pwd); this.reader = reader; public abstract Object get(int i); //Zero−based lookup this.data = data; public abstract List removePersistent(int i); public Object get(int i) { if (r == null){ } public abstract void set(int i, Object o); return this.contents.get(i); "Login Failed"System.out.println( ); public abstract Object head() throws NoSuchElementException; } return; public String getKey(){ return this.key; } public abstract List tail() throws NoSuchElementException; } public Role getReader(){ return this.reader; } } public void add(Object card) { public String getData(){ return this.data; } this.contents = new Cons(card, this.contents); Database db = new Database(); } } class Login { private static class Nil extends List { if (authorizedToRead(r, key, db)) { private Nil(){}; public void remove(int i) { db.secretOf(key));System.out.println( private static class PwdTableEntry{ public int size() { return 0; } this.contents = this.contents.removePersistent(i); } else { String uname; public boolean isEmpty() { return true; } } String pwd; public Object get(int i) { throw new IllegalArgumentException(); } Role role; public List removePersistent(int i) { public void set(int i, Object o) { private PwdTableEntry(String uname, String pwd, Role role){ throw new IllegalArgumentException(); } this.contents.set(i, o); this.uname=uname; } "Access not authorized");System.out.println( this.pwd=pwd; public void set(int i, Object o) { throw new IllegalArgumentException(); } this.role=role; } public void addAll(SimpleArrayList lst) { } } Iterator i = lst.iterator(); } public Object head() throws NoSuchElementException { while(i.hasNext()){ private static boolean authorizedToRead(Role r, String key, Database db){ throw new NoSuchElementException(); this.add(i.next()); Role required = db.readerOf(key); private static SimpleArrayList pwds; } } ) (Role.lteq(required,return r)); ( } } public static void init() { public List tail() throws NoSuchElementException { } pwds = new SimpleArrayList(); throw new NoSuchElementException(); private class SimpleArrayListIterator implements Iterator{ class Database { pwds.add(new PwdTableEntry("Bond", "007", Role.SUPER_SPY)); } pwds.add(new PwdTableEntry("M", "0", Role.SPY)); } private List lst; private SimpleArrayList data; pwds.add(new PwdTableEntry("Q", "1337", Role.TECH_GUY )); } private static class Cons extends List{ public SimpleArrayListIterator(SimpleArrayList simpleArrayList) { public Database() { private Object contents; lst = simpleArrayList.contents; data = new SimpleArrayList(); public static Role doLogin (String userName, String pwd){ private List next; } data.add(new DbEntry("A", Role.SPY, "AA"))); ( for(int i=0; i < pwds.size(); i++){ private Cons(Object , List n){ //slow but remove is a pain with a persistent data structure if (pwds.get(i) instanceof PwdTableEntry) { if(n==null){ public boolean hasNext() { PwdTableEntry e = (PwdTableEntry) pwds.get(i); throw new IllegalArgumentException(); return (!lst.isEmpty()); if( e.uname.equals(userName) && e.pwd.equals(pwd) ){ } } return e.role; } this.contents = c; public Object next() { data.add(new DbEntry("B", Role.SUPER_SPY, } this.next = n; if(lst instanceof Cons){ "BB"))); ( } } Cons c = (Cons) lst; return null; lst = c.tail(); } public int size() { return c.head(); return 1+next.size(); } } } class Role { throw new NoSuchElementException(); data.add(new DbEntry("C", Role.MOLE, public Object get(int i) { } "CC"))); ( public static final Role SPY = new Role("spy"); if(i < 0){ public static final Role SUPER_SPY = new Role("007"); throw new IllegalArgumentException(); public void remove(){ public static final Role MOLE = new Role("mole"); } throw new UnsupportedOperationException(); public static final Role TECH_GUY = new Role("tech"); if(i==0){ } return this.contents; } data.add(new DbEntry("D", Role.SPY, private final String id; } ""))); ( private Role(String id){ return next.get(i−1); public Iterator iterator() { } this.id = id; } return new SimpleArrayListIterator(this); } } //returns null if no such entry } private DbEntry lookup(String key) { public static boolean lteq(Role r, Role s){ public boolean isEmpty() { //I think we want a termination annotation here if (r == null || s == null){ return false; for(int i=0; i < data.size() ; i++){ return false; } if(data.get(i) instanceof DbEntry) { } DbEntry e = (DbEntry) data.get(i); public List removePersistent(int i) { if(e.getKey().equals(key)){ if (s == SUPER_SPY) { if(i < 0){ 2/24 @output "out" @output "out" @output "out" @output "out" @input "authCheckOk" @input "secret" @input "secret" @input "secret" @input "secret"

class Client { return e; return true; throw new IllegalArgumentException(); } } } public static String fakeReadingArgs(int i) } if(i==0){ throws java.util.NoSuchElementException { } if (r == MOLE) { return this.next; if(i<42) { return null; return true; } return "bork bork"; } } return next.removePersistent(i−1); } else { } throw new java.util.NoSuchElementException(); public Role readerOf(String key) { return (s == r); } if (lookup(key) != null) { } public void set(int i, Object o) { } return lookup(key).getReader(); if(i < 0){ } } throw new IllegalArgumentException(); public static void main(String args[]){ return null; import java.util.Iterator; } String uname; } import java.util.NoSuchElementException; if(i==0){ String pwd; this.contents = o; String key; public String secretOf(String key) { public class SimpleArrayList implements Iterable { } else { try{ if (lookup(key) != null) { this.next.set(i−1, o); uname = fakeReadingArgs(0); return lookup(key).getData(); Inferred policyList contents; } pwd = fakeReadingArgs(1); } } key = fakeReadingArgs(2); return null; SimpleArrayList() {this.contents = new Nil();} } } public Object head() throws NoSuchElementException { catch (Exception e){ } int size() { return contents.size(); } return this.contents; System.out.println( class DbEntry{ boolean isEmpty() {return contents.isEmpty(); } } private String key; 7→ void clear() { this.contents = new Nil(); } "Useage \"java Client \"" ); private Role reader; out if (authCheckOk[0]) public List tail() throws NoSuchElementException { return; private String data; private static abstract class List { return this.next; } private List() { } } public DbEntry(String key, Role reader, String data){ abstract int size(); } Login.init(); this.key = key; thenabstract boolean Reveal isEmpty(); (secret[0+], Role r = Login.doLogin(uname, pwd); this.reader = reader; public abstract Object get(int i); //Zero−based lookup this.data = data; public abstract List removePersistent(int i); public Object get(int i) { if (r == null){ } public abstract void set(int i, Object o); return this.contents.get(i); "Login Failed"System.out.println( ); public abstract Object head() throws NoSuchElementException; } return; public String getKey(){ return this.key; } public abstract List tail() throws NoSuchElementException;authCheckOk[1+]) } public Role getReader(){ return this.reader; } } public void add(Object card) { public String getData(){ return this.data; } this.contents = new Cons(card, this.contents); Database db = new Database(); } } class Login { private static class Nil extends List { if (authorizedToRead(r, key, db)) { private Nil(){}; public void remove(int i) { db.secretOf(key));System.out.println( private static class PwdTableEntry{ public int size() { return 0; } this.contents = this.contents.removePersistent(i); } else { String uname; public boolean isEmpty() { return true; } } String pwd; public Object get(int i) { throw new IllegalArgumentException(); } Role role; public List removePersistent(int i) { public void set(int i, Object o) { private PwdTableEntry(String uname, String pwd, Role role){ throw new IllegalArgumentException(); } this.contents.set(i, o); this.uname=uname; } "Access not authorized");System.out.println( this.pwd=pwd; public void set(int i, Object o) { throw new IllegalArgumentException(); } this.role=role; } public void addAll(SimpleArrayList lst) { } } Iterator i = lst.iterator(); } public Object head() throws NoSuchElementException { while(i.hasNext()){ private static boolean authorizedToRead(Role r, String key, Database db){ throw new NoSuchElementException(); this.add(i.next()); Role required = db.readerOf(key); private static SimpleArrayList pwds; } } ) (Role.lteq(required,return r)); ( } } public static void init() { public List tail() throws NoSuchElementException { } pwds = new SimpleArrayList(); throw new NoSuchElementException(); private class SimpleArrayListIterator implements Iterator{ class Database { pwds.add(new PwdTableEntry("Bond", "007", Role.SUPER_SPY)); } pwds.add(new PwdTableEntry("M", "0", Role.SPY)); } private List lst; private SimpleArrayList data; pwds.add(new PwdTableEntry("Q", "1337", Role.TECH_GUY )); } private static class Cons extends List{ public SimpleArrayListIterator(SimpleArrayList simpleArrayList) { public Database() { private Object contents; lst = simpleArrayList.contents; data = new SimpleArrayList(); public static Role doLogin (String userName, String pwd){ private List next; } data.add(new DbEntry("A", Role.SPY, "AA"))); ( for(int i=0; i < pwds.size(); i++){ private Cons(Object c, List n){ //slow but remove is a pain with a persistent data structure if (pwds.get(i) instanceof PwdTableEntry) { if(n==null){ public boolean hasNext() { PwdTableEntry e = (PwdTableEntry) pwds.get(i); throw new IllegalArgumentException(); return (!lst.isEmpty()); if( e.uname.equals(userName) && e.pwd.equals(pwd) ){ } } return e.role; } this.contents = c; public Object next() { data.add(new DbEntry("B", Role.SUPER_SPY, } this.next = n; if(lst instanceof Cons){ "BB"))); ( } } Cons c = (Cons) lst; return null; lst = c.tail(); } public int size() { return c.head(); return 1+next.size(); } } } class Role { throw new NoSuchElementException(); data.add(new DbEntry("C", Role.MOLE, public Object get(int i) { } "CC"))); ( public static final Role SPY = new Role("spy"); if(i < 0){ public static final Role SUPER_SPY = new Role("007"); throw new IllegalArgumentException(); public void remove(){ public static final Role MOLE = new Role("mole"); } throw new UnsupportedOperationException(); public static final Role TECH_GUY = new Role("tech"); if(i==0){ } return this.contents; } data.add(new DbEntry("D", Role.SPY, private final String id; } "DD"))); ( private Role(String id){ return next.get(i−1); public Iterator iterator() { } this.id = id; } return new SimpleArrayListIterator(this); } } //returns null if no such entry } private DbEntry lookup(String key) { public static boolean lteq(Role r, Role s){ public boolean isEmpty() { //I think we want a termination annotation here if (r == null || s == null){ return false; for(int i=0; i < data.size() ; i++){ return false; } if(data.get(i) instanceof DbEntry) { } DbEntry e = (DbEntry) data.get(i); public List removePersistent(int i) { if(e.getKey().equals(key)){ if (s == SUPER_SPY) { if(i < 0){ 2/24 class Client { return e; return true; throw new IllegalArgumentException(); } } } public static String fakeReadingArgs(int i) } if(i==0){ throws java.util.NoSuchElementException { } if (r == MOLE) { return this.next; if(i<42) { return null; return true; } return "bork bork"; } } return next.removePersistent(i−1); } else { } throw new java.util.NoSuchElementException(); public Role readerOf(String key) { return (s == r); } if (lookup(key) != null) { } public void set(int i, Object o) { } return lookup(key).getReader(); if(i < 0){ } } throw new IllegalArgumentException(); public static void main(String args[]){ return null; import java.util.Iterator; } String uname; } import java.util.NoSuchElementException; if(i==0){ String pwd; this.contents = o; String key; public String secretOf(String key) { public class SimpleArrayList implements Iterable { } else { try{ if (lookup(key) != null) { this.next.set(i−1, o); uname = fakeReadingArgs(0); return lookup(key).getData(); Inferred policyList contents; } pwd = fakeReadingArgs(1); } } key = fakeReadingArgs(2); return null; SimpleArrayList() {this.contents = new Nil();} @output} "out"} public Object head() throws NoSuchElementException { catch (Exception e){ } int size() { return contents.size(); } return this.contents; System.out.println( class DbEntry{ boolean isEmpty() {return contents.isEmpty(); } } private String key; 7→ void clear() { this.contents = new Nil(); } "Useage \"java Client \"" ); private Role reader; out if (authCheckOk[0]) public List tail() throws NoSuchElementException { return; private String data; private static abstract class List { return this.next; } private List() { } } public DbEntry(String key, Role reader, String data){ abstract int size(); } Login.init(); this.key = key; thenabstract boolean Reveal isEmpty(); (secret[0+], Role r = Login.doLogin(uname, pwd); this.reader = reader; public abstract Object get(int i); //Zero−based lookup @output "out"this.data = data; public abstract List removePersistent(int i); public Object get(int i) { if (r == null){ } public abstract void set(int i, Object o); return this.contents.get(i); "Login Failed"System.out.println( ); public abstract Object head() throws NoSuchElementException; } return; public String getKey(){ return this.key; } public abstract List tail() throws NoSuchElementException;authCheckOk[1+]) } public Role getReader(){ return this.reader; } } public void add(Object card) { public String getData(){ return this.data; } this.contents = new Cons(card, this.contents); Database db = new Database(); } } @output "out"class Login { private static class Nil extends List { if (authorizedToRead(r, key, db)) { private Nil(){}; public void remove(int i) { db.secretOf(key));System.out.println( private static class PwdTableEntry{ public int size() { return 0; } this.contents = this.contents.removePersistent(i); } else { String uname; public boolean isEmpty() { return true; } } String pwd; public Object get(int i) { throw new IllegalArgumentException(); } Role role; public List removePersistent(int i) { public void set(int i, Object o) { @output "out"private PwdTableEntry(String uname, String pwd, Role role){ throw new IllegalArgumentException(); } this.contents.set(i, o); this.uname=uname; } "Access not authorized");System.out.println( this.pwd=pwd; public void set(int i, Object o) { throw new IllegalArgumentException(); } this.role=role; } public void addAll(SimpleArrayList lst) { } } Iterator i = lst.iterator(); } public Object head() throws NoSuchElementException { while(i.hasNext()){ @inputprivate static boolean authorizedToRead(Role "authCheckOk" r, String key, Database db){ throw new NoSuchElementException(); this.add(i.next()); Role required = db.readerOf(key); private static SimpleArrayList pwds; } } ) (Role.lteq(required,return r)); ( } } public static void init() { public List tail() throws NoSuchElementException { } pwds = new SimpleArrayList(); throw new NoSuchElementException(); private class SimpleArrayListIterator implements Iterator{ class Database { pwds.add(new PwdTableEntry("Bond", "007", Role.SUPER_SPY)); } pwds.add(new PwdTableEntry("M", "0", Role.SPY)); } private List lst; private SimpleArrayList data; pwds.add(new PwdTableEntry("Q", "1337", Role.TECH_GUY )); } private static class Cons extends List{ public SimpleArrayListIterator(SimpleArrayList simpleArrayList) { public Database() { private Object contents; lst = simpleArrayList.contents; @inputdata = new SimpleArrayList(); "secret"public static Role doLogin (String userName, String pwd){ private List next; } data.add(new DbEntry("A", Role.SPY, "AA"))); ( for(int i=0; i < pwds.size(); i++){ private Cons(Object c, List n){ //slow but remove is a pain with a persistent data structure if (pwds.get(i) instanceof PwdTableEntry) { if(n==null){ public boolean hasNext() { PwdTableEntry e = (PwdTableEntry) pwds.get(i); throw new IllegalArgumentException(); return (!lst.isEmpty()); if( e.uname.equals(userName) && e.pwd.equals(pwd) ){ } } return e.role; @input "secret"} this.contents = c; public Object next() { data.add(new DbEntry("B", Role.SUPER_SPY, } this.next = n; if(lst instanceof Cons){ "BB"))); ( } } Cons c = (Cons) lst; return null; lst = c.tail(); } public int size() { return c.head(); return 1+next.size(); } } } @input "secret"class Role { throw new NoSuchElementException(); data.add(new DbEntry("C", Role.MOLE, public Object get(int i) { } "CC"))); ( public static final Role SPY = new Role("spy"); if(i < 0){ public static final Role SUPER_SPY = new Role("007"); throw new IllegalArgumentException(); public void remove(){ public static final Role MOLE = new Role("mole"); } throw new UnsupportedOperationException(); public static final Role TECH_GUY = new Role("tech"); if(i==0){ } @input "secret" return this.contents; } data.add(new DbEntry("D", Role.SPY, private final String id; } "DD"))); ( private Role(String id){ return next.get(i−1); public Iterator iterator() { } this.id = id; } return new SimpleArrayListIterator(this); } } //returns null if no such entry } private DbEntry lookup(String key) { public static boolean lteq(Role r, Role s){ public boolean isEmpty() { //I think we want a termination annotation here if (r == null || s == null){ return false; for(int i=0; i < data.size() ; i++){ return false; } if(data.get(i) instanceof DbEntry) { } DbEntry e = (DbEntry) data.get(i); public List removePersistent(int i) { if(e.getKey().equals(key)){ if (s == SUPER_SPY) { if(i < 0){ 2/24 code

X policy This work

Goal: Workflow of iterative policy refinement via inference

code policy

{X,X}

JIF [Myers et. al. ’03], FlowCaml [Simonet ’03], . . . 3/24 Goal: Workflow of iterative policy refinement via inference

code policy code

{X,X} X

JIF [Myers et. al. ’03], policy FlowCaml [Simonet ’03], . . . This work 3/24 Goal: Analysis suitable for mostly unmodified, legacy Java programs

Low annotation burden Deep analysis of practical Java features: exceptions, heap allocated objects, etc. Security guarantees scale with effort

security

effort

4/24 The central tension:

expressive effective policies analysis

5/24 Outline

1 Policies

2 Inference

3 Implementation and Validation

6/24 Inferred policy

stdout 7→ Reveal( stdin[0] )

Reveal policies describe what information is released

x = @input "stdin" readln(); y = x; println(@output "stdout" y);

7/24 Inferred policy

stdout 7→ Reveal( stdin[0] )

Reveal policies describe what information is released

x = @input "stdin" readln(); y = x; println(@output "stdout" y);

7/24 Reveal policies describe what information is released

x = @input "stdin" readln(); y = x; println(@output "stdout" y);

Inferred policy

stdout 7→ Reveal( stdin[0] )

7/24 Inferred policy

stdout 7→ Reveal( H[1] == null )

H[0] — the recent H input H[1] — the second most recent H input . .

Precise input expressions name inputs and derived data exactly

String read(){ return @input "H" readln(); }

x = read(); y = read(); println(@output "stdout" (x == null));

8/24 Inferred policy

stdout 7→ Reveal( H[1] == null )

H[0] — the most recent H input H[1] — the second most recent H input . .

Precise input expressions name inputs and derived data exactly

String read(){ return @input "H" readln(); }

x = read(); y = read(); println(@output "stdout" (x == null));

8/24 Precise input expressions name inputs and derived data exactly

String read(){ return @input "H" readln(); }

x = read(); y = read(); println(@output "stdout" (x == null));

Inferred policy

stdout 7→ Reveal( H[1] == null )

H[0] — the most recent H input H[1] — the second most recent H input . .

8/24 Inferred policy

out 7→ Reveal( in[0+] )

in[0+] — information from any in input in[1+] — . from the second most recent and older in inputs . .

Imprecise expressions name multiple inputs sharing a label

int i = 0; while (i <= 100) i += parse(@input "in" readln()); } println(@output "out" i);

9/24 Imprecise expressions name multiple inputs sharing a label

int i = 0; while (i <= 100) i += parse(@input "in" readln()); } println(@output "out" i);

Inferred policy

out 7→ Reveal( in[0+] )

in[0+] — information from any in input in[1+] — info. from the second most recent and older in inputs . . 9/24 Conditional policies describe when information is released

secret = @input "secret" ... password = @input "password" ... guess = @input "guess" ...

if (guess == password) { println(@output "stdout" secret); }

Policy?

stdout 7→ Reveal( secret[0], guess[0], password[0] )

10/24

Inferred policy

stdout 7→ if ( guess[0] == password[0] ) then Reveal( secret[0] ) Conditional policies describe when information is released

secret = @input "secret" ... password = @input "password" ... guess = @input "guess" ...

if (guess == password) { println(@output "stdout" secret); }

Policy?Inferred policy

stdout 7→ Revealif ( guess[0]( secret[0], == password[0] guess[0], password[0] ) then ) Reveal( secret[0] )

10/24 Inferred policy

out 7→ if-executed safe_release(String) then Reveal(in[0])

Track policies describe where information is released String safe_release(String x) @track { if (/∗ auth check ∗/) { return x; } else { return ""; } } x = @input "in" readln(); y = safe_release(x); println(@output "out" y);

11/24 Inferred policy

out 7→ if-executed safe_release(String) then Reveal(in[0])

Track policies describe where information is released String safe_release(String x) @track { if (/∗ auth check ∗/) { return x; } else { return ""; } } x = @input "in" readln(); y = safe_release(x); println(@output "out" y);

11/24 Track policies describe where information is released String safe_release(String x) @track { if (/∗ auth check ∗/) { return x; } else { return ""; } } x = @input "in" readln(); y = safe_release(x); println(@output "out" y);

Inferred policy

out 7→ if-executed safe_release(String) then Reveal(in[0])

11/24 Inference

12/24 (pc1, Γ1)

(pc2, Γ2) (pc3, Γ3)

(pc4, Γ4)

. . ⇒

if (x < y)

x = x + 1;

z = x; X Natural handling of strong updates. . X Convenient for calculating non-syntactic . control dependencies.

Inference algorithm is based on dataflow analysis

if (x < y){ x = x + 1; } z = x;

13/24 (pc1, Γ1)

(pc2, Γ2) (pc3, Γ3)

X Natural handling of strong updates. X Convenient for calculating non-syntactic (pc4, Γ4) control dependencies.

Inference algorithm is based on dataflow analysis

if (x < y){ . x = x + 1; . } ⇒ z = x; if (x < y)

x = x + 1;

z = x;

. .

13/24 X Natural handling of strong updates. X Convenient for calculating non-syntactic control dependencies.

Inference algorithm is based on dataflow analysis

if (x < y){ . x = x + 1; . } ⇒ (pc1, Γ1) z = x; if (x < y)

(pc2, Γ2) (pc3, Γ3)

x = x + 1;

z = x;

. (pc , Γ ) . 4 4

13/24 Inference algorithm is based on dataflow analysis

if (x < y){ . x = x + 1; . } ⇒ (pc1, Γ1) z = x; if (x < y)

(pc2, Γ2) (pc3, Γ3)

x = x + 1;

z = x; X Natural handling of strong updates. . (pc , Γ ) X Convenient for calculating non-syntactic . 4 4 control dependencies. 13/24 Contexts (pc, Γ) approximates information flow

Location context: information content of fields or heap locations.

Γ: Location * Policy Program counter: what each branch taken to a program point reveals.

pc : BranchId * Policy ∪ PIE

Location = abstract vars and refs PIE = precise input exps. BranchId = static branch ids Policy = policies

14/24 1 Calculate explicit flows. explicit = Γ(x)

2 Calculate implicit, imprecise flows. G implicit = (range(pc) ∩ Policy) (pc, Γ0) 3 Calculate dominating precise input exps.

{d1, d2,..., dn} = range(pc) ∩ PIE

4 Assemble a policy.

pol = if d1&& d2 && .... dn then (explicit t implicit)

5 Update the dataflow . Γ0 = Γ[ z 7→ pol ]

Example: Updating a context for an assignment node

(pc, Γ)

z = x;

15/24 2 Calculate implicit, imprecise flows. G implicit = (range(pc) ∩ Policy) (pc, Γ0) 3 Calculate dominating precise input exps.

{d1, d2,..., dn} = range(pc) ∩ PIE

4 Assemble a policy.

pol = if d1&& d2 && .... dn then (explicit t implicit)

5 Update the dataflow graph. Γ0 = Γ[ z 7→ pol ]

Example: Updating a context for an assignment node

1 Calculate explicit flows. (pc, Γ) explicit = Γ(x)

z = x;

15/24 (pc, Γ0) 3 Calculate dominating precise input exps.

{d1, d2,..., dn} = range(pc) ∩ PIE

4 Assemble a policy.

pol = if d1&& d2 && .... dn then (explicit t implicit)

5 Update the dataflow graph. Γ0 = Γ[ z 7→ pol ]

Example: Updating a context for an assignment node

1 Calculate explicit flows. (pc, Γ) explicit = Γ(x)

z = x; 2 Calculate implicit, imprecise flows. G implicit = (range(pc) ∩ Policy)

15/24 (pc, Γ0)

4 Assemble a policy.

pol = if d1&& d2 && .... dn then (explicit t implicit)

5 Update the dataflow graph. Γ0 = Γ[ z 7→ pol ]

Example: Updating a context for an assignment node

1 Calculate explicit flows. (pc, Γ) explicit = Γ(x)

z = x; 2 Calculate implicit, imprecise flows. G implicit = (range(pc) ∩ Policy)

3 Calculate dominating precise input exps.

{d1, d2,..., dn} = range(pc) ∩ PIE

15/24 (pc, Γ0)

5 Update the dataflow graph. Γ0 = Γ[ z 7→ pol ]

Example: Updating a context for an assignment node

1 Calculate explicit flows. (pc, Γ) explicit = Γ(x)

z = x; 2 Calculate implicit, imprecise flows. G implicit = (range(pc) ∩ Policy)

3 Calculate dominating precise input exps.

{d1, d2,..., dn} = range(pc) ∩ PIE

4 Assemble a policy.

pol = if d1&& d2 && .... dn then (explicit t implicit)

15/24 Example: Updating a context for an assignment node

1 Calculate explicit flows. (pc, Γ) explicit = Γ(x)

z = x; 2 Calculate implicit, imprecise flows. G implicit = (range(pc) ∩ Policy) (pc, Γ0) 3 Calculate dominating precise input exps.

{d1, d2,..., dn} = range(pc) ∩ PIE

4 Assemble a policy.

pol = if d1&& d2 && .... dn then (explicit t implicit)

5 Update the dataflow graph. 0 Γ = Γ[ z 7→ pol ] 15/24 Implementation and Validation

16/24 We prototyped an implementation that analyses Java 1.4 programs

Implementation (35,100 lines of Java code) extends the Polyglot compiler framework. [Nystrom, Clarkson, & Myers ’03] Program analyses refine security inference: Object-sensitive pointer analysis reasons precisely about non-aliased objects, preventing label creep. Post dominance analysis determines identifies where pc levels may be safely lowered. Precise and exception analysis simplifies control flow graph by ruling out spurious exceptions (e.g. from casts that always succeed).

17/24 Case studies: successful inference of interesting information flows

Computer Inputs sanitized before display. psychiatrist

Interactive password Passwords only revealed via (simulated) manager cryptography.

Access-controlled Information release governed by reference key-value store monitor.

Go Fish The computer doesn’t cheat.

Battleship game The computer doesn’t cheat. [Jif]

Private keys influence outputs appropriately Mental poker . [Askarov & Sabelfeld ’05] 18/24 Computer player, p2 Human player, p1 @input "p1 board"

p2 query 7→ if (p2 query ok[0]) then Reveal(p2 query ok[1+], p2 hit p1?[0+], p1 hit p2?[0+])

Computer (p2) player’s queries only depend on public data.

Sample policy: Battleship

private PwdTableEntry(String uname,public String static pwd, final Role Role role){ MOLE = new Role("mole"); return lookup(key).getReader(); this.uname=uname; public static final Role TECH_GUY = new Role("tech"); return lookup(key).getReader(); int size() { return contents.size(); } } this.pwd=pwd; } boolean isEmpty() {return contents.isEmpty(); } return null; this.role=role; private final String id; return null; void clear() { this.contents = new Nil(); } } } private Role(String id){ } } this.id = id; private static abstract class List { public String secretOf(String key) { } public String secretOf(String key) { private List() { } if (lookup(key) != null) { private static SimpleArrayList pwds; if (lookup(key) != null) { abstract int size(); return lookup(key).getData(); public static boolean lteq(Role r, Role s){ return lookup(key).getData(); abstract boolean isEmpty(); } public static void init() { if (r == null || s == null){ } public abstract Object get(int i); return null; pwds = new SimpleArrayList(); return false; return null; public abstract List removePersistent(int i); } pwds.add(new PwdTableEntry("Bond",} "007", Role.SUPER_SPY)); } public abstract void set(int i, Object o); } pwds.add(new PwdTableEntry("M", "0", Role.SPY)); } public abstract Object head() throws NoSuchElementException; class DbEntry{ pwds.add(new PwdTableEntry("Q", "1337",if (s == Role.TECH_GUY SUPER_SPY) { )); class DbEntry{ public abstract List tail() throws NoSuchElementException; private String key; } return true; private String key; } private Role reader; } private Role reader; private String data; public static Role doLogin (String userName, String pwd){ private String data; if (r == MOLE) { private static class Nil extends List { public DbEntry(String key, Role reader,for(int String i=0; data){ i < pwds.size(); i++){ return true; public DbEntry(String key, Role reader, String data){ private Nil(){}; this.key = key; if (pwds.get(i) instanceof PwdTableEntry)} { this.key = key; public int size() { return 0; } this.reader = reader; PwdTableEntry e = (PwdTableEntry) pwds.get(i); this.reader = reader; public boolean isEmpty() { return true; } this.data = data; if( e.uname.equals(userName) &&return e.pwd.equals(pwd) (s == r); ){ this.data = data; public Object get(int i) { throw new IllegalArgumentException(); } } return e.role; } } public List removePersistent(int i) { } throw new IllegalArgumentException(); } public String getKey(){ return this.key; } } } public String getKey(){ return this.key; } public Role getReader(){ return this.reader;} } import java.util.Iterator; public Role getReader(){ return this.reader; } public void set(int i, Object o) { throw new IllegalArgumentException(); public String getData(){ return this.data;return } null; import java.util.NoSuchElementException; public String getData(){ return this.data; } } } } } class Login { public class SimpleArrayList implements Iterable { class Login { public Object head() throws NoSuchElementException { } throw new NoSuchElementException(); private static class PwdTableEntry{ class Role { List contents; private static class PwdTableEntry{ } String uname; String uname; public static final Role SPY = new Role("spy");SimpleArrayList() {this.contents = new Nil();}

19/24 p2 query 7→ if (p2 query ok[0]) then Reveal(p2 query ok[1+], p2 hit p1?[0+], p1 hit p2?[0+])

Computer (p2) player’s queries only depend on public data.

Sample policy: Battleship

private PwdTableEntry(String uname,public String static pwd, final Role Role role){ MOLE = new Role("mole"); return lookup(key).getReader(); this.uname=uname; public static final Role TECH_GUY = new Role("tech"); return lookup(key).getReader(); int size() { return contents.size(); } } this.pwd=pwd; } boolean isEmpty() {return contents.isEmpty(); } return null; this.role=role; private final String id; return null; void clear() { this.contents = new Nil(); } } } private Role(String id){ } } this.id = id; private static abstract class List { public String secretOf(String key) { } public String secretOf(String key) { private List() { } if (lookup(key) != null) { private static SimpleArrayList pwds; if (lookup(key) != null) { abstract int size(); return lookup(key).getData(); public static boolean lteq(Role r, Role s){ return lookup(key).getData(); abstract boolean isEmpty(); } public static void init() { if (r == null || s == null){ } public abstract Object get(int i); return null; pwds = new SimpleArrayList(); return false; return null; public abstract List removePersistent(int i); } pwds.add(new PwdTableEntry("Bond",} "007", Role.SUPER_SPY)); } public abstract void set(int i, Object o); } pwds.add(new PwdTableEntry("M", "0", Role.SPY)); } public abstract Object head() throws NoSuchElementException; class DbEntry{ pwds.add(new PwdTableEntry("Q", "1337",if (s == Role.TECH_GUY SUPER_SPY) { )); class DbEntry{ public abstract List tail() throws NoSuchElementException; private String key; } return true; private String key; } private Role reader; } private Role reader; private String data; public static Role doLogin (String userName, String pwd){ Computer private String data; if (r == MOLE) { private static class Nil extends List { public DbEntry(String key, Role reader,for(int String i=0; data){ i < pwds.size(); i++){ return true; public DbEntry(String key, Role reader, String data){ private Nil(){}; this.key = key; if (pwds.get(i) instanceof PwdTableEntry)} { player, p2 this.key = key; public int size() { return 0; } this.reader = reader; PwdTableEntry e = (PwdTableEntry) pwds.get(i); this.reader = reader; public boolean isEmpty() { return true; } this.data = data; if( e.uname.equals(userName) &&return e.pwd.equals(pwd) (s == r); ){ this.data = data; public Object get(int i) { throw new IllegalArgumentException(); } } Human return e.role; } } public List removePersistent(int i) { } throw new IllegalArgumentException(); } public String getKey(){ return this.key; } } } public String getKey(){ return this.key; } public Roleplayer, getReader(){ return p1 this.reader;} } import java.util.Iterator; public Role getReader(){ return this.reader; } public void set(int i, Object o) { throw new IllegalArgumentException(); public String getData(){ return this.data;return } null; import java.util.NoSuchElementException; public String getData(){ return this.data; } @input "p1 board" } } } } class Login { public class SimpleArrayList implements Iterable { class Login { public Object head() throws NoSuchElementException { } throw new NoSuchElementException(); private static class PwdTableEntry{ class Role { List contents; private static class PwdTableEntry{ } String uname; String uname; public static final Role SPY = new Role("spy");SimpleArrayList() {this.contents = new Nil();}

19/24 Sample policy: Battleship

private PwdTableEntry(String uname,public String static pwd, final Role Role role){ MOLE = new Role("mole"); return lookup(key).getReader(); this.uname=uname; public static final Role TECH_GUY = new Role("tech"); return lookup(key).getReader(); int size() { return contents.size(); } } this.pwd=pwd; } boolean isEmpty() {return contents.isEmpty(); } return null; this.role=role; private final String id; return null; void clear() { this.contents = new Nil(); } } } private Role(String id){ } } this.id = id; private static abstract class List { public String secretOf(String key) { } public String secretOf(String key) { private List() { } if (lookup(key) != null) { private static SimpleArrayList pwds; if (lookup(key) != null) { abstract int size(); return lookup(key).getData(); public static boolean lteq(Role r, Role s){ return lookup(key).getData(); abstract boolean isEmpty(); } public static void init() { if (r == null || s == null){ } public abstract Object get(int i); return null; pwds = new SimpleArrayList(); return false; return null; public abstract List removePersistent(int i); } pwds.add(new PwdTableEntry("Bond",} "007", Role.SUPER_SPY)); } public abstract void set(int i, Object o); } pwds.add(new PwdTableEntry("M", "0", Role.SPY)); } public abstract Object head() throws NoSuchElementException; class DbEntry{ pwds.add(new PwdTableEntry("Q", "1337",if (s == Role.TECH_GUY SUPER_SPY) { )); class DbEntry{ public abstract List tail() throws NoSuchElementException; private String key; } return true; private String key; } private Role reader; } private Role reader; private String data; public static Role doLogin (String userName, String pwd){ Computer private String data; if (r == MOLE) { private static class Nil extends List { public DbEntry(String key, Role reader,for(int String i=0; data){ i < pwds.size(); i++){ return true; public DbEntry(String key, Role reader, String data){ private Nil(){}; this.key = key; if (pwds.get(i) instanceof PwdTableEntry)} { player, p2 this.key = key; public int size() { return 0; } this.reader = reader; PwdTableEntry e = (PwdTableEntry) pwds.get(i); this.reader = reader; public boolean isEmpty() { return true; } this.data = data; if( e.uname.equals(userName) &&return e.pwd.equals(pwd) (s == r); ){ this.data = data; public Object get(int i) { throw new IllegalArgumentException(); } } Human return e.role; } } public List removePersistent(int i) { } throw new IllegalArgumentException(); } public String getKey(){ return this.key; } } } public String getKey(){ return this.key; } public Roleplayer, getReader(){ return p1 this.reader;} } import java.util.Iterator; public Role getReader(){ return this.reader; } public void set(int i, Object o) { throw new IllegalArgumentException(); public String getData(){ return this.data;return } null; import java.util.NoSuchElementException; public String getData(){ return this.data; } @input "p1 board" } } } } class Login { public class SimpleArrayList implements Iterable { class Login { public Object head() throws NoSuchElementException { } throw new NoSuchElementException(); private static class PwdTableEntry{ class Role { List contents; private static class PwdTableEntry{ } String uname; String uname; public static final Role SPY = new Role("spy");SimpleArrayList() {this.contents = new Nil();}

p2 query 7→ if (p2 query ok[0]) then Reveal(p2 query ok[1+], p2 hit p1?[0+], p1 hit p2?[0+])

Computer (p2) player’s queries only depend on public data.19/24 Sample policy: Computer psychiatrist

display 7→ if-executed (Liz.sanitize(java.lang.String)) then Reveal(input[0], input[1+])

Inputs are not displayed unless sanitize has been called.

20/24 The prototype scales to low 1000s loc, but is not performance tuned

!"##$%

01#23$456)748#)9:;)<46#:)5=)>5?#

/++

.++

A -++ : ? 6 5

2 *++ # : @ ) #

8 ,++ 4 7 %++

+ %+ %++ %+++ %++++ <46#:)5=)25?#

C665$'$456:)9:;)<46#:)5=)>5?# 21/24 ,+ %B %/ : 6 5 4 %- $ ' $

5 %, 6 6

C %+ ) D # B 8 8

' / D ( 5

D - & , + %++ %+++ %++++ <46#:)5=)25?#

I6=#D#62#)C665$'$456:)9:;)JIK)7LG#)C665$'$456: &'(#)*

%++++

%+++

: C665$'$456: 6 5 4 $ %++ J4=)C665$'$456: ' $ 5 6 6 C %+

% E'$$F#:"4G &5H#D !"##$%

01#23$456)748#)9:;)<46#:)5=)>5?#

/++

.++

A -++ : ? 6 5

2 *++ # : @ ) #

8 ,++ 4 7 %++

We succeeded+ in requiring few %+ %++ %+++ %++++ programmer annotations<46#:)5=)25?#

C665$'$456:)9:;)<46#:)5=)>5?#

,+ %B %/ : 6 5 4 %- $ ' $

5 %, 6 6

C %+ ) D # B 8 8

' / D ( 5

D - & , + %++ %+++ %++++ <46#:)5=)25?#

22/24

I6=#D#62#)C665$'$456:)9:;)JIK)7LG#)C665$'$456: &'(#)*

%++++

%+++

: C665$'$456: 6 5 4 $ %++ J4=)C665$'$456: ' $ 5 6 6 C %+

% E'$$F#:"4G &5H#D Conclusion

23/24 Policy inference via dataflow is a promising info. flow analysis

This project demonstrates:

A novel workflow of iterative policy refinement via inference Expressive policies that explain information flows and reflect program structure A precise analysis suitable for (mostly) unmodified Java programs

24/24 Bonus Slides Policies express how information flows from inputs to outputs

where

what

when

[Sabelfeld & Sands ’05] NB. Our policies generalize delimited release [Askarov & Sabelfeld ’07] Annotation burden compares well with explicit systems

Jif primary goals: modular typing, DLM policies, and not inference. [Myers, Zheng, Zdancewic, Chong, Nystrom ’01–’09] Annotation burden compares well with more explicit systems

!"##$%

456#2#57#)8550$'$.05-)9-:);4<)=>/#)8550$'$.05-

%3333

%333

- 8550$'$.05- 5 0 . $ %33 ;.6)8550$'$.05- ' $ 0 5 5 8 %3

% +'$$,#-"./ &01#2

Jif primary goals: modular typing, DLM policies, and not inference. [Myers, Zheng, Zdancewic, Chong, Nystrom ’01–’09]

&'(#)* Goal: Policies that explain and reflect information flows

Inferred policy

out 7→ if (authCheckOk[0]) then Reveal(secret[0+], authCheckOk[1+])

Policies...... reflect structure of information flow within program . . . describe flow in terms of meaningful channel names Policy rewriting is needed for widening and improves readability

Example

Reveal(H[j],H[i+]) = Reveal[i+] (provided i ≤ j) Reveal(H[j],H[i+]) Reveal(H[min(i, j)+]) v Reveal(H[0+])

Widening ∇(p1, p2): Let p := p1 and p2 If |p| ≤ threshold return p Rewrite (=) until |p| ≤ threshold or no further rewrites possible Rewrite conservatively (v) until |p| ≤ threshold Result := p