Video details

Liran Tal - You thought your React application is secure? Think again | ReactNext 2021


Israel's Annual React & React Native conference
You thought your React application is secure? Think again
Modern frontend frameworks like React are well thought-of in their application security design and that’s great. However, there is still plenty of room for developers to make mistakes and use insecure APIs, vulnerable components, or generally do the wrong thing that turns user input into a Cross-site Scripting vulnerability (XSS). Let me show you how React applications get hacked in the real-world.
Liran Tal Developer Advocate at Snyk
Liran Tal is a software developer, and a GitHub Star, world-recognized for his activism in open source communities and advancing web and Node.js security. He engages in security research through his work in the OpenJS Foundation and the Node.js ecosystem security working group, and further promotes open source supply chain security as an OWASP project lead. Liran is also a published author of Essential Node.js Security and O'Reilly's Serverless Security. At Snyk, he is leading the developer advocacy team and on a mission to empower developers with better dev-first security.


Hi. So we're going to get started really quick here. And I guess everyone coming here are building re add applications, but they're not always secure. So let's get to it. I know what some of this may be going on your head. You might be thinking, Leron, this is 2021. We're almost at 2022. Why are we talking about security in React applications? Why are we talking about XSS with React? Because that's mostly secure. Well, for that, I say hello, my old friend XSS snippet of code from Twitter earlier this year. Probably a new person coming to React. If you haven't spotted the mistake, here it is. If you're not entirely sure why that is wrong, we're going to look at some examples of how this is very much of a sync to an XSS. So with that said, my name is Darontal. I'm a developer advocate at Sneak, where we give and build free tools to developers to build applications securely. I'm also doing a lot of web, JavaScript and node related activity for the Node Foundation security for OWASP. Recently a GitHub star as well. So if you want to catch and talk with me about anything, I'm on Twitter and today we are talking about a section security. I'm going to say React is mostly secure by default. And at this point you were all probably saying, what do you mean? Mostly I thought React is secure, right? Front end, modern, front end frameworks. But to understand what does it mean, we kind of need to break it up to two things. First of all, what does it mean secure by default? And secondly, we need to kind of understand what are cross site scripting or XSS attacks. So let's do a quick alignment of what does XSS look like in the browser. If you have this kind of piece of code in React, you are rendering something to the screen. You have this probably user input of a first name potentially containing malicious strings, like a script or something, some other kind of payload into the page. What happens when you put them together onto the page? This is going to trigger an XSS or not? No. Okay. Some of you answer this very well. It's going to look like this, but it's not really going to trigger an XSS. Cross site scripting is not going to happen. Why is that? That's because those special characters that the Dom would then translate to create new Dom nodes on the page are not really the real characters of like the angle, the left and right angle brackets. They're actually a representation of what they should be. And so when we say React is secure by default, what we basically mean is that React takes those characters, translates them into something that's called an HTML entity, which is how we represent them onto the page. And that's how this is basically created and giving us the protection that we need in the security professional linguistics. It's called output encoding and that is the way that React is secured by default. So when we refer to React is secure by default. This is what we meant as a view library rendering data onto the page. This is it. But I didn't say React is secure by default. When I opened this talk, I said React is mostly secure. So we can have XSS. How do we have it? Well, we're going to go and dive into a real world application that I've built for this talk so we can see and learn how we could be making mistakes with regards to React and security. So what I've built is essentially this web application. It's called an open source package indexing. It is basically like npmjs. So you have this maintainer called Case Libby. She has a few testimonials. You can fund her. She builds NPM kind of packages and you'll see all of her packages in a second. Specifically, it would be nice if for example, I could link some contact information for this maintainer like this Twitter link. Right. So let's get to build something like that, making sure this is big enough for everyone to see. And this is our web application. So this is pretty functional. We'll get you a lot of the functionalities here in a second. But you could see if I open and try to click that link, maybe that takes me over to Twitter and that kind of works to build a simple enough application for us to make this live demo go smooth. I'm not actually making API calls to a back end or database. I simply have this JS database object which I'm importing into my React application code over here. And we can make changes here. And that's basically the way that we're being updating the database in real time. So making sure that we have that and we know that now Kate has those Twitter links over there to the right. What I actually want to do is start with a simple example. Let's say that Kate tries to maybe insert some malicious characters like maybe she changes her name here from Kate to this sort of payload. And now you can see that Reacting did have securely created this data on the page, right? It created output encoding over here, over here. And that is why the XSS did not work. That looks okay, let's try changing other things. Essentially we have this link here. What I can do with the link, which is database Twitter link over here is something similar, right. Since I control this link, I can link to any web page I have on medium and Dev dot two. I can put whatever payload I want as well. What happens if I put JavaScript? Potentially React is supposed to save me, right? Like this is user input Friender to the page. So we have now created an Accessor to the page. So one important thing already to remember and understand is React does most of the things securely. It does not go into Href attributes, values and things like that. So any input goes into there. We kind of need to take care of it. Let's try to fix that. Let's say we've kind of known about this and we want to fix and escape this whole thing. So going to put a bit of code into this example. And what we'll try to do is detect the fact that we have JavaScript starting as part of this payload and then we'll replace it with something else, initialize it with this and create a quick one to update it. So we'll say if this Twitter link has JavaScript there somewhere. Well, not somewhere. Basically it starts with JavaScript because I could have JavaScript as part of my input of a website. What I want to do is change that to this. Hopefully that works. Let's take a look and still have an XSS because we probably haven't used. Okay, let's try again. Okay, so we've now essentially fixed this problem and we do not have an excess anymore due to links. Right. This is just not doing anything with the link except what happens when someone then adds simply changes it, right? Upper case, lower case. We haven't fixed that problem. So if I do that refresh the page, XSS comes back. What do I do to fix it? Awesome. Lower case, let's fix it. Lower case, reload fixed it. Ready to go to production. A lot of yes here, a lot of yes. Okay. What else could go wrong is input is not what we've expected it to be. First, we didn't expect it to be a JavaScript. Secondly, we didn't expect it to be maybe upper case or lower case. Are we writing security testing, for example? Security unit test? Probably not. Another thing that could go here is control characters. If I use these control characters as an input, if I'm able to somehow inject it, this whole lowercase index of just doesn't see it. And at this point I have XSS again. Okay, so we did this small exercise to also understand how not to fix problems, which I'll get to in the takeaways. But this is one example, first of all of how XSS gets into react applications. So let's remove this and follow up with okay, good. So some takeaways here. First of all, reacts is not including attributes. Like we said, be careful what you put into these Href values. Sanitize it when you can. Do not implement denialists like we try to do. Do not implement if it has this character, that character. This blacklist denialist is not a good way of doing it because payloads will always change and you would need to change with them. This is why this is a bad idea and you should learn from it. Next up, since this is an open source package index thing like npmjs, maybe what you want to do is it would be nice if I would go into a package registry thing and I would see maybe the package JSON, maybe just the scripts in it because it's like handy and I can see what script already has. So let's build something like that if you want to do it. If you want to build those kind of fancy eyes, I'm not going to script the whole thing and make it themable. And all of those layouts and indentations, which are pretty cool way of displaying and printing JSON to the page. I'm going to use the library three steps, react JSON pretty. It's a pretty downloadable package, has I think 40K downloads a week. So it's pretty used. I'll import it to my application and easy enough. And how to use it, I'll just import it as a component past all my package manifest to it, which is essentially just a JSON. And that's it. I'll have this sounds simple, right? Okay, let's get to coding. You are going to use some packages here. So let's see how that works. First of all, I have my package parser here, my own component that uses the JSON pretty NPM package, and then I have it here. So I'm basically just using that to render that to the page, giving it database package manifest, which if we scroll down a bit here, we'll find the package manifest. So as you would expect, it's pretty simple. Json. What I could do with it is, first of all, if I Scroll a bit here, it has the payload. So as one way of trying to inject excess here is of course, since Kate again is the one in control of her Twitter, her first name and this JSON, what I could do is I can change the package name to something else. Maybe I will change it to something malicious like this. If I try to do that, it doesn't work. Doesn't work. Why? We'll see in a second. But essentially this is also encoded correctly to the page. Now, I could try and be a bit smarter about it and maybe say potentially, maybe there's a code in this JSON pretty package parser thing that actually goes through the fields and sanitizes them, but it has a bug. Maybe it doesn't do it for some fields, maybe it doesn't run recursively on all of my JSON data. So I could try that. But that also doesn't work. So I'll save you the trouble of trying this one. Let's try something else. Let's say that. Let's say that instead of providing this package manifest as a JSON because I'm in control of it, maybe I can just do something entirely different, which the application would not expect actually provided as a string. So the moment I do that, I have an excess. So you could think about an elaborate system where maybe there's like a queue gets messages, get notifications, the data flows to it from somewhere, get ingested, and I can just change my package JSON basically not be a JSON input, right. It may not have JSON content in it, it may just have a string like this. And if my component is not rendering it correctly, I would have an XSS. Why that happens is actually interesting. Right. Let's take a look. Let's bring this back to what it was and take a look at what's going on there. So he said React JSON 3D is actually very much used, 40,000 downloads a week. So that's pretty good download usage two years ago, kind of like a release, probably not that maintained, but we'll leave it at that. Let's see at what is going on and why that XSS happens. If you look at this piece of code, this part of React JSON pretty code base. So if you look, it has this dangerously set in HTML because it has to do some kind of styling, some kind of custom business logic that it wants to apply. Right. It has this HTML part and it feeds the object, which is what we're passing it into this underscore pretty. This underscore pretty takes all of that data and does some stuff with it. It's own magic to basically draw everything out. But it also does what we've seen that React does as well. So it takes care of the data and actually removes any of those potentially malicious characters that could add excess to the page with something else, which is great. That's why our first attempt of adding XSS into the payload of the JSON values did not work. But the flow was elsewhere, right? The flow was here. If I Scroll back up in this version, this is the rendering part of the component. It tries to do a few things like JSON parse it, but if it felt parsing it, what does it do? There's a catch statement here, actually renders all of it, but this time it doesn't actually call this pretty. It does something else, but this does not go through output encoding. So this is where it kind of should be. Mindful of how you also handle your errors. But not only that, because we use a lot of open source packages, a lot of open source supply chain stuff, and then we kind of need to understand the risk that comes with it because we might do things in a good and secure way. Others may not. Let's see how they fix it. Here's the deal. That fixes it. You could see that they essentially added this excess clause here exactly on that catch statement. So if anything goes wrong, that is cleared, if I Scroll back up, it's a pretty simple way of excess escaping everything out definition of what needs to be escaped. And of course, replacing it makes sense. So moral of the story reacts library dangerously set in HTML for it. Bring it in. But the thing is, our takeaways, as we said, right, first of all, avoid dangerously set in HTML. But of course you have to use it for some kind of edge cases, which we'll talk about in a second, and you might want to do it and do it securely, but it doesn't mean that others are doing it as well. So the other takeaway is of course going to be well, if you know you're using open source packages, you should be scanning them for third party dependencies and vulnerabilities that you might be having in them. This is great. That particular vulnerability actually does not exist in NPM audit if you use sneak. So whatever tooling works for you and whatever you end up using, that is great. Make sure you use it. Make sure it has good coverage. Make sure it helps you actually deploy securely too. So let's talk about it for a second. We just said I can use dangerously set inner HTML because that library does use it. As long as I escape it, right? If I escape it, if I do the same thing that react does, I basically had my way with it and I can escape the whole thing. Let's take a look. Using dangerously state inner HTML. That's great. Not terrible. Let's say we all have custom business logic. Like for example, maybe you have a toaster. Maybe you have a toaster message or a pop up. Usually you kind of want to maybe say success created specific data points and you want to have a link, right? Add a link to the record ID or something else. So maybe you have this kind of a bit of logic where you have dangerously set in HTML and provide an image source. And in my case specifically here, maybe you also want to customize the image itself. Like maybe for SEO reasons, you actually need to add Alt text into the image itself that gets rendered. And maybe that is the whole reason for why you need to have your own custom business logic. So what I'm going to do now is we have an XSS function that this library uses, this react. Json previously. So I'm going to reuse that, apply it here and see if we find any issues with that. So I've saved it here from before. I just basically went to this repository, got the excess removed, the whole TypeScript stuff, and this is the exact same thing. Copy paste of whatever React. Json 3D is using. I'll push it here, scroll to the top of it, see where I posted it. We have our excess function. What we want to do next is transfer one of those values that we have here on the page. So I have an image and if I Scroll back, you will see that my database has auto screenshot and auto screenshot description this shows up here. This is our example dangerously sending our HTML. I give it the contents, I have the XSS. So if I do, let's leave it just as that we haven't even used the XSS function. I just use this. Everything works. This is our image. And let's say that we want to add something to it to create an excess payload. First thing I can try is again, this adding this the right way. There we go. So let's say I will try to add this payload as the description that goes over here. If you look at it, there's like an ending of the image tag. It doesn't look really well. It's not exactly what we wanted to do, so it breaks the page. I could actually do something else since I know I'm inside the Altag. What I can do is close the image tag before it like that. Open a new one, a new image tag. I just need to close it in my input because the code is already closing it for me. So if I do that, I have an XSS. So far so good. Like this is what we would expect, right? This is malicious input going into dangerously said in HTML, nothing is escaping it. What we want to do, however, is we just added an XSS escape function, so we actually want to take that and apply it here. And if I do it, it looks like it works. No XSS anymore, because it's probably taking all of those characters that are potentially dangerous or on the right and removing them or not really removing, but actually output, encoding them, displaying them the way that they should be displayed, not the way that they should be rendered to the page. So far, so good. Let's commit and push to production. Yes. Okay, cool. Except there's another sort of payload that will actually work here. What if I can just since this is Xssessed, but I am inside an attribute value so I can escape the attribute value by just giving it any character, for example, then having onload alert one which essentially creates another attribute key and value to it. So my payload now changed and XSS does not protect me anymore. Okay, different payloads create different results here. So while we were thinking we could actually take the whole XSS approach that React does and maybe JSON pretty does and apply it here. This doesn't actually work in practice. There are payloads that would happen and flow to the application itself and put it at risk just to make sure everyone are aware of what I'm using here. This is all React 17. Okay, this is all React new versions. This is not some old version of React. This is the way that React works and the way that our code interacts with React. So what have we learned from this? First of all? Of course, if you can avoid using dangerously setting your HTML, please do because it's there to protect you. Right? The other thing is we've learned about something really important with escaping. Escaping is very much context aware. If you are putting input into a CSS, into JavaScript, into an HTML, whether it's Dom element, whether it's an attribute key value. These all need different escaping. If you're trying to escape URL there's specific URL encoding for how to do that properly so no one else will add malicious content Tinder so understanding that context is part of your input handling is super important to avoid those kinds of mistakes and so that's it hope you enjoyed it. Hope you learned something new. Thank you for coming to my dog.