Goal: prevent private data from becoming public.
In class we saw discretionary and mandatory access control.
Focus on confidentiality, not integrity (harder).
For simplicity, think in terms of variables:
h
for private ("high" security) variables.l
for public ("low" security) variables.Problem: Assign private expressions to public variables.
E.g.
l := h + 1
Problem: Use private variables to affect control flow, which in turn affects public variables.
E.g.
l := False
if h {
l := True
}
// l == h here!
Implicit flows are covert channels.
Consider:
while h {
;
}
Secure for attackers that see only public variables.
Not secure for attackers that see termination behavior.
Security of a program depends on what attacker can observe!
Recall explicit flow:
l := h + 1
In what sense is h + 1
a "private expression"?
Answer: confidentiality level of an expression is the least upper bound (lub
) of the levels of its parts.
E.g. h
is high, 1
is low, so h + 1
is high == lub(low,high).
E.g. more levels, non linear:
H H HAN
| | / | \
| | HA | HN
| | | MAN |
| | | / | \ |
| vs M vs MA | MN
| | | LAN |
| | | / | \ |
| | LA | LN
| | \ | /
| | \|/
L L P
Where A
is army, N
is navy.
E.g., HAN
is least upper bound of HN
and LA
.
A little better than (discretionary or mandatory) access control.
Monitor program as it runs.
Fail if security violation detected.
Now we think of explicit returns, not just confidentiality levels of variables.
E.g. OK:
h := l + 1
l := l + 2
h := h + l
E.g. not OK:
l := False
if h {
l := True // Reject this only if execution gets here!
}
low return l // Unsafe when h == False!
Track security level of guards so far.
Reject returning expression at lower level.
E.g.
Code Security context
---- ----------------
l := 1 // L
if m { // M
... // M
} // M
... // M
if h { // H
... // H
} // H
... // H
Security context grows with the guards!
Goal: guarantee program is secure for all possible inputs.
Bonus: no scope creep!
Private input does not affect ("interfere with") public output.
A program p
is non-interfering iff
ls = (l1,l2,...)
is public input,hs1
and hs2
are private input,implies
low (p ls hs1) == low (p ls hs2),
where low e
is the public output of e
.
This is a precise definition of info-flow security.
Check program security at compile time!
Compositional (cf. public key)!
Assume all variables are numbers for simplicity.
Notation:
expression : level
means all variables in expression
are at or below level
.
[level] |- statement
means all assignments in statement
are safe for level
.
An assignment v := e
is "safe" for level
if v is at or above level
and e
is at or below level
(i.e. e : level
).
Here [level]
is the security context.
E.g. OK:
l + 1 : low
l + 1 : high
h + l : high
[low] : l := l + 1
[low] : h := h + l
[high] : h := h + 1
E.g. not OK:
h + l : low // l is OK, h is not.
[low] : l := h // l is OK, h is not.
[high] : l1 := l2 + 1 // l2 is OK, l1 is not.
E.g. if
statement is safe if guard e
and body b
check at same level:
e : low [low] |- b e : high [high] |- b
--------------------- -----------------------
[low] |- if e { b } [high] |- if e { b }
Don't worry if you haven't seen type systems before.
If a program type checks then it's non-interfering!
Subsumption rule avoids scope creep!
Overly restrictive.
Precludes password checking:
if l_supplied_pw == h_stored_pw {
l_authenticated := True
}
and transmission of encrypted data:
l_ciphertext := encrypt (h_key, h_plaintext)
!
Conclusion: need an escape hatch for practical programming (cf. casting in Java).
For more info on everything here, see:
Sabelfeld and Myers, 2003. Language-Based Information-Flow Security.
Volpano, Irvine, and Smith, 1996. A Sound Type System for Secure Flow Analysis.
Denning, 1976. A Lattice Model of Secure Information Flow.