Tip:
Highlight text to annotate it
X
>> DOUGLAS CROCKFORD: And now, the part you've all been waiting for: Part 5, The End of All
Things. I believe that children are our future. And, I believe, also robots. So children and
robots are our future. What do I mean by this? Well, you remember that last time we talked
about the browser, and the wonderful things that are in the browser and the terrible things
that are in the browser? The worst of all of the things is its security problems.
In particular, the worst of all of its security problems is the so called cross site scripting
attack, or XSS. It's not called CSS because that would be confused with the Crappy Style
Sheets system that's in the browser. It really shouldn't be called cross site scripting either,
because there are modes of this attack which have nothing to do with operating across sites.
You can have one of these attacks happening within a site. What it really is is a confusion
of interest attack. One thing that's unfortunate is that the security experts who identified
and named it got it wrong, and have not corrected it since then, and expect all the web practitioners
to be adopting their broken jargon. We're going to talk a lot about these classes of
problems, and how we're going to fix them.
Why is it a problem? What happens if an attacker can execute one of these cross site scripting
attacks and get some script onto your page -- what can he actually do? What's the real
hazard here? The attacker can request additional script from any server in the world. There's
a thing in the browser called the same origin policy, and it puts absolutely no restriction
on this. With just a little bit of script -- just enough to create a script tag, or
it can even be a little bit of HTML that creates a script tag -- you can then load megabytes
of additional programs. The page will not be notified that this initial programming
is put in, and the page is powerless to prevent it from acting. Once the evil script gets
its foothold, it can obtain all the script it needs to do whatever it wants to do to
you.
The attacker can read the document. The attacker can see everything that the user can see.
In fact, the attacker can see things that the user cannot see. He can see the comments
in the HTML, he can see all of the scripts, he can even see the hidden fields that you
have which are keeping the secret authentication that allows you to communicate with your server.
He can see your cookies, he can see everything.
The attacker can then make requests of your server. If your connection is secured over
SSL, which it should be, he gets access to that secure connection. If you are authenticating
your requests by having crumbs or some other token of authentication, he gets access to
that too. There's absolutely nothing the server can do to distinguish between one of these
illegal requests and one of your own scripts' requests. Worst than that, if your server
accepts SQL requests from the browser then you have given the attacker direct access
to your database. If you're creating SQL queries, inserting code that comes from the browser,
you may also be doing the same thing. It seems that SQL was optimized for SQL Injections
Attacks.
The attacker has control over the display, and can request additional information from
the user. So one common technique to try to prevent these sort of attacks is whenever
something important happens, ask the user to type in their password, because the attacker
doesn't know that. But the attacker has absolute control over the display, and can ask the
user: 'what's your password?' If you're on a site that has passwords and you tend to
ask the user a lot for their password, then they've been conditioned to give it up at
any time. The browsers now have a lot of anti-phishing chrome, and unfortunately most users don't
pay any attention to that. But if they are paying attention to it, it will tell them
this is a legitimate request, that this is a page that came from the site they were on,
so they should probably do whatever it tells them to do.
The attacker can then take the information he's obtained from the user, from your server,
from the page itself, and send it to any server in the world. Again, the same origin policy
does not prohibit this at all. The browser does not prevent any of these things. These
are not due to bugs. There are, obviously, lots of bugs in browsers, and there have been
security problems because of the bugs, but I'm not talking about bugs tonight. The web
standards require these vulnerabilities. If you don't have these vulnerabilities in your
browser, it is not standards compliant. There is something deeply, deeply wrong with the
standards of the web, because they not only allow this stuff, they require that we have
this vulnerability in everything that we do.
The consequences of one of these attacks can be horrible. We can see real harm to our customers,
we can experience a loss of trust which is really bad for business, there can be legal
liabilities. In some cases there can even be criminal liabilities. Again, there is not
a lot we can do about this. It's inherent in the browser platform.
HTML5 is in no way a solution. I think HTML5 is a big step in the wrong direction. They
not only do not correct this problem, they make it significantly worse. They do that
in two ways. First, they make the browser a much more complicated platform, and complexity
is a tool of the enemy. We should actually be looking to simplify and normalize, regularize
the platform. HTML5 does not accomplish that. Two -- even worse -- HTML5 amplifies the rights
that the attacker gets, so the attacker, in addition to doing all the other stuff we talked
about, is now able to get at your local database and can pull all the information out of that,
or modify it in any way that he wants to. He now has the ability to do some more extended
forms of network communication and inter-process communication.
For all those reasons, I strongly believe that HTML5 in its current form should be abandoned.
We should start over with a new set of design rules specifically intended to solve the cross
site scripting problem, and then to enhance the browser and make it more competitive with
the other platforms as a secondary goal. But that has to be secondary to the primary goal
of making this a safe, reliable platform. I don't think the current HTML5 effort can
be moved into that direction, I think it's too late. I think the best thing to do would
be to start over. There are some good ideas in HTML5, and I think the RESET Project should
mine the current proposal for what good ideas it has. But I think that the best thing would
be to start over.
This problem has been with us for a long time. Cross site scripting attacks were invented
in 1995. We have made no progress on the fundamental problems since then, except for one baby step
that we made in December of last year. In a little bit I'll talk to you about that.
Why do we have cross site scripting attacks? What is the cause of that, and why is it such
a difficult problem to fix? There are a number of causes, and any serious problem has more
than one cause. The first is that the web stack is too complicated. We have a lot of
languages in the web – we've got HTTP, HTML, CSS, URLs, JavaScript, and a lot more,
and these languages each come with their own encoding, quoting, commenting, and escapement
conventions that are all different across all of these languages, and they can all be
nested inside of each other. On top of that, the browsers do heroic things to try to make
sense out of malformed content. Adding all of these together means that it's surprisingly
easy for a villain to put bad stuff into an HTML document, or into user-generated content,
and it's very difficult for us to discover it and keep it out and keep everybody safe.
The last one, the bit of false heroism, sometimes that's blamed on the browser makers for working
too hard to try to make sense out of stuff. I think the blame should actually go to unprofessional
webmasters, particularly the first generation of them, who couldn't write correct stuff.
During the browser wars, the browser makers competed by seeing who could make the most
sense of all of this crappy coding, and in doing that, increased their market share,
because the amount of bad coding out there was significant enough that capturing it was
apparently a beneficial thing.
Another factor is our reliance on template-based web frameworks. I think they're optimized
for XSS injection. This includes things like ASP, JSP, PHP -- pretty much anything with
a P in it, you're asking for trouble. It's possible to do stuff correctly in all of those
systems, but the defaults are against you. It's easier to do things wrong than to do
things right, and that's a recipe for disaster.
Another cause -- perhaps the biggest, most fundamental cause -- is the JavaScript Global
Object. It gives every scrap of script in the page exactly the same set of capabilities.
JavaScript confuses the interests of your script with all the other script that's running
there. Now, as bad as the browser is at security, it is better than everything else. Everything
else is worse, because everything else confuses the interests of the program, or the site,
and the interests of the user. The browser understands that those are two distinct things
and keeps them straight. Nobody else does that; at least, not very well. Where the browser
got it wrong was it failed to anticipate that there could be additional interests in the
page than just the user and the site, and that's the place where it fails.
Then to complicate that, there is the mashup problem. Mashups are wonderful things. A mashup
is the combination of programs representing multiple interests, and the browser confuses
those interests. An advertiser on a page gets the same privileges as an Ajax library, or
an analytic file, which get the same rights as the main application, and any of those
XSS things that somehow got onto the page. Everybody gets the same set of rights. So
it's impossible for any of those components to defend themselves from the other components.
This is particularly troublesome in the case of advertising, because advertising is a self-inflicted
cross site scripting attack. Mashups are a self-inflicted cross site scripting attack.
I think mashups are the most interesting thing to happen in programming development in many
decades, so it's important to get this fixed.
Fortunately, we have made some progress on this in the safe JavaScript subsets. Two of
the best known of these are Caja, which was developed at Google and is being used very
extensively at Yahoo!, and my own ADsafe system. The thing they have in common is that they
are attempting to keep guest code away from the global object. If guest code can't get
at the global object then it will not be able to do all the bad things that I described.
Caja and ADsafe take different approaches to accomplishing that. Caja is a transformer,
so it will rewrite your JavaScript into different JavaScript which has different properties,
which will have runtime checks put around certain dangerous things in the language,
which will insert doppelgangers into the system so you think you're talking to a global object
but you're actually talking to something that's benign. Caja is a wonderful system, but it
comes at a performance penalty. Also, it's difficult to debug, because the code you're
actually running may not look very much like the code that you wrote. ADsafe takes a different
approach. ADsafe works with static validation and removes features from the language which
are difficult to verify statically. It turns out most of the languages is preserved, but
you lose the ability to use this, and your ability to use subscripting brackets is greatly
curtailed, which is really annoying.
So we have these two systems that are available which give us a partial solution to the security
problem, but last December we did better. ECMA formally adopted the fifth edition of
ECMAScript -- hooray -- and it contains a new mode called strict mode. Strict mode allows
us to do a very nice thing. Strict mode makes it possible to have static verification of third party
code without over-constraining the programming model. So you get the best of both Caja and
ADsafe in that you've got the full use of the language that Caja permits you, but you
run with the same speed and the same convenience as you do with ADsafe. This is a really nice thing,
except it doesn't do us any good unless everybody is running on ES5/Strict, and it's not in
the market yet.
Even having that, there's still a lot of work left to do. ES5/Strict can protect the page
from its widgets, but it cannot protect the widgets from the page. This is important in
applications where you want to put advertising on any website and be confident that the website
is not going to do something bad to the ads, or suppress the ads, or engage in click fraud.
There's no way to do that. ES5/Strict cannot protect the page from cross site scripting
injection, so that danger is still present. ES5/Strict is an important step toward a programming
model in which multiple interests can cooperate for the user's benefit without compromising
each other or the user. It's an important step, but not a sufficient step.
The browser makers are now in a race to implement ES5/Strict. It's not clear who's in front
yet, but I'm hoping that they start emerging in the marketplace soon. On the day when all
five major browsers support ES5/Strict, IE6 must die. I don't want to sound repetitive,
but it's really important that we kill it. We get absolutely no benefit from ES5/Strict
if we still have to be backward compatible with browsers that are broken, so somehow
we have to figure out a way to say goodbye to these guys.
Many people get discouraged when you talk about security because so many popular approaches
to security fail. They just fail. For example, there's security by inconvenience, which I'm
sure you've seen practiced at the airport where they put us in corrals like cattle and
run us around. It's proven that that is not an effective security mechanism, but they
do it anyway because we've got to do something; we have to at least put on a show that we're
making you safe. That makes people feel better, so it accomplishes that -- maybe -- but not
much else. We've tried security by obscurity, where you try to make your system so complicated
that the attackers can't understand them. That doesn't work. We've seen people try to
inject speed bumps into the information super highway with the thinking that's going to
slow the attackers down -- it doesn't. We've seen confusion of security with identify,
that if we know who wrote the code then that tells us something about how safe it is to
use. That turns out to be completely useless. What we're left with is security by vigilance,
and that doesn't really work either.
So what we do is we've got some very, very smart people who have been studying the corpus
of things that are wrong with the browser, which is huge, and understanding it fully
and keeping up to date on it, and then communicating that information back to our developers. It
turns out it's just too much information to handle. It's way too complicated, and the
process of transferring that information to developers, who already have enough to do,
is really not effective. But at the moment, that's the state of the art. That's the best
we know how to do, and it is just not good enough.
I think we can do better, and the solution to doing it better is actually already in
JavaScript. JavaScript gets beat up for its security problems, but it is actually closer
to being a secure language than virtually any other language out there. You just have
to recognize the pattern and make it work. I'm going to show you some examples of how
to think about this. This is stuff that is between Caja and ADsafe, but it's already
in the language and just needs to be exposed.
JavaScript is a Pass By Reference language. That means that when you pass a value to a
function, you're not passing a copy of it and you're not passing the name of it, you're
passing a reference to it. In oldspeak that would be a pointer. Sometimes it's a little
confusing when values are mutable, as some JavaScript values are, and Pass by Value and
Pass By Reference look the same, but JavaScript is a Pass By Reference system as most systems
are today.
In a reference system like this one, you get effects like this. We've got a variable named
variable, and I initialize it to an empty object. Then I've got two more variables,
a and b, and I assign variable to both of those. Then I test for equality. Equality
will be true only if a and b are the same object; it will be false if they are identical
objects. In this case, can you guess what it's going to do? It's true. They are the
same object. So a and b are not copies of the variable, they are the value that the
variable references. They all contain exactly the same thing at this instant.
It's important to understand this because people often get confused between values and
variables and misunderstand what their relative goals are, so let me show you an example.
Here I've got a little function called funky, and it's a silly function. It takes a parameter
called o, and it assigns null to it. Not a useful function, but there it is. Then I create
a variable called x, and I assign an empty object to it. Then I call funky, passing x.
After I do that, what will the value of x be? Who thinks it's going to be null? Who
thinks it's going to be an empty object? It's going to be an empty object, and the reason
is we're passing to funky the value that x had, we're not passing it x. So funky knows
nothing about x. All funky knows is that he was passed an object. When he does the assignment,
what he's doing is replacing what his o points to from the object that was passed in to the
null that he replaces it with. All of that happens completely independently of x. This
is a really important thing to understand about the language because all of the security
properties we're going to talk about later derive from this.
Let me show you one more example. I've got a swap function, where I pass it a and b,
and using a temporary variable t, I will swap a and b. Then I'll create a few variables:
x and y, and initialize them to 1 and 2, and then call swap. After I do that, what will
the value of x be? Will it be 1, or 2? 1? 2? Good, nobody's hand went up that time.
The answer is 1. Because again, swap doesn't know anything about x and y. Swap only knows
about the values that they were associated with, it doesn't see the variables themselves.
This confusion about variables and values can lead people into writing really bad code.
Here I've got a function called do_it. It'll take some inputs and do some useful things
with them, and compute a result. When it is done, it wants to assign the result to the
variable whose name was passed in. There are a couple of ways you could do that. Both are really bad, which is why
they're in red. One is you construct an eval, where we take the name and concatenate it
with equal sign and result, and that's just so awful. Nobody ever, ever do that. If you
ever find yourself writing code like that, seriously, step away from the machine, OK?
[laughter]
Another alternative which is not quite so totally awful, but is still very awful, is
to recognize that the window is the alias of the global object, so you can find any
global variable in there and do the assignment there. That's bad for a number of reasons.
One is you really shouldn't be messing with global variables, because they're evil. The
other is that if you really want a structure in which you can have names and refer to names
by looking them up, what you really want is an object, and JavaScript is really good at
that. You can use the language well in that way.
Another way that I could write do_it is I could pass in the object and the name and
then I can do it the right way. This is in pink because it's still not good, but it's
a lot less bad than the previous one. It's still too obsessed with trying to change something
that doesn't belong to it. Even though it's passed in, there's a bad information hiding
property going on here. A better way to write this would be simply to return the result,
and then the guy who called can decide what to do about it. Sometimes that's not the right
way to do things, particularly if the result cannot be determined immediately. For example,
if the result depends on going to the server and getting some more information, or if the
result depends on waiting for the user to do something, then this kind of pattern comes
closer, because you can't lock up the browser waiting for those things to happen.
Another way you could write this -- and this is the right way to do it -- is to have a
callback. In this case I will provide a function which should be called at the time that we're
ready to produce a result, and the function will obtain the result as a parameter and
do something useful with it. Do_it doesn't know what it's going to do, but it provides
that service. This has some really nice security properties. For one, do_it cannot get at whatever
is stored in the callback. The callback is a function, it has a closure, it's closed
to do_it, so it cannot see what's going on, it can't get at my_object. That's really good
-- I can give it this token, this function which allows it to do something useful for
me without giving away the farm. That's a wonderful thing.
Also, because JavaScript is lexically scoped, the callback function cannot get access to
do_it's multiple variables. It cannot steal any information from do_it, it can only get
the stuff that do_it explicitly passes into it. That's really good, too; that's the basis
of a secure system, and that's where we're going with this. Having that, I can now call
do_it and in my call I pass the inputs, and I pass a function, and one of the things my
function could do is take the result and assign it to my_Object.blah. There is no way do_it
can find out what I'm going to do with its result, all it can do is deliver it to me.
That's good -- we've got the information hiding happening in a really nice way now.
Now, if I'm going to be doing this a lot, if I'm going to create a lot of these callbacks
that just do a simple storage into an object, I can automate this a little bit. I'm going
to make a function called a storer which will produce a function that will do that other
thing. I pass to the storer function the object and the name of the property that I want to
set, and it returns a function which will do that when it's called with the result.
Because of closure, it's going to keep track of the object and name and they will be hidden
from do_it, so that's pretty handy. I can call it now by calling do_it and passing in
the inputs and then passing it the result of calling the storer on the two things. Everybody
with me?
Now, if we find we're doing this a lot a lot, for example we're doing a lot of storers to
the same object, we can automate this a little bit more. I'm going to make a storer_maker,
I will pass to it an object, and it will return to me a storer which already knows about that
object so all I have to pass in when I call it again is the name. Down at the bottom I
make my_storer, which is the result of calling the storer_maker with my_object, and then
I can call do_it, passing it now the inputs and my_storer('blah'). You can get as intricate
as you need to with this stuff. You have such a nice property that you can, to a large extent,
create the programming language which is perfect for writing your application. You can sort
of embed all of the things you do inside of functions which allow you to describe the
activities of your program in those functions.
One problem with that is that do_it could hold onto that function that I passed and
use it again, perhaps some time when it's not to my advantage. Maybe I don't want to
allow him to do that, that he can only store once into that variable. So I'll create a
once function, and the once function will take a function and return a function which
wraps it and which guarantees that it can only be called once. If you try to call it
a second time, you'll get a null exception and it'll fail. So there's the function again.
We're taking advantage of closure. When the function it returns is calm, it nulls out
func, and that guarantees that it cannot be called usefully again. That allows us to have
it called only once. Then I can take the result of storer and pass that once, and now we've
got a function that can only be called once. Doing this, I've made a pretty big change
to what do_it is able to do, without changing do_it. Because none of this is do_it's business.
Now, suppose I want to change the rules a little bit. Instead of allowing it to do it
once, I want to allow him to change it as often as he wants until I decide you can't
change it anymore. At that point, it cannot be changed again. To do that, I create a revoker.
The revoker this time returns two functions: one of them is the revocable function, which
acts similarly to the way the once function did, and also the revoke function. When we
call the revoke function it causes the revocable function to fail by removing its function
reference. OK, everybody still with me? Some of the stuff I showed you in Act III, things
like promises, things like sealers and un-sealers, also fit very well into this sort of pattern.
Suppose I want to do more than one thing in response to what do_it does. I'd like to have
a sequence of functions called. I could just write a function every time that happens,
but I can automate that too. In this case, I've got a sequence function which takes a
number of functions as parameters and returns a function which, when called, causes all
of those functions that were in the past to be called in sequence. That would allow me
to write it like this. I'd call do_it, and I will allow it to once execute a sequence
of calling that storer, and that storer, and then taking the result and calling alert with
it. We can do that, too, because alert is a function, so I can pass it in as well. OK?
thing I'd ever seen, which maybe it was at the time. But ten years ago I took a second
[Applause]