Introduction to Web Application Security – Dominik Kundel – AngularConnect 2017

Introduction to Web Application Security – Dominik Kundel – AngularConnect 2017


DOMINIK: All right. Welcome, everyone. This is the original title of my talk, but
I thought… I’m not gonna be sure if a lot of people are
gonna be there if I put that in the title. But over the next 20 minutes I would essentially
give you an introduction to web security. Meaning I’m gonna try to squeeze as much different
topics into 25 minutes as I can, until they sort of kick me offstage. Quick introduction to myself. My name is Dominik. You can reach me pretty much anywhere on the
internet at dkundel. I live in Berlin and I work for Twilio. We have a bunch of different APIs around sending
and receiving SMS calls, chat, video, et cetera. If you want to have a chat about that, I’ll
be at the Twilio booth later, so you can have a chat with me there. There’s one more thing you should know about
me, and that’s that I belong to a group of JavaScript developers that are obsessed with
onesies. We like to go to conferences in onsesies. There is a group for pretty much any obsession. There isn’t one for onesie-loving developers. So I built my own, Introducing Onesie Life. I tried to make sure this is as secure as
possible. So it couldn’t be hacked by hackers like this
guy and the banana in that GIF. I thought about everything. It has https, it’s 100% free, it uses the
strict transport protocol to make sure there’s no downgrading of the https connection, and
I’m sanitizing the HTML that could be put in the page in the form of markdown. And there’s no room for SQL injections, because
if you don’t use a real database, there can’t be room for database injections. I’m not using a NoSQL database. Just plain storage. It turns out that developing web applications
isn’t as easy as I thought. So I chatted with my buddy and asked him to
take a shot at the application and he showed me a bunch of different vulnerabilities that
I would like to share with you today. So with that, enough talking. Let’s actually show some things. This is the page, the login dialogue. And I’m gonna open dev tools on the side. So I’m gonna log in here. With my password. And basically what this does is it does a
POST request that redirects to the page. For authentication, it uses cookies. To be precise, it uses a cookie called authtoken. And if we see this, this is what is called
a JWT or JSON web token that I’m actually using for the authentication part. So if I inspect this, you can see it consists
of three parts. The red part, which is the header, the purple
part, which is the payload, and the blue part, which is the signature that this has been
signed with. First of all, you might see the bright red
bar here which says invalid signature, and that’s due to the fact that I obviously was
smart enough to not sign this with a secret called secret. So therefore it wasn’t able to verify the
signature here. But there is this thing that we can see here,
which is the header field algorithm. That’s the algorithm that was used to generate
the signature for this token. I’m using HS256, which is one of the mandatory
algorithms that has to be implemented with JWTs. The thing is, there’s another mandatory one,
that is called num. And that means we can drop this part. And copy the other part. And let’s do some fun stuff. Namely, let’s change up my user role to admin. I’m gonna keep the user name. I could also change the username. And we’re gonna copy this. Everything but the signature, because we don’t
need that, since we actually don’t use it. And now I’m gonna set the auth token to this
value that I copied. And what I’m gonna do… I want you to pay attention here to the fluffy,
which is only shown if you’re a user. If you’re an admin, on refresh, you’re an
almighty. So we circumvented the security by setting
the token and choosing what role we wanted. We could just generate a token and log in. And that is how easy we could circumvent the
authentication here. So how can we prevent that? First of all, the cookie should be HTTP only. We shouldn’t access that in JavaScript. There’s no reason to manipulate that token. Additionally, it should be signed with a secret
so we can’t just alter it and the server would accept it. And it should be marked as secure so we can
only have it in a secure connection. The other thing is we should have used safe
JWT implementation. This is the library I’m using, but I’m using
an old version. It should be able to specify that we want
a certain set of algorithms used. That those are the only valid algorithms that
should be used for this verification, rather than just using any algorithm. And this way we can prevent that someone can
just use the non-header. Now, it’s a social network, so obviously we
have a home feed. We already saw that. That we can post stuff to. So this is the feed. Again, I’m gonna pop open the dev tools. We can have images in there. And I said it’s markdown, so I can write something
like… Hi, AngularConnect. I’m gonna post this. You can see it’s now bold. And if we go to the network pane, we’ll see
what actually happened under the hood. And what happened under the hood is it just
submitted a normal form post here. I’m gonna resize this a bit. It just did a normal form URL encoded post
request with the cookie that I had, and additionally down here, the form data, which is the actual
data that has been transmitted. Now, if we can do this, the problem is that
attackers can do the very same thing. So if we go here and we perform a so-called
cross side request forgery as an attacker… I’m gonna click here… We see… You got pwned. And if we go back to the feed here and refresh… We see that we were just able as the attacker
to post on behalf of the user a message. And the reason why this works is that the
browser is sort of eager when doing the post request to also send you all the cookies. All right. Here are all the cookies. And that means we’re authenticated. So this was actually the code for the attacker. It’s just a form that is being submitted on
load. And all it has — has the message in there. It doesn’t have any authentication or something. It’s just because this is the URL to post. And it figured out that… Hey, you’re already authenticated. So I’m just gonna send you the cookie. Cool. Let’s see how we can prevent ourselves from
that. Or not. Because… Apparently the attacker also hacked my slides. So let’s first jump back to the slides. Cool. So before we talk about cross side request
forgery, we’ll talk about how my slides were just hacked. Namely, I opened the new window with the attacker
page, using a normal target-blind link, like you would use when you want a new tab to be
opened. The problem, though, is that by default, the
attacker, or whatever page you’re opening, now has access to window.location. Which is not only the actual location that
the page was at, but I can manipulate that as well and set it. So that’s pretty much what went on when I
visited the attacker page. It rewrote the URL of my other page. That’s what the attacker can use to redirect
you to a phishing page. The user will trust the contacts that are
in there and will just interact as if it’s a valid page. The nice thing is it’s fairly straightforward
to protect yourself from that. You should set the rev attribute to null opener,
making sure that the child page is not able to access the window.opener at all in any
browser but IE and Edge. If you want to protect yourself in those browsers,
norefer helps make sure that you’re not able to access the referrer URL, and on top of
that, there’s a script you can inject as well to kind of make sure this isn’t the case. Now let’s talk about the cross side request
forgery again, or CSRF. The way we can protect ourselves from that
is fairly straightforward and can be implemented in a couple of lines of code. When we get the page that ultimately should
host the post request that is being performed, we will transmit random generated nones as
the CSRF token on the cookie. And when the page submits the form, it has
to transfer this value once as the cookie and then either in the request body or in
the parameter or as an HTTP header. And the reason why this actually works and
protects you from these attacks is that the attacker will be able to leverage the fact
that the cookie is being sent by the browser by default, but they won’t be able to access
the value of this cookie. And send it in one of the other fashions. So this way we can protect ourselves from
that time of request. All right. Next up, let’s talk about Little Bobby Tables’
little brother. If you’re not familiar with Little Bobby Tables,
it’s a comic from xkcd, about a family that named their kid a SQL injection, and the school
being upset, because the student records have been dropped. While most people have SQL injections in their
mind when they develop with a SQL database, a lot of people still don’t take access as
seriously as they should, because it can cause a lot of damage, and on top of that, it’s
not really easy and trivial to block. So this is the source code of one of the most
famous XSS attacks. Can anyone guess what this one is? So this is the Myspace Worm, also known as
Sammy Worm, because it was posting something like “Sammy is awesome!” On everyone’s feed and friending Sammy. That was also how they caught him. (laughter) Like, if you attach your handle, that’s sort
of difficult. But Sammy used a bunch of interesting tricks
to circumvent the XSS protection that Myspace actually had. So first of all, he wasn’t able to insert
any script tags, but he could use the background URL of a div tag and then set the URL to a
JavaScript protocol and then put in stuff there. The problem was he wasn’t allowed to use quotes. So where do you write your code? You just put it into a different attribute
and read that one and eval it. And on top of that, Myspace kind of blocked
a couple of keywords, like inner HTML. So he circumvented that by using eval and
string concatenation. Because in… n… er… HTML is not the same as inner HTML. So he was able to circumvent that. So you might think: All of that is caused
by eval, so I should just block eval, and that’s fine. The problem is JavaScript is really great
in writing obstrusive JavaScript. So all of that is the same basically as eval. This is a project that I’m not sure I can
mention by name, but it’s a project that allows you to generate any sort of JavaScript with
just six different characters. And this is valid JavaScript. There’s no transpiling or compiling or whatever. You can just copy-paste that into your console
and it will just work. So the bottom line here is that blocking XSS
is not trivial. And in fact, you might have guessed it — our
page actually has an XSS vulnerability as well. So I’m gonna open this again. And now I said I’m sanitizing stuff because
I’m using a markdown library called Marked, and that one has a sanitizer, or a secure
method. So posting something like this doesn’t work. Now, the thing is: There is a bug in the version
that I’m using. That allows us to do something like… Click! And then we can write something like JavaScript
alert… Hello! Now, this by default won’t work, because the
sanitizer is still smart enough to grep this and be like… No, that’s not valid. The great thing, though, is… Or the not so great thing… Is that if we use encodings here, and use
the equivalent here, for closing bracket… Then this will still not directly work, because
it will still figure out that JavaScript ampersand hash 57 semicolon means JavaScript column. But there’s this interesting feature… If I modify this to this, the browser will
be like… Hey, you wrote ampersand hash 58. You forgot the semicolon. That’s fine. I’ll put that in for you. And that’s a call-in. The rest is just valid JavaScript. If I click this, and we click this link, we’re
able to XSS this attack. This has been fixed by the folks from Sneak,
so this is not existing in the current version of Marked anymore. There is still a vulnerability around data
URLs, though. So if you’re using Marked, you might want
to be aware of that. Cool. So bottom line here is encoding can be dangerous. Because there are a million ways now to write
my code and not just one. So especially considering quirkinesses like
the one that I just showed you with the browser. Let’s talk about another thing called JSONP. JSONP, or JSON with padding, is another way
to circumvent origin policy. If you’re trying to grab data from a different
domain, dynamically or asynchronously, you would commonly use that. I think jQuery had a wrapper that allowed
you to do that immediately. But what this does is it exposes a global
function to the global function scope. In our case, we called that got_post, that
should be triggered when the data has been received, and we inserted a script tag that
has a callback parameter where we name what the function should be, and your JSONP end
point will take the data and wrap it into a function call with that name, and this is
how we can access the data, because when the browser receives that script, it will parse
it and send that data. The problem is, if you poorly implement that,
and you already have an XSS vulnerability, like I do, now it’s getting dangerous. So let’s jump back into the page here. And the code that I need now is a bit longer. How many of you have attended the service
worker talk earlier? So you all know how service workers work,
right? So service workers are great. They allow you to write offline-capable applications
and do all sorts of stuff in the background, while actually having the page not available. And it allows you to cache stuff and overwrite
the network requests. Well, what is it if we installed one? Now, service workers actually have to be installed
on your page. Service workers can only come from your own
domain. Since we have a vulnerable JSONP implementation,
we can actually just do a POST request — go to the URL here that is vulnerable and pass
as a parameter the JavaScript we want. And then we finish it with //, which will
just comment the whole data afterwards. Because we’re not really interested in the
data. So I’m gonna click this, and then I’m gonna
click this link, because I’m super interested what’s happening. And nothing really happened. Or did it? Because if we go to application, we’ll see
that we have our very own service worker installed now. This is the service worker. If I prettyify this, you can see what it actually
does. If we make this a bit bigger… So basically it will respond to ever request
with “hacked!” Insert our own script. And then set the content type to text HTML. And if we then… And comment out all the data, because we don’t
care about the data. So let’s refresh this. We’re hacked. This is obviously a very blunt attack, because
we wouldn’t want to override the whole page. But we could just fetch the original data
and append our own script to it. Trying to detect that as a user is not that
straightforward. How many people actually check which service
workers you have installed? I recently clicked on this beautiful thing
here. Let’s first unregister this. I clicked on this show all. And it shows a bunch. I’m not really sure if a lot of people read
the whole source code of that and what it actually does. So this can be a very dangerous attack that
is sort of hard to recover, because you would have to tell your users to go into a service
worker and uninstall that. That’s not the most reasonable thing that
you request most users for your application, unless they’re programmers. Because I tried to explain… All right. Open the dev tools. Cool. So luckily, there’s a hero in town that can
protect us from some of these XSS attacks in your application, and that one is called
content security policy. That one solves a bunch of our problems. If I open this page, which basically is the
same page, just that I’m inserting a bunch of security measurements here, and now we’re
clicking this link again, which should be alerting, but it doesn’t… Some clicking, doesn’t do anything… And the reason is… If you open the dev tools and go to console,
we have a bunch of messages here that say that the content security policy was violated,
the images won’t load anymore because I’m loading from Twitter, and that’s not a valid
URL that I whitelisted, and it’s trying to report this these were actually broken. These policies. So how does content security policy look like? It’s just an HTTP header, or alternatively
you could transfer this as a metatag if you don’t have access to setting the HTTP headers
yourself. And then you define a bunch of rules to play
by. So default source is something that the browser
will just fall back to, if it can’t match any of the other rules. In this case, we’re using for script source
and style source — we’re using a nones, to make sure that we can’t just load any JavaScript. We can only load JavaScript that has an HTML
attribute called nones in there that has the exact value you see on the screen. You can’t hard code this. This should change on every request. I’m not allowing any objects here, because
who still needs objects. And then the image source can only be self
or API.adorable.io. And yes, that’s also a real website. Font source can only be self or Google Fonts,
because that’s what I’m using. I’m blocking all mixed content to ensure there’s
no mixed content loaded, and I’m specifying a report. You can set this to report only — if you
want to gradually introduce this, you can run it in report only mode, and once you don’t
have reports anymore, you can swap this to fully actually blocking the things. The important thing to keep in mind, though,
is that CSP should not be your security strategy. You know, like, seeing all the vulnerabilities
that we’ve seen before, most of them are caught with the CSP. At least, all the XSS vulnerabilities. But that doesn’t mean that you can just, like,
throw in CSP and you’re good. First of all, writing a really secure CSP
header can be tough. Because you will quickly realize that you’re
relying on a bunch of external sources, and you’re like… Should I use a nones or a hash or should I
actually whitelist the domain? If you whitelist the domain, suddenly there
might be other vulnerable resources that can be loaded from there. There’s a great paper about that, that I’ll
link to later. How to write it safely. But you should always try to fix the vulnerabilities
that you stumble upon. CSP is more there to catch you, in case you
missed one, or one of your dependencies introduced an XSS vulnerability by accident, and until
it’s fixed, CSP has kind of got your back. Other things you should look out for you. You should avoid clickjacking by disallowing
framing. Set your HTTP header and your page can’t be
loaded in an iFrame or object, so attackers can’t overlay that on their own UI to make
sure that they’re kind of intercepting your clicks. Don’t show the versions of the frontend libraries
you’re using or the server version that you’re using. Because while this might be nice for you to
debug things, it’s also really easy for the attackers to look up what vulnerabilities
are known for that version, and then explicitly attack you, based on that. And check for the types of input that you
have. Because if you, for example, expect a string
to curry for it in your NoSQL database and you get an object passed and your buddy parser
is happy to pass that as an object, you might be subject to a NoSQL injection that you didn’t
realize existed. Other things you should do… There’s so much around security. This is by far not everything. So you should consider security audits, especially
if it’s a new code base that was never subject to a security audit and especially if you
don’t have a security expert on your team. Hire someone externally to actually penetrate
your application and figure out what’s wrong with it. Stay up to date with the versions of dependencies
that you’re using. GreenKeeper is a useful tool for that. And there are tools like Sneak that allow
you to monitor your dependencies for known vulnerabilities. I personally like Sneak because it provides
patches for libraries that might not be patched yet by itself. If you’re using Sneak, for example, and the
Marked vulnerabilities, like the data one that is still existing in the dependency,
is actually being fixed by this. All right. Let’s summarize what we covered in the last
20 minutes. Use signed HTTP only cookies, rather than
just sending your cookies bluntly. If you don’t need to access this in JavaScript,
there’s no reason to make it acceptable in JavaScript. Be skeptical of JWTs, don’t just accept whatever
is being sent. Always remember — no opener, no refer. It’s better to always have that and then remove
it when you actually need to access window.open for whatever reason, than just not having
it and letting people kind of attack it. Use CSRF tokens whenever you have a request
that can modify data or something like a POST request. Always use CSRF tokens. Blocking XSS is not trivial, by all means. Especially because encoding can be a pain. There’s so many ways to express the same thing. Be careful of JSONP. Always make sure that you’re regexing the
callback name, that it can only contain word characters and not random stuff. Use CSP as a safety net, and stay up to date
with libraries that you’re using. If you want to look at the slides, I put them
up at this URL and I’ll tweet about them after the talk. The application I showed on this page — and
there’s documentation on every vulnerability I showed. If you find any additional vulnerabilities,
I’m happy to merge the documentation into it. Please just create a pull request or an issue
or something. Because I believe that security is about sharing
what you know and learning from others so we’re not doing the same mistake all over
again. With that, I would like to thank you all for
your attention. I’ll be around for the rest of the day. I think we have some office hours later, if
you want to drop by there. Or else you can find me at the Twilio booth. And thank you for your attention. (applause)>>Thanks so much. That was very informative. So… Up next we have Minko. We’re gonna be in this same room. Just give us a couple of minutes to switch
round and we’ll be back. Thank you. There are more seats in the front, if you
want to come forward. Welcome back, everyone. There are still a few seats I can see. There’s a couple over here, if you want to
make your way forward. Up next is Minko, the co-founder and CTO of
Rhyme.com. He speaks about Angular and computer science,
and he’s here today to talk about Purely Fast. Let’s hear it.

Danny Hutson

1 thought on “Introduction to Web Application Security – Dominik Kundel – AngularConnect 2017

Leave a Reply

Your email address will not be published. Required fields are marked *