Attacking the front-end: Modern-day client side security
Introduction to Modern Day Client-Side Security
Kaif Ahsan opens his talk titled 'Attacking the Frontend,' focusing on modern web applications' security, particularly in the context of frameworks like React and Angular. He aims to explore both the security attacks and the development best practices to mitigate these risks.
Digital Transformation and Security Concerns
Ahsan discusses the rapid digital transformation, especially during the COVID era, which led to an increase in online presence and consequently more opportunities for cyber attacks. He emphasizes the evolution of security challenges in modern frameworks.
Code Injection Attacks and Their Persistence
He delves into the persistence of code injection attacks, one of the oldest types of cyber threats, and the inadequacy of handling unexpected data inputs in many applications.
Cross-Site Scripting (XSS) in Modern Frameworks
Ahsan focuses on cross-site scripting (XSS) attacks, particularly how they occur in frameworks like React and Angular. He gives an overview of XSS, its impact, and common scenarios where it takes place.
XSS Vulnerabilities in React Applications
The talk then moves to specific vulnerabilities in React applications, discussing aspects like user-submitted HTML, escape hatches, and handling URLs, which can lead to XSS vulnerabilities.
React Escape Hatches and Safe Practices
Ahsan explains React escape hatches and recommends best practices for safely using React components and avoiding common vulnerabilities.
Code Injection in Angular and Security Measures
He compares React and Angular in the context of code injection, highlighting Angular's superior handling of such attacks and discussing best practices for Angular security.
Cross-Site Request Forgery (CSRF) Attacks
Ahsan discusses CSRF attacks and their potential impact on web applications. He outlines when and how these attacks can happen and the importance of CSRF tokens in preventing them.
Third-Party Dependencies and Security Risks
The presentation shifts to the risks associated with third-party dependencies. Ahsan highlights common attack vectors and the importance of being vigilant about third-party code used in applications.
Conclusion and Further Resources
Ahsan concludes his talk by providing further resources for those interested in client-side security, inviting the audience to explore more in-depth content on the subject.
Welcome everyone.
Like I was telling you, I promise you this is not a security conference.
This is actually a dev conference and this is the third talk back to back.
So welcome to my talk, Attacking the Frontend, where we'll look at modern day client side security.
And in this talk I invite you to come to a journey.
And what's the journey about?
It's about to see how modern web applications, especially React and Angular, gets hacked these days.
And not just the security attacks that happen, but also what the previous speakers were telling.
What can you do about it?
How can you do better?
So not just exploring the attacks, but we will also see what kind of development best practices we can do to protect ourselves against it.
And last but not least, some just general resource sharing and Q& A.
So without any further ado, let's get started.
A very quick intro so you're not talking with a stranger, won't speak too much time.
This journey is going to be more like a road trip rather than a full Europe tour, cause it's 30 minutes.
I'm only gonna say, similar to you guys, I was actually once a developer, and then I fell in love with security, and then moved over to security around three ish, three to four years, years ago, and have been with security since then, but I still haven't figured out what do I love more, coding or security, and do both in my day to day.
Yeah, with that, let's actually jump onto the talk, and the first thing I want to do is set the landscape we're talking about.
In the past, year or so, especially, during COVID, when we were locked in our homes, we saw a rapid digital transformation, because everything and anything needs to have, online presence.
And with more online presence, there are more things that can be attacked.
I'd like to pose a question to the audience.
Can I see a quick show of hands, how many of us remember, building a website with just pure HTML, CSS and JavaScript, Just a quick show of hands.
Quite a few of us, right?
Fast forward today, we have so many different frameworks and languages and other things and technologies that are helping us do so.
In that, we've definitely had a lot of progress where a lot of these new modern frameworks come with built in protection, but we're going to see how some of those old vulnerabilities still haunt us as well as some of new ones that are popping up left, right, center.
And the first attack we still see to this day, and it's almost as old as time, is the code injection attacks.
And imagine this scenario where you have a website where you have a text box and that text box is just a search bar.
You put some text in and it gets run by a website and they show you all the products.
That is an attack surface, quote unquote.
But what if, instead of a text, a user puts in a code?
How would your website handle that unexpected data?
Chances are, in a lot of cases, it isn't still handled well, and that's one of the biggest reasons we still see code injection attacks.
I think that coffee has finally hit my system.
And the most popular type of attacks we still see is something called cross site scripting or XSS.
And in the next few slides, I'm going to focus more on how XSS happens in React and Angular.
But before I dive into that, I want to get a read of the room again.
How many of us already know what is an XSS attack, how it happens, and how to protect?
Quite a few.
Actually, quite a few.
That's awesome.
But I still saw some hands didn't go up.
I'll very, quickly go through what is XSS, how it happens.
Because if you don't understand that, it doesn't make sense how to go talk about how XSS happens in React and all.
Bear with me the folks who already know.
This will be very, quick.
But, in short, what XSS is when you put code, when the user puts code into your website and because of a vulnerability in your website, your website basically runs it.
It's as simple as that.
And with XSS, it can be a minor nuisance like someone defacing your website, to things like installing malware, to all the way full account takeover of the users as well.
So it can be very, drastic.
Let's have an example of how it can happen.
Let's say I'm a bad guy, an attacker, and my goal is to steal the user's session tokens or cookies.
And I found this vulnerable website where basically the code I can put in runs.
So I can craft a malicious URL like this one where basically in the URL I'm asking the browser to run some code in that search bar.
And trick the user to click on this link.
And when it runs on their side, if they were already logged in and had some sessions stored, that session, based on what code I have and what kind of protections you have, that token will then, be transferred to the user.
And then they can log in as that user using that session token and do all kinds of bad stuff.
That's just a very toned down example of XSS.
And it does happen still in the wild very often, even though we are getting better.
So some of the biggest bounties I've seen are from XSS and some of the most black mirror type of issues I've seen is also from XSS where people can spoof your, or get hold of your location and all.
And one of the things I want to convey is XSS can happen everywhere and anywhere.
When I talk with the developers, our common understanding is it will be mostly in the login or authentication page or common things like the navbar.
This is just one example we have from in Atlassian.
Some of you might be familiar with one of our products, Bitbucket, and it's basically same as GitHub.
And when you're doing a pull request in it.
In that pull request pop up bar, we used to have XSS.
We fixed it and we can talk about it now, but that's one of the, weird locations.
You don't expect code to run, but it can run.
Now, XSS and React.
That's the juicy part.
Let's get into it.
But before diving into it, can I, ask the audience, I'll be asking the audience a lot of times so I have apologies in advance.
What do you think is the best way to prevent a command injection attack like this, between these four?
Would it be doing some sort of encoding, data validation, context sensitive, output encoding, or, enabling CSP or content security policy?
Anyone want to shout out between the number one to four?
Four?
One?
Blast off?
Cool.
The right answer, again, this varies from context to context, but the best thing you can do is context sensitive output encoding.
Because when you're doing output encoding, that's where you exactly know where the user input is going and what kind of context it will be running on.
And that's, why you want to run it.
Number one doesn't do anything, it's just that.
Number two is, yes, it's useful, but trying to do, data sanitization is extremely hard, so you've got to be careful, and it's usually paired up with output encoding.
Number four, again, is awesome, but by itself it cannot protect against XSS if your app has vulnerabilities.
We saw context sensitive output encoding is the best thing you can do, and React does it out of the box.
So in this example, if you try to, this is just a dummy React application where, which has context sensitive output encoding turned on.
And if you put a code there, it won't run it.
It will just output it as a string.
It'll, be like, Oh, that's code.
That's not my domain.
I'm not going to run it, which is awesome.
Does that mean React, you can't do XSS and, thanks for attending my talk.
Unfortunately, no.
The most common ways we see XSS happen in React, first, is when you're trying to do, user HTML, user Submitted HTML output and you don't encode it properly or sanitize it properly.
The second thing is something called escape hatches.
And last but not least, certain HTML elements like URLs when they are not handled properly.
We're going to dive into each of them one by one.
So let's look at the first attack vector.
Okay, cool.
Outputting user HTMLs, through React.
React gives this functionality called DangerouslySetInHTML, and obviously the name gives it away.
So if you do want to take input, HTML input from users for all variety of reasons, and I'm not just talking about taking raw code, there's multiple ways users can be giving HTML specific input to your application.
You guys probably know that better than me.
And you want to output it somehow.
DangerouslySetInnerHTML is the React default way of doing things.
And by itself, it has no protection.
Which means if you give it some code and some HTML, it will actually run it and output that raw HTML.
That's alright.
That's not too bad.
Because fixing it is also easy.
You just put any known good sanitization library, it's usually a third party library, you chuck it in and you output it there and that's fine and dandy.
The issue really becomes, and that's one of the things I, I have with the React, with React is if you go to the React's official website, whoops, I've gone through two slides, and you look up, and when you research how to, output, HTML through your React application, chances are you'll go to the official DangerouslySetInnerHTML documentation.
And this is just a screenshot from the actual React website.
It tells you that yes, DangerouslySetInnerHTML is bad.
It can lead to cross site scripting attacks.
But it really doesn't give you an example of how actually to do it properly.
So it lets the developers pretty much figure out on their own what should be their remediation.
And in a lot of cases I've seen where, okay, we see it's bad, let's make our code run, let's come back and probably we'll figure it out later on how exactly to do it.
Now sometimes people actually come back and do it and sometimes they don't.
If you search GitHub with just the, text dangerously set in your HTML, you get more than one million hits.
Now, are all of them secure?
Maybe, or maybe not.
So it really is up to interpretation, and you'll find so many dangerous stack overflow code blocks where they haven't done any sanitization or anything done.
So that's where one of the, one of the challenges lie.
So how do we protect ourselves against it?
Thankfully, it is, known to be a very dangerous function.
So most security tools will pick it up, when you're running it.
But a challenge we have faced is because it's so dangerous, even though you have sanitized it, a lot of popular tools will still give you an alert.
And if you're doing that sanitization in lots of places, you have, there's a chance of lots of noise and lots of false positives.
How did we deal with that, high noise situation?
One of the things we did is, created a custom React component where we have put the, HTML handling and made sure that the, HTML input is properly being sanitized with the correct library.
And then we have given that component to the devs that, hey, instead of trying to implement DangerouslySetInnerHTML yourself, go, use this component everywhere and it's, we have that well documented and if you go to our XSS page, you will find this.
And the best part is because this is in only one place, the security scanner only picks up that specific file.
You won't see 10 different alerts for the same issue because that's wrapped up in one neat component.
And if you suddenly see DangerouslySetInnerHTML pop up somewhere else, that other than that, let's say supposed safe HTML component, that's an alert for you to go check it out that, hey, someone's going away from that paved path and we should probably have a look at it.
Cool beans.
Let's look at some more ways to hack React.
Does anyone know what is a React escape hatch?
Out of these four, is it a way to enable cross origin flows?
Is it a way to access the native DOMs?
Is it a way to access the DOM securely?
Or is it encoding?
Number four?
Yes, I see a few number fours, and that's correct.
So escape hatches are a way to bypass React's mechanisms and basically directly access the native DOM APIs.
And for obvious reasons, that is one of, also one of the dangerous things you can do, but because then you're bypassing the default output encoding, context sensitive output encoding React does, and Directly handling the DOM's APIs, which can get very, dangerous.
One of the good news is, and I apologize, I know I'm not giving enough time to read and process the code.
We don't have much time, so I'm pacing myself a bit, but, the slides will be up there.
Thankfully, some, elements like FindDOMNode is being deprecated, but things like CreateReference is still gonna be around.
CreateReference by itself is not dangerous, but it's how you use it.
There are some alternative approaches like using innerText instead of innerHTML, which is, recommended, and also most static analysis tools do pick up escape hatches when you use it.
Cool, more React XSS vectors.
Which one of these do you think can be, HTML elements can be used to run code?
Is it URLs, CSS, markdown, or all of them?
Exactly.
All of them.
That's one of the, another big issues we have seen where people don't suspect, a traditional HTML element can run code, but it actually can.
For example, one of the biggest ones we see is URLs.
In these two React blocks where you're dealing with URLs in an insecure way, if you chuck in a, a JavaScript URL there, which is still a valid URL, it will actually run that code.
That's one of the things to look out for.
The good news is, JavaScript URLs is again being blocked on the newer versions of React, but a lot of other things like resource URLs is still being able to run, and that is something like data dot dot dot dot is gonna still gonna run, so that's, not good.
Cool.
How do we defend against that?
One of the, this is probably a more hardline approach, but if you're dealing with some specific URLs, you can probably make the users give input part of it.
For example, if you need to embed a YouTube video, instead of taking the whole YouTube video link, just take the video ID and hardcode the rest of that URL so you know that nothing funky can be done.
But we do understand that many applications, it can be that rigid.
So that's where URL sanitization comes in.
But one of the biggest flaws we see with that is trying to do a blacklist rather than a whitelist.
So when you're trying to do URL sanitization, it's better to do a whitelist of good ones rather than trying to know which ones are bad and then block them, because attackers always find a way around.
A very 10, 000 feet overview.
We talked about, doing proper, input, handling, not using escape hatches and dangerous, HTML elements.
So there's some, just some resources.
If you search React Vulna or React Suspended, those are some deliberately made, deliberately vulnerable React apps you can play around with.
One quick note that I haven't, covered trusted types in React.
That's one of the other cool things you should be looking at, because, we just don't have enough time.
Move on forward, chug, chug, chug.
What about Angular?
Turns out Angular is actually better than React when it comes to code injection attacks, because Angular not only does output sensitive, context sensitive output encoding, but it also can do input validation when, where you need it.
And applies auto escaping.
What do I mean by that?
I'll give you a very quick example to explain it.
So let's say when you use Angular's innerHTML property, this is different from the DOM's innerHTML, and you put some dangerous code, it will automatically will remove the dangerous code and only keep the safe HTML which is secure to run.
And it also will give you an alert.
Like here, when it has removed something, it thinks dangerous.
But, when you need, you don't want to run any code at all.
You can use Angular's interpolation method and it will then make everything a string.
But again, if you want to run some HTML and not run some, then Angular comes out of the box to get rid of the insecure ones.
Which is, which is, great.
That means you're not fiddling around things like dangerously setting a HTML.
Does that mean Angular is also good?
It's all rainbows and sunsides.
Unfortunately, again, not.
There are a few things, ways, XSS can still happen in Angular, but unlike React, you really have to be trying.
So things like using the bypassSecurityTrustHTML function, which obviously sounds very alarming, is a key thing.
Similarly, there's other ways of reacting the native DOM element, such as the element reference function, or the render to API.
These allow you to directly handle the native DOM, which again, is quite risky.
One more thing with Angular is certain versions of Angular is prone to this attack called a template injection attack.
Again, due to time I'm not going to dive too deep into it, but when you're using Angular you definitely need to check what kind of versions you're using and whether template injection is a thing for you or not.
Very, quick 10, 000 feet of protect, how to protect your Angular apps.
By default, Angular does a better job.
So you stick to Angular's default ways and you would be fine.
Avoid using custom DOM manipulation unless you really, have to.
And if you do have to, please know what you're doing.
And last but not least, protect against template injection attacks.
Let's move away from code injection and let's look at something different.
In this, scenario, we have Bob, whose website, has a functionality of storing their, phone, user's phone number.
And you can, recover your password using that phone number.
Someday, one of Bob's friends come and say, Hey, I was using your website, my number has actually changed, what happened?
If your website is prone to something called the cross site request forgery attack, an attacker can craft a malicious request from their site and change certain things in your website, which is, again, something we don't want.
And it can lead to things like full account takeover, like TikTok had earlier this year.
Although that meant no dance videos for a day or so, which I'm sure a lot of you were sad, but they fixed it quite quickly.
When does a CSRF attack can happen?
When you have a state changing action, like updating your phone number, changing your name, etc.
And you're doing authentication, not authentication, sorry, session management purely based on cookie and nothing else.
And there's no unpredictable request parameters as well.
That's where the attacker can craft a malicious page like this one, where in their malicious page, they have hidden a form where when the user visits that form and it will basically send a request to your website using their tokens and or cookies and if your cookies is stored and tokens stored on the browser, will just, okay, this request is going to malicious website XYZ, I'm just going to fetch that cookie that is stored there, plug it in and make that request happen.
How do we protect ourselves against that?
So there's this fabulous thing called CSRF tokens.
It is unpredictable.
It is associated with user sessions.
It has a limited lifespan and it is action specific.
It seems like I'm trying to sell a tool, but it's not.
CSRF tokens come by default with most of the React and Angular frameworks.
But the important part is turning it on.
By default, CSRF tokens are not turned on.
So for your application, you do need to go and check it out.
Apologies for the rushed nature of the talk.
This is not...
yeah, this thing is giving me a hard time.
It doesn't work that much.
Anyways, a quick shout out that CSRF tokens can only do so much.
If your website has XSS attack in the first place, then CSRF tokens can't do much.
It's, already a god case.
What kind of CSRF tokens to use, based on whether the application is stateful or stateless.
There can be various kinds.
And, yeah, the names are up there.
Sorry, you're going to rush slightly.
Cool.
Some strategies Atlassian has taken to protect against CSRF.
We let no state changing actions happen through get HTTP methods.
Yes, I know that some people still try to do it, but that's why your protections are not fall through.
So please, no state changing requests using get.
Similarly, having the sameSite attribute turned on is fantastic.
Last but not least, we make sure on a proxy level, or, sorry, on a gateway level that all requests have a valid CSRF token, no CSRF token, no request, that's a hard policy.
And last but not least, CORS, cross origin requests off, so to ensure we have a layered approach to security rather than, I know I just said a bunch of jargon and not explained them much.
Apologies for that, but, you can always go back to the slides and maybe take a picture and look them up later.
Cool.
We have talked about our protecting our own code, but what of other people's code?
Because as we know, as we're developing sites, we, are pulling in stuff from all kinds of places.
And this is one of the, favorite images I have about third party dependencies.
Again, a very quick question.
I know I'm going over time, so I'm just going to take one or two more minutes.
If you're using a third party dependency, you're just, if that third party dependency gets hacked, you're just as much impacted.
And in the past, how many of these ways do you think a NPM dependency or a PyPy package has been popped?
Did the attacker hack them by asking nicely to hand the package over?
Did they buy the package from them?
Did they socially engineer them and trick them to hand it over?
Or, last but not least, did they, did the package just run out of, got deprecated?
They registered something?
Any guess?
All four.
Yes, you're correct.
All four.
All four ways someone has managed to pop an npm dependence, npm package.
And the funniest of all is probably the most ironic where the attacker pretended to be a genuine contributor and then said that, hey, your, dependence is awesome.
Can I buy it from you and maintain it?
And the guy was like, sure, but I don't get any money.
I don't get paid for it, let alone much recognition.
Here you go.
Next thing like the previous guys were showing, Monero is running and they're mining Bitcoins.
I'm actually a bit disappointed that they only ran Bitcoin miners rather than not doing much, which is a bit sad.
But yeah, a third party library is a big risk, and this is just a snapshot of the top NPM packages at that time when I was preparing the talk, and how many vulnerabilities that existed in the top 10, and as you can see, most of them actually had a critical vulnerability or a vulnerability high enough.
Similarly, it's not just...
so how can we protect ourselves against it?
Again, there's lots of ways built in into GitHub, bitbucket that help you do it, but on top of that, there are tools.
I'm not going to mention any anyone specific, but go check it out.
There's lots of great tools out there and it's a whole different subject.
Actually, I have another talk just on this topic in another conference, so I'm just going to leave it there as a next thing.
Utilize frameworks like Salsa or S2C2F.
I know more jargon, but it's "Secure Software Consumption Framework", and it's by Microsoft, they have open sourced it, fantastic.
These can really guide you a lot.
And my hot take is, when you have a dependency, staying actually one version behind the bleeding edge is often something we recommend because you never know where that latest version came from.
It could be a genuine thing or it could be a Bitcoin miner.
You really don't know these days.
Very quickly, it's not just consumption, it's also when you're producing it.
If you're an open source, package maintainer, there are things you should check out.
This article is awesome.
I haven't maintained any, so this is not my forte.
I'm not gonna talk about it, but do check it out.
What's next?
I'm gonna Skip over these because she's getting nervous, and these are some holistic ways we are approaching security.
Just some shameless plug, if you like this and want more security content, I, just host a YouTube channel where I talk, me and my mate, talk about security and just do different experiments, Go check it out if you're interested, I'll leave the link there.
But yeah, thank you so much, for listening to it.
Thank you very much.
It was, I learned, I think someone wanted to take a photo.
I'll let you take a quick photo.