Guide to Defensive programming
(this is work in progress)
How to write code that is secure in the SecureSqueak environment.
There are several types of security leaks possible:
- Private objects should not be returned from a method or block invocation or passed in parameters to another untrusted object.
- Method invocations on untrusted objects may never return or may raise exceptions.
- Some methods of a class might be private. These must not be callable from outside that class.
General Tips
The first thumb-rule is to make sure you're aware of which objects are your private ones and which objects have a publicly exposed interface.,
The classes of private objects should generally be kept in a private namespace. The public interface to your system of objects should never have any unguarded methods which could return a private object.
Keep in mind that private objects are mutable. If an attacker gets a reference to a private object with accesser methods, they could potentially modify the state of your object system to their advantage.
Code all publically accessable methods very defensively. Any arguments passed to publically available methods are untrusted objects.
When dealing with an untrusted object:
- Ideally, verify its identity exactly.
- Otherwise, verify its implementation by making sure it is an instance of a particular class.
- If neither is possible, guard every method invocation on that untrusted object with timeouts. There is no guarantee that method invocations ("message sends") on an untrusted object will ever return.
- Be careful with the references you pass to an untrusted object. Do not pass anything which confidential state. Remember that most private objects are mutable, and if you pass a reference to an untrusted object, that untrusted object could modify those objects.
- Don't trust values from an untrusted object. Always verify their type: if you expect an Integer, make sure it really is an Integer or SmallInteger.
Be careful with blocks. When you pass a block to another object (e.g. Collection>>do:), the return value of that block is available to that other object. The return value of a block is the value of the last statement executed. Get in to the habit of putting a nil at the end of your blocks, e.g. myCollection do: [ :each | each doSomething. nil. ]. In some cases, returning "self" from a block is not a good idea, especially if "self" is a private object.
Equally so, don't return private objects from public methods.
Managing blocks
Blocks might be secure: having a reference to a block only lets the reciever evaluate that block and does not give access to private variables in that block's context (currently in non-closure Squeak, this also includes local variables in that block's containing MethodContext).
Evaluating a block from an untrusted source does not give that block access to local vars, but the invocation may stall indefinitely. A block might contain a non-local return, meaning that executing that block will cause your method to halt abruptly as execution continues from that block's originating context.
For example:
b := [ ^ Transcript show: 'block evaluated'; cr].
If any method executes the above block, that method will be forcedly returned from and execution will continue in the method where that block was defined. If that method context no longer exists, the block will be send #cannotReturn:
b := [ ^ Transcript show: 'block evaluated'; cr].
[ b value ] fork. " Will pop up a debugger complaining that the block cannot be returned from "
The result of the evaluation of the last statement in a block is returned when the block is evaluated. This is a possible security leak. Perhaps the compiler should warn the user or some special syntax should be used? "[ someThing someValue returnFromBlock ].", cf: "[someThing someValue. nil.]". This only matters if the block is accessed outside the current method. (Alternatively, the IDE could issue a warning).
To summarise secure programming using blocks:
- An untrusted method evaluating a block has access to the return value of that block, which is the result of the last statement in that block.
- An untrusted block has access to the arguments passed in to it.
- An untrusted block can stall or non-locally return denying the evaluator the ability to continue execution. An untrusted block can throw an exception which has the same effect.
Method return values
Be wary of what you return from methods. Returning "self" is normally secure and is the default behaviour. However, if an interface object is being used, any method that returns "self" in the secured object should mean that the interface returns itself rather than the secured object.
Stalled invocations
An invocation on an untrusted object may possibly stall. This is a security risk as it prevents the rest of your code from running.
A simple single threaded solution would be to implement a block with a timeout:
a := [ untrustedObject doSomething ] ifStalled: [ ... ].
a := [ untrustedObject doSomething ] ifStalled: [ ... ] ifError: [ :e :r | ... ].
a := [ untrustedObject doSomething ] ifStalledOrError: [ :e :r |... ].
A multithreaded solution would be to have a Future with a timeout set on it.
a := Future withTimeoutDoing: [ untrustedObject doSomething ].
If the timeout occurs, then "a" is assigned an InvalidObject with the error message. Alternatively:
a := [ untrustedObject doSomething] futureOnTimeout: [ ...handle a timeout situation... ].
or
a := Future doing: [ untrustedObject doSomething] onTimeout: [ ... ].
Secure Interface objects
A secure Interface object is an object which has the public API of some other object, but simply forwards all invocations to that other objects. This makes sure that holders of a reference only have access to a particular sub-set of methods of that object. Also, an Interface can be disabled; by removing the reference to that object from the Interface, the connection is broken.
If a method on an object returns "self" (which methods with no return value do), then the secure interface might need to catch this case and return itself instead:
MyInterface>>someMethod: args
| result |
result := target someMethod: args.
(result==target) ifTrue: [ ^ self ].
^ result.
If the secure interface is disabled, target could be replaced with nil or some other error object.
Interfaces to define APIs
Often in Smalltalk, this technique is used:
(someObject isKindOf: SomeClass) ifTrue: [ ... ].
In SecureSqueak, this is no longer useful, because the class might be a different version than what is referred to in the method literal. Instead, an Interface could have the ability to return whether an object implements that particular API:
someMethod: someObject
someObject mustImplement: SomeInterface. " Will raise an exception if false. "
(someObject implements: SomeInterface) ifTrue: [ ... ].
The same Interface object could be used for both the secure interface (above) which is passed as a public accessor to a private object, and as an API definition. An IDE could provide support for generating an interface automatically from a particular method category or categories.
There are two ways that interfaces could be implemented:
- Classes could declare that they implement particular interfaces at development time.
- At runtime, a class can be checked to make sure it implements all methods of a pariticular interface.
The issue here is that a class may implement all methods of a particular interface, but not intentionally: the fact that the methods are implemented is cooincidental and the method implementations might be different from what the interface prescribes. In reality though, this is unlikely to happen.
Open question: if a developer implements all required methods of an interface, but does not explicitely state that the particular class implements that interface, then should that class be considered to have implemented that interface?
Other names for an interface: arbitor, negotiator, emissary, moderator, referee. Hmm... I still prefer "Interface"; it comes with a prepackaged definition from Java.
Private Methods, Namespaces
Private methods could be defined, but would only be able to be checked at run-time: "Method private." (or maybe "Sender private"?) would throw an AccessDeniedException if the method is invoked by anybody other than self. The VM could be modified to be fast for this.
Other levels of protection for methods are:
- private to this class, and subclasses ("protected" in Java).
- private to this class and superclasses, but not subclasses ("private" in Java).
- private to this namespace.
- private to this package.
- callable only by this set of objects: [...]
Namespaces can also be private: they would only be searchable by particular other Namespaces. For example:
- A namespace might only be accessable to its members (although how would you initialise it?)
- A namespace might only be accessable to parents in the same package.
- A namespace might only be accessable to this package.
This could be implemented by:
- calling a method that verifies the sender using thisContext.
- Modifying the VM to somehow separate the method dictionaries of a class.
Non-overridable methods (final methods)
Some methods (in particular, Object>>==) may not be overrided.
This also ensures that an attacker who tries to pass an object which is a subclass of your class as your original object cannot override methods which may cause destructive behaviour.
This must be enforced by the code loader. Somehow, the code loader must know that a method cannot be overridden.
The options for overriding this are:
- Provide a special "thisMethod" keyword, which refers to some interface to the current CompiledMethod. Any method sends to this interface would be read by the code loader. Disadvantage: breaks the OO model.
- Using <pragmas>. Pragmas are a contentious issue; they are only in Squeak and VW, and may need to be removed because they are insecure.
- Somehow add meta info to the compiled method directly, using the browser UI. This meta-info would be retained in file-outs and when the class is serialized.
Non-subclassable classes
Sometimes a class should not be able to be subclassed. This will prevent somebody from overriding methods with malicious implementations.
Verifying the sender
Sometimes, it is important to make sure that only a particular object or set of objects can invoke particular methods on a object.
A private method is a method that verifies that the sender is self.
The following would verify that the sender of the method is permitted to do so:
someMethod
Sender mustBe: someObject. " Will raise an exception if false "
Sender mustBe: self. "Private method. "
Sender mustBeIn: authorizedObjects.
Sender mustImplement: SomeThing.SomeInterface.
(plus one to specify that the sender is self, and the method is in this class rather than a super or sub class).
Sender mustBeInNamespace: Some.Namespace.
Sender mustBeInPackage: SomePackage. " Useful for verifying that an object's implementation is in a trusted package. "
These methods would have special access to thisContext to verify the authenticity of the sender.
Globally available objects such as System.SystemEditor (which can call >>instVarAt:put: on objects, etc) would be useful to verify that a method is not being invoked by an attacker.
Sender spoofing
It is possible to use blocks to fake your identity.
Say for example:
Object>>instVarAt: num
Sender mustBe: System.Debugger.
...
Now, if System.Debugger has any method which calls >>value on a block, then an attacker could make an evil block such as:
System.Debugger new doThis: [ secret := crackedObject instVarAt: 1 " Could potentially pass through the mustBe: " ].
This could be avoided by making Sender>>mustBe: and friends check that stack frames are not block contexts. Or, maybe the sender is the original owner of the block? TODO: check this.
Patterns
- Capabilities with a back-door to the object's instvars.
- Methods that verify their sender or the capabilities of the sender.
- dominion encapsulation - make sure you don't leak your dominion.
Immutable Objects
If you want to make sure that nobody can edit your object, you can make all mutator methods return a new mutated copy of your object, leaving the original untouched.
Baking objects
A baked object is an object that has a boolean value in it that specifies whether that object is read-only or not. All methods which set values in that object first check that boolean value. That boolean value is initialised to be "false" (i.e. the object is mutable) and to "bake" the object, that boolean value is set to "true". No method allows that boolean value to be set back to "false" again.
To bake an object is to make that object read-only. The metaphor translates directly from cooking; to make a cake, you mix all the ingredients together and bake them. Once the cake has been baked, it cannot be unbaked. This would be implemented by having a "baked" instance variable and a >>bake method:
SomeObject>>initialize
baked isNil ifFalse: [ ViolationException signal: 'Attempted to modify baked object.' ].
... initialize other stuff.
SomeObject>>bake
" There is no way to set baked anything after true after this invocation. "
baked := true.
SomeObject>>accessSomething: newValue
"Every accessor which modifies baked state will test for baked first. "
baked ifNil: [
something := newValue ].
Alternatively, a secure interface could be used to only give access to methods which can read but not write the state of the object.
Alternatively, setter methods could allow only a particular object to set the state of object, verifying the sender's identity using "Sender shouldBe: someObject".
Protecting instance variables from subclasses.
It might (or might not be) worthwhile preventing subclasses from being able to directly access instance variables in superclasses. Instead, accessor methods could always need to be used to modify instance variables, and these accessor methods could be private methods. This is changing the semantics of Smalltalk and could make porting existing code a difficult exercise.
This could be done by a bytecode verifier.
Special APIs
(work in progress)
Dominions are available:
self dominion. "return my dominion, only callable by self."
self hasCapability: SomeCapability...
self dominion cpuSpeed < 150 ifFalse: [...].
A special "sender" global variable is available. This has access to thisContext (whereas other code doesn't) and can verify who the caller is:
(sender = someObject) ifTrue: [...].
(sender hasCapability: someCapability) ifTrue: [...].
sender isKindOf: aClass...
sender isMemberOf: aClass... "less useful if multiple versions of that class are available.
sender implements: aClass... " throws an exception, or should it return true/false? "
sender mustBeSelf. " Declare a private method. "
A mechanism for declaring types of objects will be implemented.
myObject isA: SomeClass. "Throws an exception if not. "
myObject implements: SomeInterface. "Throws an exception if not. Hmm... looks like it should really return true/false. "
(or maybe:)
myObject mustImplement: AnInterface. "This is probably the most commonly used one."
myObject mustBeKindOf: aClass.
myObject mustBeMemberOf: aMember.
(or maybe:)
Declare v: myObject is: SomeClass.
Declare v: myObject implements: SomeInterface. " Interface adherance is cached in each class."
Some of the above methods would need to be heavily optimised, perhaps even be primitives or bytecodes.
Using Dominions
(TODO)
- Test the resources available to that dominion.
- Create an object in a different dominion.
- Verify that an object belongs to a particular dominion: "anObject mustBeInDominion: d".
- Ask the user for more privileges.
Example: classes.
Currently in Squeak, "anObject class" will return that class. The given class object contains all the methods to edit that class. In SecureSqueak, this must be refactored so that "anObject class" returns a read-only object that does not allow the given class to be edited.
(aside: currently "anObject class" is a primitive. This will be changed.)
Possibilities:
- Return a proxy instead.
- Return the class which is read-only, but also provide another object elsewhere with references to the instance variables of class (name, instvars, methoddict, ...) and the ability to edit them. You would then have two classes: "ReadOnlyClass" and "ClassEditor". This has the disadvantage of not being able to modify the references that the instvars are.
- Make a special capability object in Squeak. In every method that can edit the class, use "Sender hasCapability: foo" to verify that the sender is allowed to edit that class.
- Make sure the sender is in a particular Dominion, or in a dominion with a particular capability?
- Make classes read-only with a "read-only-flag" that prevents setter methods from working once set.
Comments (0)
You don't have permission to comment on this page.