Policies - control flow safety - memory safety - stack safety - type safety * Q: Are these sufficient for mobile code security? Safe languages Reminders on Java -- applets and applications -- object-oriented, garbage-collected, type-safe language -- no pointers!! -- classes, objects, packages -- threads -- native code -- type-safety -- all casts are explicit -- private, protected, package scope, public, synchronized, final (methods & classes), try{}finally{} -- subclassing, interfaces, rules for constructors Java bytecode (JVML) -- a little different from the Java source language -- expansion of syntactic sugar -- opcodes for an imaginary ("virtual") machine with a stack & some registers -- subroutines for try/finally and nested exception handlers Architecture for type-safety -- Compiler totally untrusted -- A mixture of static and dynamic type-checking -- TCB: Verifier, JVM, SecurityManager, ClassLoader, libraries -- Bytecode verifier -- simple checks -- dataflow analysis verifies: -- at any given point in code, -- stack is always same size & has same type -- registers not accessed until loaded with correct type -- methods typecheck -- field accesses typecheck -- opcodes have appropriate type arguments on the stack -- instructions are used atomically (i.e. control flow instructions point to the start of an instruction) -- at any "join" in control-flow, merge the type of the world (if possible; otherwise fail) -- note that at any instruction, every registered exception handler is a possible successor (except that you push an exception object on the stack before the jump to the exception handler) -- i.e. registers are not polymorphic -- e.g. the following code will fail, due to static type-checking: Object obj; if (flag == 0) obj = new File(); else obj = new String(); if (flag == 0) println(obj.getPath()); else println(obj); -- Q: would the above technique have worked for x86 assembly? A: no: variable-length instructions, registers are untyped, stack discipline not followed, untyped load/stores -- JVM -- interpreter for JVML -- Dynamic type-checking on first access to every class, method, field -- array bounds-checking and storage type-safety (give an example of why the latter has to be dynamically: covariance) -- Garbage collector has to be secure -- if it deallocates memory while there's still a pointer to it, can subvert type system -- if it doesn't zero out memory, you have object re-use problems -- ClassLoader -- dynamic loading of classes The remainder of the security architecture: how to use type-safety -- SecurityManager -- Libraries Evaluation -- Does not address denial-of-service attacks! -- package mechanism is poorly designed -- should be hierarchical -- and note that any class can add itself to a package just by adding an appropriate "package" line -- problems with complete mediation with SecurityManager -- use of bytecodes was poorly considered; ASTs would have been better -- subroutines hard to verify due to polymorphism, could have been left out of JVML at no loss of generality and very little cost -- JVML is more general than Java, which has caused some non-obvious bugs -- implementation problems, which we'll see in a future class -- ``Java Security'' paper -- A very early, influential paper; it talks about JDK1.0/1.1. -- Namespaces -- A different naming environment for each applet, except that they share a common "system" name space; "system" namespace always searched first -- Classloaders define the namespace: File classloader + network classloader; when classes are loaded, they are tagged with which classloader they came from; this identifies whether they get the "applet" security policy or the "application" security policy -- Packages: flat namespace, anyone may join a package; but decisions on "protected" access etc. are made using the pair (package name, type of classloader) -- except... -- defeating the namespace (e.g. with a rogue classloader) lets you do type confusion (because implicit "sharing constraints" are violated); an example was given earlier -- problem is there's not a very strong tie between class names and Class objects -- Denial of service -- spinloop (and threads may not be preemptive!) -- while(1)malloc(); -- acquire a lock on other shared code (deadly to Hotjava, other applets that you can name) -- can kill other threads (deadly to other applets that you can name) -- denial of service not always easy to detect, because it might merely cause degradation rather than a total lock-up -- also, it can be hard to assign blame (might be time-delayed) -- cute example: a hostile applet by Microsoft which sits innocently in the background, until you download an applet from one of its competitors; then it starts eating up system resources and occasionally crashes the JVM, in an effort to make you think that the competitor's applet is unstable. If done right, you might never use the competitor's applet again; you might even decide never to buy anything from that competitor again ("if they can't even write an applet that works, how are they going to write an application?") -- Q: how do you fix this? -- A: hard to fix; not a design goal -- Covert channels -- can leak data via DNS queries -- Q: how do you fix this? Implementation weaknesses -- problems with complete mediation with SecurityManager (stack introspection helps) -- Trusted path woes: (aka ``slash and burn'' attack) -- JVM looks for class java.net.Socket in java/net/Socket.class -- but they forgot to prohibit '/' in a package name... -- why is this a problem? (applet can run /tmp/netscape/cache/foo.class as trusted "application" code by running the foo class from /tmp/netscape/cache package, since anything loaded from file is marked with file classloader and thus trusted) -- similar example of this problem with Eudora, MSIE, etc.: email someone a Java applet; it gets saved on disk in a tmp file; then Eudora calls the Windoze HTML renderer library; Eudora forgets to turn off Java; renderer reads the Java stuff from the file, loads it via file classloader, and thus marks it as trusted; boom -- Classloader attack -- was this clear? (if not, explain it) -- constructed a rogue ClassLoader via class CL extends ClassLoader { CL() { try { super(); } catch (Exception e) {} } } -- note that the above is illegal in Java source, but legal in JVML -- then you have complete control of the namespace, boom, done (because code loaded by classloader L asks L to load any further classes its needs; same classname can refer to two different classes in two different namespaces, which lets you pass instances of one class from first namespace into second namespace and use it in the second namespace as though it were an instance of the second class) Language weaknesses -- JVML is more general than Java, which has caused some non-obvious bugs -- methods can be called on partially initialized objects (by calling them from within a constructor) -- and object creation separated from initialization -- ASTs would have been better than bytecodes, for ease of parsing -- no formal semantics -- jsr adds to complication a LOT (hard to verify, due to polymorphism) -- and is unnecessary (could have been left out of JVML at no loss of generality and very little cost) -- Array woes: -- name of class for array of A is 'A[]' -- in some cases it is possible to declare a class with name 'A[]' -- this is supposed to get erased later -- but one version of JDK didn't quite erase everything it was supposed to, and some remnants were left -- boom, type confusion -- Return by reference -- At one point, it was possible to change Hotjava's ( or JDK1.1.1's) idea of who signed your applet -- you call getSigners(). it returns an array of the principals who signed the current applet. BY REFERENCE ! boom -- is it obvious why this is bad? -- there's a converse, too: if user passes you a reference, clone it before storing it in your internal data structures -- Serialization woes -- Public/private keys stored in an identity database -- Type system ('protected' methods) used to protect who can read the sensitive information in there (e.g., the private keys) -- But it lives in a serializable class -> can serialize it and send the serialized object to another host, who then reads the private keys out of the database -- Problem: Serialization methods didn't do any access checks -- package mechanism is poorly designed -- should be hierarchical (nested packages) -- very weak: any class can add itself to a package just by adding an appropriate "package" line -- even if the other stuff in the package is all signed, and the "cuckoo bird" isn't signed -- this caused a real bug in August 1996 in MSIE: Microsoft chose to prevent folks from joining a crucial system package by using SecurityManager checks, but they screwed up -- Q: how do you fix this? -- A: it would be safer if packages could declare in one place who their members are, but Java doesn't allow for that -- In practice, SecurityManager and Classloader prevent network classes from joining packages java.* or sun.* -- Another real bug: Java SSL implementation implemented under crysec.* package; but any hostile applet could declare classes in these packages and gain access to private crypto keys -- Policies -- bad security policies -- Hotjava: read .mailcap, public_html (privacy) -- Hotjava on Windoze95: write to C:\TEMP (disrupt other programs; virii) -- Netscape: no file access -- Hotjava: System.getenv() not protected -- time not protected, so possible to benchmark a system, and potentially deduce what kind of box it is (similarly, can learn how much memory it has, can potentially learn how much disk space by writing to C:\TEMP until it overflows, can learn something about OS and browser by asking browser to open a new web page, can learn something from whether threads are preemptive or not, etc.) -- they just forgot... -- algorithm for checking whether an applet from host H can connect to host J: -- let IP(h) be the set of IP addresses for host h -- then, if IP(H) \intersect IP(J) is non-empty, allow the connect -- what's wrong with this? (attacker can choose IP(H)) -- why should we care? (applets running behind a firewall can attack all other hosts behind the firewall; this shows that Java's security model doesn't compose well with the firewall security model-- Java and firewalls don't play together, because firewalls assume everything inside is trusted, and the whole point of Java is to run untrusted stuff inside) -- Discussion pt: How do you fix the firewalls stuff? (one solution: run Java on a playground machine *outside* the firewall, then send X events in through firewall) -- Hotjava: can change the FTP and HTTP proxies (!) -- why is this bad? (can play MITM games) -- what went wrong? (browser state was in public variables in public classes; Hotjava relied on Java type system for isolation from applets, which means every class has to have every field and every method declared properly -- forgetting even one is deadly) -- Discussion pt: Netscape coded in C++, with an explicit JVM; HotJava purely in Java; which was a better design strategy, and why? -- Interactions between applets: -- paper suggests "non-interference" property (roughly, no applet can tell whether there are even any other applets running, let alone what they might be doing; thus, no applet can affect any other applets execution) -- Discussion pt: is this a good idea? (very hard to do: covert channels, timing, shared state, etc.) -- Lots of interesting user interface problems -- Architecture weaknesses -- #1 problem: TCB is huge (what's in it?) SFI generalizes to arbitrary injection of enforcement mechanism Q: Who injects the enforcement mechanism? - One possibility: Code consumer (e.g., web client) does the injection; but then work might be repeated many times, and also might consumer has no access to types and other high-level information about code - Another possibility: Code producer (e.g., compiler) does injection; but then the consumer must verify that injection was done properly Proof-carrying code - Code producer injects enforcement mechanism, and then supplies a proof to consumer that the result satisfies the consumer's policy Consumer->Producer: policy Producer->Consumer: safe code, proof