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.
Let Me Show You How React Applications Get Hacked in the Real-World
Transcription
Hello and welcome to my talk. You thought your react application is secure? Think again. My name is Zerontal and I want to show you today a few gaps that you should know about when you do react coding. So why are we here today? I know what you're thinking right now. This is the year 2021. Is this going to be another talk about xss? What do you want from my life right now with xss and react? Because I thought we got over it by now. Well, that's where I'm going to say hello xss, my old friend. And I'm going to show you a couple of examples that I was just running over Twitter just to find some coding examples of people showcasing some of their code stuff on react. And what are they actually doing there? So here's one example. This is xss. This is this year. What's going on? Let's see. It looks like someone is trying to build a counter, but something's messy over there. Are you seeing what I'm seeing? Because when I zoom in a bit inside, I see the whole fancy use effect kind of stuff. Is it fancy still? I don't know, it's already a few versions into react. But anyway, you can see that there's a count variable that's coming maybe from the outside. We are putting it in. We have no idea what's in it. Is it sanitized? Is it not? Should we output and code it? What does it even mean to output and code things? We'll learn about it in a second. But as you're seeing, there's a mix here of react APIs and the DOM APIs like innerHTML, which is not a really secure way of doing things. And this mix actually causes cross-site scripting attacks or vulnerabilities like in this application. Let's look at another example. I've also seen this case on Twitter, which is also this year, not so long ago. So this person was trying to avoid the runtime evaluation eval function by basically accepting user input as well and running it. Because when you're doing return A plus B and A is an IIFE or a function or whatever, it's going to get run. It's going to get evaluated. So that doesn't really solve the problem either. So we're having a lot of those different things that if we're not using the right APIs, we're not aware of what are the secure APIs or what are the insecure APIs. I'm not just talking about dangerously innerHTML things. I'm talking about other things as well. And you'll see it in a second. So introducing myself, my name is Iran Tal. I'm a developer advocate at Snyk, where we help developers build applications securely using open source. So if you're in your IDE writing some JS6 code, writing some node.js code, Java, whatever that is, we'll help you find it. We'll augment your experience of doing that by telling you real time if you are actually having some vulnerabilities and insecure code writer. Well, this is really cool, but I'm doing some other things as well. GitHub star, activists on web security topics. So node.js, probably send me active there as well. Doing all of those kinds of things. But really, if you just want to reach out and talk about any of this or something else on Twitter, Iran underscore Tal, and reach out and say hi. So let's get on with this. react is mostly secure by default, right? Like other frameworks like vue and angular, most of these get things right on the client side, right? As far as it goes to xss and maybe some other things when some frameworks take a bit more bulky and take a step further as well. But why am I saying mostly secure, right? Like what is going on? Why is Iran on this stage right now saying mostly secure? What is left for me to protect? Because I thought using react actually protects me from all of this weird xss stuff happening to my applications. So to realize that, we kind of need to explore something else. I'm saying react is mostly secure by default. But what does secure by default mean? We need to explore this a little bit. And you need to understand a little bit more of the jargon. And that's where I'm going to just level up and just say what xss is. So everyone here can be on the same level playing field. So when you write this code and render it to the DOM, the user input here, like first name for example, what happens to it? In the general case where this is insecure user input, it could lead to potential vulnerabilities like xss on the web page. But with react, you're kind of safe. Now why is that? And so let's see what happens when we actually put something in like a script alert thing or image source or whatever into this JSX code snippet. Well actually, if you look at the page, you get something like this, which looks like a script or whatever element I would have put there, but it's not. That is because if you look very carefully with your devtools inspector thing into this specific component, specific element on the page, we'll actually see that all of those angle brackets, they're not real angle brackets. They're actually html entities that encode and tell the page that it needs to render the angle brackets to the left and to the right, but they're not real angle brackets. So that is why when you put that into the page as user input, nothing would happen. No script would actually get evaluated, no runtime javascript evaluation because this isn't actual real html element or something around it. And the browser knows this, and this is what I mean when I say react is mostly secure by default. react is mostly secure by default actually means that it knows to encode. So now we have this new jargon telling us what this actually is. This is secure by default if you actually do output encoding by default on any user input. So with that said, with this knowledge, let's move a little bit forward and figure out now if react is secure by default and it does secure output of user input, why are you saying this is mostly secure by default? What is still left for me to protect and where are really all the gaps? So let's dive now in into some live coding together and see what we can learn about some pitfalls. And so for that, I have this build application website where it's, you know, this Kate Levy was building her package profile for open source maintainers and things like that. So it's kind of like the, well, a webpage with packages on it. So let's dive into something like this and see how it looks like. Well, I need my ID. There we go. Let's make sure everything works. It does. And if it does, I should have this one here. Okay, cool. So this is the website that I've just built and it looks like it's working pretty cool, has some elements in it like user input here, sorry, like the name, description or something, link to their Twitter, some more stuff that I can use like testimonials. So there's a lot of user input here that we can use as a baseline of just simulating what could actually go wrong. So let's take a look at this a little bit more. For example, this database has all of the things that I was talking about over here. And this is what we're actually going to use to simulate what could actually go wrong. So I'm going to go and change some of them, for example, just for the sake of things. So I can change this to my name here. And once I do it, you see it auto loads to Liranta over here. That's fine. But what happens if I change it to something like image source X on error alert one. And hopefully that triggers an xss. It doesn't. This is where react actually helps you and secures things by default. Actually outputting codes. This is what we said with the example of the first name. This is why if you try those things and they didn't actually execute, that's fine. That's because that's fine database author name over here is simply something that react knows to now apply output encoding and change all of this into html entities. So so far so good. Let's bring this back to Kate Levy. And let's change something else. Let's try a link. Which is not just a bit of text. Let's try and change this to this is over here. So this is if I go, I just hover it, you can see the browser bottom left. This is going to her Twitter account. So I'm going to change this to let's try and be kind of malicious here. Let's say someone has this free form field and they just build up javascript alert, which is like, again, the classic way of maybe introducing some xss. So if I do this, let the page reload, assuming it does is what you need to like go here and maybe click on it. And we see now an exercise with why, why is it going on? This is actually pretty weird. Like why do I have an xss and alert if I'm actually using react? This is pretty newish version of react as well. Okay, this, you know, 17 and above. So what is going on here? Now to understand what is going on there, let's go back here. The Twitter link. So database of Twitter link actually gets inserted here. And so what we now understand is, well, react does not apply output encoding and protects you from HRF values, like html attributes that go here. And that is something that you should be aware of. So let's try to fix it. If first of all, now you are aware of it and you know that if something is malicious, we need to fix it. But let's see, there are some pitfalls in how we try to fix those things. So let's look at that as well. I'm going to go here and I mean, we can, we can live it at that. And just before around here a bit, we have enough room. And so let's go ahead and try out these whole user factors, just like change and sanitize it together. So let's try something like this is Twitter link. We put it over there. Okay, set Twitter link and use date. I'm going to kick it off with the database. Yes. Okay, cool. Yes, do that. Use effect, use effect and come on. Yes, autocomplete it. There we go. And then we want to find if, you know, not that one. GitHub co-pilot. That's insane, right? Twitter link that index of javascript. Like what do you think about this? Is this a good fix? Let's try it. I like this autocomplete. Go with that one. So basically this says if Twitter link over there on the database that we get as user input has javascript in it, it will actually set it to, I don't know, set it to something like this, right? Just a hash and that's it. Hopefully I didn't forget to close anything. Looks good. I think it looks good. Let's try it. Did it reload? No idea. Let's see. It didn't reload. Did reload. I'll reload. There we go. So no, we still have it. So this is not yet working. Oh, did I not save this one? Something here I didn't save. Let's try again. Oh, I'll tell you what we didn't do. So now we need to set the Twitter link instead of the database Twitter link, right? This is real life coding, real life debugging. There we go. I forgot to use the new variable. Okay. Now, is it loading it fast enough? There we go. Now it did load it fast enough and you can see that on the bottom left. Actually, I can click on it and just nothing happens because it adds just a hashtag there and that's it. So it looks like we fixed it. Did we fix it? What happens if someone gets a bit, you know, well, tricky and maybe they do something like that. They just, you know, do it as a uppercase kind of string. So now if I do it, did it reload? Wait for it to reload. There you go. So I reloaded it and now we have the xss again. So the fix wasn't really that good for us. Let's try something else. Let's try, let's say that we, I mean, we want to catch it. So what do you think we should do like that? Should we like do a two lowercase and try it like this? Two lowercase. Would that work? Let's do it like this. And as you can see, this is basically the thought process of a developer and what would be great is if they had a tool that would help them on the IDE to basically follow the right APIs. I will, did it get loaded? Let me see. There we go. Now it did get loaded pretty fast, I think. And it looks like we fixed it. Did we? We knew it was coming, right? So we fixed the uppercase, we fixed the lowercase, but what else could go in, right? Like anything else, we can strip, trim, space characters and all those things. We can try all of those, but would it actually work? Would they not? We can try different way of escaping it maybe, but that's maybe a bit tricky. What we haven't tried is, what happens if we add things like control character, and that is something that I can actually add a control character over here and escape something like a slash R, slash N, something like that. And that would actually escape the whole two lowercase and index sub and everything will be just fine. Let me show you what I mean by that. If I just do slash, no, not here, over here, I just started off with slash X, nine, 10, which is a control character. If I try that one, the whole thing falls apart. Reload it again. There we go. So as we're learning, doing one thing and the other and the other, these are all trying to mitigate a problem, but there are still some issues, still some issues that we should figure out how to do it. So learning how to do things right is important. Now let me go into something else. Let's say that I actually want to, let's avoid this whole thing for a second, so I can try another example. I said I'd actually want, well, Libby actually wants to go ahead and do this whole package JSON view. And well, she's not going to go and, I mean, I'm not going to go and write a parser that I indent stuff and highlights. And this is, you could actually have themes and like pretty cool colors. So I'm not going to build all of this. I'm going to go outside and find something where I can give it a JSON file, like the package manifest for your npm package. We'll go ahead and do this whole really cool highlight syntax thing, because I'm also not really a css person. So this whole thing is not for me. I'm going to go over to something like the Snyk Advisor and I'll look for some packages. Already found something that's called react JSON Pretty. It's unfortunately it looks like inactive. It's unmaintained or something, but it has something like 40K downloads, which is a lot. So, you know, maybe I'll try and use it. You could try and check if this is popular by version or not. So hopefully, I mean, hopefully everyone are on the latest version, which they are. So that's a good thing from security wise, if there's like, this was vulnerable, this was vulnerable, you knew what to upgrade to. It also looks like this is mostly used as a direct package, which makes sense as opposed like an indirect package. So I'll go ahead and use it. The most popular version indeed has no security fixes. Let's see what's going on. I'm going to go and use this. So I'm going to go into package profile. Where is it? It does package parser. I'm pretty sure. There we go. So I've imported this as package parser and I've provided it the package manifest, which is basically all of this JSON from the database file that I have here. Let's play around with this one here. So it's a simple component, just take some configuration and then things apply it by default. That's it. And I can go ahead and what I want to show you is like, let's try and do xss from other places. Let's say that name is not now Gibson Explorer. Let's go back here. Right? Not Gibson Explorer anymore. That's going to be again, that thing that we saw. Well, let's do, I don't know, let's do, I mean, javascript alert will not work there. That's not a link. So I can try this vector instead. Click alert and see what happens. And changed it. But I mean, no alert. I don't even see an image tag, so that's okay. Let's try something else. What I would say is, I don't remember what was here, so I'll just do ABC. But let's say this, you know, if I were thinking how to exploit this, I would say, if this package was written by someone, maybe they didn't take a good care enough of sanitizing the data or output encoding it by going into all of the recursive elements that actually are in within the JSON. So all of the elements that are kind of living inside other fields and other fields. So what if I tried to put it here? Like maybe this is, this level on the JSON is sanitized, but this one is not, or output encoded would be the best way of doing this, but it looks like it is. So like that, that looks okay. So there we go. Getting back to normal. Now it looks like this is working and I've, you know, if this is a JSON input from an HT, from an npm package that you get as well, that is not controlled by you. That is controlled by someone else, right? So what if that person gives you, you know, gives you a bad package JSON, right? Like I'm going to go and do something like this and say, Hey, my, like you're getting this package JSON. There you go. So package manifest. There you go. No, it's, it's not a JSON anymore. Now it's a string. It's a string, which I control because I can, I can define whatever I want in my package JSON and my just a tar file and I'll send it to you. Right? So if this is the input now, let's see what happens. Change the whole meaning of what the package JSON is. But the question is, is the application ready to handle something like that? Doesn't look like it because it looks like it tries to render it. You should probably have put an alert box somewhere there. On alert. On our alert. Probably should have worked. Oh, there we go. So now we see that what's happening, right? So now you're using a component and open source library that may have vulnerabilities in it, even though you were thinking it might be okay, you know, probably, you know, all that probably it's not, but no, you have to like test all of those things. So it's kind of like brings us into the world of, you know, managing components, learning about all of those things. So here are my takeaways. Let's go back to slides. Yeah, sorry though. So takeaways for us, right? This is, you know, avoid all of this, you know, dangerously inner html, obviously you do not want to do this. But the fact that you're avoiding it doesn't mean that others are avoiding it as well, like we've seen here. So you should really scan your third party dependencies. If you're doing something like npm audit, you know, that's fine. It's a great way to, you know, get awareness for security vulnerabilities. But in my case, I found out when I was using react.JSON pre in my demo here is that he did not find it when I was doing npm install, npm audit, nothing was showing up for it. You know, luckily I was using a sneak test and I was able to find it. So, you know, be aware of what you're putting into this. If I have to summarize, right, be aware of what you're putting into your, I would say dependencies in general, libraries collection or your applications components, be aware of what you're putting there, be able to scan it all the time and fix it. And the other thing is, of course, be careful of what you put into those href html attributes because they are not getting output encoded by default. So thank you, you know, react, as I would say, react responsibly and securely. And if you want to reach out, I'm Lirontal. Thank you and have a nice time. Bye bye.