Walnut/Secure Distributed Computing/Auditing minChat

From Erights

(Difference between revisions)
Jump to: navigation, search
(wikify, other minor corrections)
m (Reverted edits by (Talk); changed back to last version by Ansible)
(2 intermediate revisions not shown)

Latest revision as of 22:13, 24 September 2009

Example: Auditing minChat

The minChat application presented earlier as the example for a distributed system was designed with no consideration at all for security issues. This is literally true: minChat is derived from eChat, which was written by the author as his first practice exercise to learn E. However, because eChat was written in a capability secure environment, because the author used a fairly clean modular architecture, and because clean architecture in a capability-secure infrastructure makes its own luck, there were no serious security breaches in eChat. And, as we shall see, there are no serious security breaches in minChat, though there are many interesting lessons we can learn from it.

First of all, in minChat as in all E programs, the communication is all encrypted and the objects are all unfindable and unguessable. Therefore no third party can enter the conversation to either eavesdrop or forge messages. The only source of security breach will be the person at the other end of the chat system, i.e., the person with whom you planned to chat. So, right off the bat, simply by using E you have eliminated outsider attacks. But insider attacks are still possible, and indeed constitute the most serious threat to most security systems anyway. You have to be able to achieve cooperation in the presence of limited trust. Cooperation despite limited trust is something we humans achieve every day, for example when paying 3 bucks for a gallon of milk at the local QwikMart. But it is notoriously difficult to get these transactions working correctly on computer systems when using conventional languages. So we will examine minChat very closely to ensure that the person we want to chat with does not also get any inappropriate powers, like the ability to delete all our files.

Now, let us begin with a quick review of minChat's code to see what parts of the system we need to examine to do a security review. As you may recall from the chapter on Ordinary Computing, emakers come into existence with no authority whatsoever, and receive authority only via the arguments that are passed into their objects. In larger programs, one can often ascertain that the emaker receives so little authority it can be disregarded from a security perspective: if it doesn't have enough authority, it just can't be a danger (as documented in the DarpaBrowser Final Report and Security Review).

MinChat is just 2 pages of code. It does not use emakers, so this does not help in our current situation. However, what does help is that the only object accessible to the outside world is the chatController, whose reference is sent to the friend (or, in this case, perhaps the enemy) with whom we wish to chat (hey, we need to talk to our enemies too, from time to time). What unpleasantness can our friend do on our computer through the chatController interface he receives? We will reproduce the crucial lines of code here for convenience:

     to send(message) {
         when (friend<-receive(message)) -> {
             chatUI.showMessage("self", message)
         } catch prob {chatUI.showMessage("system", "connection lost")}
     to receive(message) {chatUI.showMessage("friend", message)}
     to receiveFriend(friendRcvr) {
         bind friend := friendRcvr        
         chatUI.showMessage("system", "friend has arrived")
     to save(file) {file.setText(makeURIFromObject(chatController))}
     to load(file) {
         bind friend := getObjectFromURI(file.getText())
         friend <- receiveFriend(chatController)

There are only 5 messages here. Let's go through what our friend could do with each one of them in sequence.

  • send(message): This is the method that sends a message to our friend. If our friend wants to send messages to himself, which we can ourselves read in our own chat window, this is amusing, but not very exciting as an attack.
  • receive(message): This is one of the 2 messages our friend is supposed to use, to send us a message. The message gets placed in the chatArea text pane. Since we are receiving data here, even a cracker of modest powers would look at this and leap quickly to the conclusion that there is an opportunity here for a buffer overflow attack--the type of attack that, at the time of this writing, gets the most news media headlines because Windows is so richly riddled with such exploits.

Memory safe languages including E are not in general susceptible to buffer overflow attacks: if you overrun the end of a data structure of fixed size, you get an exception, not a data write into random memory. Alas, this is not the end of the story. The text areas in the widget toolkit are native widgets. Eventually, we send this text string of unbound length to native code, written in an unsafe language in an unsafe way with unsafe quantities of authority. So buffer overflow attacks, and other attacks based on the strategy of sending a stream of data that will surprise a decoding algorithm into unexpected behavior, are possible in theory. This will remain a risk until all the widgets in our toolkits are also written in object-capability languages.

The only good news here is that the text widget uses a very simple data type, and has been attacked by accident by so many millions of programmers over the years that it has had to be fixed just so the system doesn't crash if you accidentally drag/drop a DLL file onto Notepad. So even under Windows, the text widget is robust against strange data. But this is an area of risk of which we must always be aware when using components written outside the capability paradigm. Text widgets are pretty safe, gif and bmp widgets are probably safe, but by the time you encounter a widget using manically optimized C to decode mpeg-4, you're looking at a widget that is complicated enough that it is probably vulnerable.

  • receiveFriend(friendRcvr): Our friend could specify someone who is not himself as the person we should send our messages to. Of course, our friend could write a slightly different version of minChat that does the forwarding to everyone he wants to see it, automatically. Or he could simply delegate the capability reference to someone else (i.e., hand a copy of it to another party). So this not very exciting either.
  • save(file): Now this is a very interesting method for attack. The opportunity for the friend to save data on our computer is an interesting one, and certainly outside the domain of what we intended for simple chatting. There's a little problem, however, from the attacker's point of view. The save method is receiving, as a parameter, a capability (i.e., a reference) to a local file. This capability is not forgeable by the friend unless he has access via other means to our file system, in which case we already granted him such power and are assuming he won't abuse it (if he has such power, he can abuse it more effectively by other means, abusing it by sending to minChat is silly). The upshot is, the worst our malicious friend can do is send us an object that is not actually a file object, which will not respond properly to messages being send under the assumption that the receiver is a near file object, and cause a "no such method" exception which will crash our session. So, our friend can crash minChat, but can't do any serious harm: an attack whose only result is to cut off the target from the attacker's attacks hardly constitutes a winning gambit.
  • load(file): Not only is loading a file less interesting than writing, or corrupting, one, but worse, this method once again expects a local file object to work with. Again, the worst case is that the attacker can cause a session crash. An interesting variant of the attack might be to try to ship an object that looks and feels like a file for minChat to play with. This would fail directly because the load operation is filling a def'd variable that is already bound, resulting in a thrown exception. But let us assume for a moment that we made the variable "friend" a var, rather than a def that was later bound. The result in this case, if successful, would simply be to redirect the minChat client to a different person for further communication (since what is being loaded is the reference to the friend). Still no joy for the attacker.

The upshot is, even the friend who has received the capability to talk to our chat tool doesn't get much traction. He can annoy us (by crashing minChat), but that is pretty much the only interesting thing he can do....or so it seems on first review.

Let's see if there is anything exotic an attacker could do by combining several of these unimportant attacks. First let us make the problem more concrete. Suppose Bob has a crush on Alice, who is dating Ted. Bob decides to make Alice think that Ted doesn't really like her. So he gives Alice his chat reference to Ted, and tells her that, when using this reference, Ted will think she is really Bob, and so Alice will be able to get Ted to "speak candidly" (to Bob) about her. At this point, Bob uses the "send" method on Ted's computer to send messages that Alice will read, messages that look to Alice like Ted is sending them! Bob sends cruel jokes about Alice, and Alice breaks up with Ted.

Hmmm....it looks pretty bad for Ted. Did we just find a really cool attack?

Well, sort of. First there is the risk that Ted will notice that his chat tool is, without any help from the owner sending offensive messages to Bob. Ted might then wonder what is going on. But more fundamentally, Bob has a much simpler scheme for messing with Alice's headspace, if Alice is open to this type of attack.

Far simpler for Bob would be to create 2 brand new chat capabilities, one that is Bob's FakeTed, and the other is Bob's FakeBob. He has Alice start up as FakeBob, and Bob himself starts up FakeTed. Now Ted is completely out of the loop, Bob can send any horrid thing to Alice he wants to send, without interference.

So is this a fundamental flaw with the entire E security approach? Not exactly. This attack can be made regardless of the technology being used -- Bob could have created 2 Yahoo Instant Messenger accounts and played the same game. Indeed, variations on this attack were used repeatedly by William Shakespeare throughout his career to create both high tragedy and low comedy. The fundamental problem comes when you trust a middleman as the sole validation of the authenticity of messages coming from another person. This is a human problem, not a technology problem. The best source of contact information for a person is the person him/herself; trust a third party at your own risk. Often the risk makes sense, but don't ever forget there is a risk there, whether you are using capability security or just a parchment and quill.

We have come full circle to the conclusion that minChat doesn't have any serious security breaches. Nonetheless, there are a couple of annoying things Bob may do to Ted. These annoyances can be traced directly to the presence of five messages in the chatController: there are only two messages out of the five that the friend is "supposed" to use. Even forgetting about security reviews for a moment, it is clear that, if the friend is only supposed to have two methods, he should not have five methods at his disposal. This make sense from a simple modular design point of view, it limits the number of mistakes you can make. For the security reviewers, this is an enormous win, because it reduces the number of methods to examine by 60%. This is a substantial gain, given how circuitous and complicated security analysis can become.

So how do we cut the number of methods available to the friend? By using a facet of the chatController that only has the appropriate methods:

 # E syntax
 # Place the following object above the binding of the chatController
 def chatFacet {
     to receive(message) {chatController.receive(message)}
     to receiveFriend(friendRcvr) {chatController.receiveFriend(friendRcvr)}
 # Now modify the chatController's save method to save the chatFacet, not the chatController:
 to save(file) {file.setText(makeURIFromObject(chatFacet))}
 # and similarly modify the load method to send the chatFacet not the chatController:
 to load(file) {
     bind friend := getObjectFromURI(file.getText())
     friend <- receiveFriend(chatFacet)

This version of minChat is more secure (only a little more secure, though, since it was pretty secure already), and vastly easier to review.

Security Implications of FORTRAN-Style E

There are still a number of interesting lessons we can learn here by considering some variations on minChat. The first variation is based on the old adage, "You can write FORTRAN in any language." This is true in E as well, but the implications can be grievous.

Let's go back the minChat without the chatFacet, and consider the following small change to the program. Suppose that the save method in the chatController, instead of receiving a file object from the chatUI and creating the uri string itself, received the file path and the uri string from chatUI:

 # E syntax
 to save(filePath :String, uri :String) { <file: filePath>.setText(uri)}

This is a fairly FORTRAN-ish way of accomplishing the goal: since FORTRAN doesn't have objects, it may seem perfectly obvious and reasonable to just send the path to the file and let the chatController figure out how to write to that file on its own.

However, in this situation, it is a disaster. Without the chatFacet, now the friend at the far end has a method he can call that allows him to write any data he wants, into any location the user can reach on his own computer:

 # on the friend's machine
 # put the code for the SubSevenServer Trojan in a string
 def code :String := "@#$!...and so on, the character representations of the code bytes"
 # The friend's friend is our poor user
 # Put the trojan horse in the user's startup profile
 friend <- save("~/startup/sub7.exe", code)

Now we're having some fun! Every time the user logs onto his computer, the friend gets full control of all his resources.

What went wrong here? There are several ways of slicing the pie and pinning different pieces of the code with blame. One we have already identified: we failed to follow POLA in allowing the friend to call the save method in the first place. But another one is just as crucial: we failed to follow POLA twice with this revised version of the save method that gets 2 strings.

We failed first by sending the save method an insufficient amount of authority (remember, Least Authority also means Adequate Authority). We did this by sending the chatController a string describing the file's path rather than just sending him the file itself. As a consequence, the chatController had to use special powers gotten from elsewhere to translate the description into a file object.

Once the chatController had to do this translation, it became vulnerable to the Confused Deputy Attack, a classic in security literature. The Confused Deputy attack is the aikido move of computer security: the attacker persuades the target to use the target's own authority against itself. Confused Deputy attacks are very hard to pull off as long as a single reference serves as both the designation of the object and the authority to use the object (as is always the case with the objects in a capability-based programming language). But when the authority to use the object is separated from the designation, as it is when a path string is being used to describe the file, you are in deep trouble.

Simply sending the file object rather than the file description solves this problem. The friend at the far end has no way to send an object that will be interpreted by the save method as a local file, and so the friend gets no traction whatsoever as long as the save method is expecting the object not the description. This leads to the general rule:

When you must read in the descriptions of objects rather than the objects themselves, translate them into capability secure objects at the earliest possible moment. When you must write out descriptions of objects rather than the objects themselves, postpone the translation of the object into a description until the last possible moment.

This rule is not merely good for secure, capability-oriented programming. Rather, it is good for all object-oriented programming. Manipulating the descriptions of objects is much less reliable, and much more error prone, than direct manipulation of the object itself--which is both why crackers like it, and object oriented designers avoid it. This exemplifies an interesting truth: capability oriented development is often just object oriented development taken seriously.

Working directly with the object, not a description, is a rule the author learned the hard way. A security review team ripped numerous holes in a single piece of the author's software, simply because the author foolishly worked with descriptions rather than with objects. The story is told in excruciating detail in the DarpaBrowser Security Review. If you are one of the rare and lucky individuals who are able to learn from other people's mistakes, rather than having to make the mistakes firsthand yourself first before you really get it, this is a magnificent lesson to learn secondhand.

Defense In Depth versus Eggshell Security

In large software systems, most objects are constructed inside emakers, rather than in the main E program body. Consequently they come to life with severely restricted, POLA-oriented authority(i.e., only the authority we send to them when we construct them, which tends to be what they need but not anything else). Let us emulate this characteristic of large systems in our little minChat by making a paranoid version of the chatFacet:

This is grossly broken syntax for eval

 # E syntax
 def makeFacet := e`
     def makeFacet(chatController) {
         def chatFacet {
             to receive(message) {chatController.receive(message)}
             to receiveFriend(friendRcvr) {chatController.receiveFriend(friendRcvr)}
         return chatFacet
 def chatFacet := makeFacet(chatController)

The universalScope eval method creates the makeFacet function in strict confinement, identical to the confinement imposed on emakers. Consequently, the only authority the chatFacet has is the authority handed in, namely, the authority to send messages to the chatController.

Now let's assume we are running a more complex program, with a more complex component, i.e., a component that is not so simple we can just look at it and see it doesn't do anything very exciting. Now let's further assume that by some extraordinary bit of legerdemaine the attacker succeeds in completely subverting the component (the chatFacet in our little example). How terrible is the disaster?

As we have already shown, there is no disaster. Even if the attacker gets direct access to the chatController, there still isn't anything interesting he can do. We have achieved defense in depth. A typical penetration of our system gives the attacker only a limited access to other objects, which in turn must be individually penetrated.

Compare this to the situation now typical in software engineering. Even with the most modern "secure" languages like Java and C#, an attack can acquire the full authority of the program by subverting even the most inconsequential object. With tools like access control lists and firewalls, we engage in "perimeter defense", which is more correctly described as "eggshell defense". It is like an eggshell for the following reason: while an eggshell may seem pretty tough when you tap on it, if you can get a single pinhole anywhere in the surface, you can suck out the entire yoke. No wonder cybercrackers laugh at our silly efforts to defend ourselves. We have thrown away most of our chances to defend ourselves before the battle even begins.

Personal tools
more tools