Check out a free preview of the full Web Security, v2 course
The "Implementing a CSP" Lesson is part of the full, Web Security, v2 course featured in this preview video. Here's what you'd learn in this lesson:
Steve demonstrates how to use CSP to prevent cross-site scripting attacks. He provides an example website with various vulnerabilities and then walks through the process of implementing CSP to mitigate those vulnerabilities. Steve explains the different directives and options available in CSP and emphasizes the importance of allowing only trusted sources and minimizing the attack surface.
Transcript from the "Implementing a CSP" Lesson
[00:00:00]
>> Steve Kinney: Let's actually take a look at an example, and we'll see a page that we have going, where we can play around with. And that is,
>> Steve Kinney: Csp-playground, that's a great name.
>> Steve Kinney: Cool, we'll grab that. And this site is possibly my best design work ever, and I'm very proud of it.
[00:00:22]
>> Steve Kinney: So we've got an awesome Google font. I don't know if you've heard of this Google font. You can load any font. We do have some inline scripting that's happening, some CSS. We've got this cat meme. And this is from the Frontend Masters site, so if you move this image ever, you will break my site, okay?
[00:00:47]
We've got some stuff to play with, CORS headers that we may or may not use. But yeah, we've got a whole bunch of stuff. We can look at my awesome markup too. If anyone wants to see that, it's really great.
>> Steve Kinney: Yeah, we can go into, I think it's just public, no, it's in Views cuz we need to put one value in there later.
[00:01:06]
So you can see I load a bunch of the Google fonts, as they call them. I have a favicon from, this is cool. If you ever need a favicon, it's just fav.farm and then slash any emoji, and that becomes your favicon. We got tailwind coming from somewhere else.
[00:01:27]
We've got that Google font style sheet as well. We've got some inline styles that make my awesome style happen. A whole bunch of fun stuff, remote pictures, local pictures, just really some of my best work, honestly. And we can go ahead and we can put in a CSP to prevent.
[00:01:53]
So I've got inline scripting running, and this is XSS attack waiting to happen, right? I'm just gonna innerHTML this stuff right in here and everything's gonna be great. And so all of that seems really precarious and something I probably shouldn't do. And the cool part about the CSP is that you can just go ahead and give it a very granular list of what is and is not allowed.
[00:02:23]
And honestly, it's one of those things when someone shows you a CSP header, you want to cry, because it's a bunch of things you don't understand, but it's actually, it's not that bad. So like I said before, you can put the tags in the head. If you have Express, you can use this library called Helmet, which is kinda cool cuz it's headers, get it?
[00:02:43]
But we'll do that, but either one will work. And again, the overall idea is the same, whether or not you use Express or something else, it doesn't really matter. So in this case, we will go ahead and import helmet.
>> Steve Kinney: Helmet from 'helmet', and then you can use it as middleware.
[00:03:06]
So you can do app.use(helmet()). And that kinda just sets up with some basic stuff, but then what we wanna do is app.use. There's other setters you can set here as well. You can even do course policies, you do helmet.contentSecurityPolicy.
>> Steve Kinney: And if you wanna play around with the CORS pieces, I've got that cors-field in there as well.
[00:03:34]
And so we can just start out with, I forget what happens if I just put an empty one here, but we can find out the hard way. It does some mostly reasonable ideas at first. You can see fonts are okay, local images are okay as well. The CSS seems to be good, but the remote image didn't load, because again, that could have been a bad get request.
[00:04:00]
You can see my inline scripts don't load anymore, right? And so even if a script got injected, it doesn't matter, right? Cuz if we take a look at this, you can see the browser was like, nope. Not allowing these things cuz it's not. My favicon didn't even load.
[00:04:20]
My beautiful little emoji favicon didn't even load. We could get even more draconian here, where we could say, we'll say, let's only go with, you can say no iframes whatsoever, which we'll find out why that might be a good thing later. You can fully sandbox this thing. You can get to the point where even forms can't be submitted.
[00:04:45]
But let's go ahead and we'll just say, let's just say these two here. So we're not even loading fonts from anywhere else. Oop, let's see. No, I don't want that either.
>> Steve Kinney: The one thing I'm not.
>> Steve Kinney: Those are the ones loading internally, but the font isn't, let's see.
[00:05:26]
>> Steve Kinney: So we got a few of them blocked here. My defaultSrc, I wonder what happens if I change this one to none.
>> Steve Kinney: Maybe I have it cached. I gotta see, let's open.
>> Steve Kinney: Truly breaking everything I'm not super interested in, so I'm not gonna play around with it too much.
[00:05:46]
But we can see that it doesn't matter what the inline scripts are, mostly protected immediately. I had it really broken before, where it looked like a regular old plain HTML page. That is while I aspire to show you that, not really useful or helpful. So I'm not gonna spend too much time fussing with that, but we can begin to loosen it up a little bit as well.
[00:06:13]
So we've got a lot of that in place, and we can, I think I said before, we can begin to build it out a little bit too. So for instance, I think if we wanted to allow, you saw before, did I have the CSS from? Let's see, let's say images, right?
[00:06:35]
We wanna let Frontend Masters in.
>> Steve Kinney: I think if I do that, that's how I broke the CSS. I let otherwise harmless CSS in. If I say only for my own origin, then now I'm down to the ugliest version possible of the page. Because we're not loading that third party tailwind style sheet or anything along those lines, we are saying only stuff coming from the own origin, which is basically solely that cat picture, I think, if I'm remembering correctly.
[00:07:08]
I think I only left that in there for that reason, but I could say something like, that should bring back the tailwind stuff. Yeah, that's fine. We can go look at the source, but you can begin to put these things in, you've got script, you've got style. If you wanted to let that other code run again, you could say, quotes.
[00:07:32]
>> Steve Kinney: My quoting got weird. There we go. I don't know what that is, we'll take that out for a second. So unsafe-inline, you can allow, again, I'm gonna explain to you why you shouldn't.
>> Steve Kinney: And yeah, you can play around with a whole bunch of stuff here. But the idea is, this is one of the fundamental principles that we wanna talk about here, which is instead of just trying to block out the bad stuff, right?
[00:07:58]
Cuz someone's gotta find a way around that. What you begin to do is you just say, hey, here is the list of things that I want to begin to build up, right? And you can kinda compose it over time, right? And kinda get all of that in place as well, and start to just allow list those domains that you want.
[00:08:15]
So if we go look at this page, right, so we could say something like, for the font source, right? Not that I really want that font back all that badly, but to make a point.
>> Steve Kinney: You could say whether or not you allow data attributes in there. If you want to on the Socialite app, and I'll leave this as an exercise to the reader, you can put in a URL for a photo.
[00:08:43]
So you could literally devoid yourself and hit everyone with a worm, right? Cuz then everyone who sees your thing will also post. So you can also block anything, but either data attributes or stuff from the on site. You can kinda play around with a bunch of these different examples.
[00:09:01]
So this is one that will get me back to the glory days, but a more protected glory days. So this is one that allows all the same stuff, but if new, [LAUGH] it's like, if new script injection happened, I would be safe. So we can kinda walk through this one here, where by default, the default self is anything from the own origin.
[00:09:21]
Here, we can choose whether or not we want to allow the save-inline. I am gonna choose to still block that, I think. And then we've got this idea of, we've got whether or not we wanna inject styles. I'm gonna say no to that, but keep the rest. Awesome, images, here we can let it from static.frontendmasters as well as my favicon, right?
[00:09:44]
But now, if new stuff gets added for any reason, right? And even if you had a content site, let's say you were making it on Instagram, right? They upload the photo, that photo's probably on your CDN, right? So you would then say, hey, the S3 bucket that I have with Cloud flare with the DNS, I'm gonna allow that.
[00:09:59]
But if other images get put in here somewhere, that could be a CSRF attack, it could be, I don't know if you can. I mean, it could actually be, there is one of the XSS attacks that I can show you in a second if you really wanna see it, is you throw in an image tag with a BS-like source, and then you give it the onerror attribute to run JavaScript.
[00:10:23]
It immediately breaks, and this will even stop that as well, right? Cuz it wouldn't be in that list, it would never try to load that resource. So actually, it's like, no, I only care about script injection. No, you care about a wide range of things cuz it doesn't always have to be JavaScript to execute JavaScript.
[00:10:41]
Your sanitizer should get that, of course, but if it doesn't, we saw that with the onmouseover link that was one of the Twitter worms, right? A CSP would have caught that if CSPs fully existed then. So you do wanna start out draconian. Now, I get it, you have a legacy app, you probably can't do that.
[00:11:02]
There is a header to set it to report, right? At which point, it won't block anything, but you can either have it show up in your console, or you can give it a URI to actually send the reports to. So you can actually see where the violations are and begin to kinda build it up like that as well.
[00:11:21]
So we'll say, yeah, no iframes as well. Put all of this in place. This is the reportTo, I think you can set a URL later. So on and so forth. Where do we allow fonts from? Cool, and so that should get me back to the glory days, right?
[00:11:41]
You can see my inline script didn't load, that I had before, that pink bar. If I put back that,
>> Steve Kinney: Now my inline script loads because I had to opt in to inline scripting. So just the act of having one, honestly, at all, I think even if this was anywhere, I think I can put a star in here.
[00:12:10]
But the point is, it would still, I think, out of the box, save you from at least the worst, which is not everything, though. And it's why, if you can, one of the principles is allow list your way up, not deny list your way down, right? And start to add those things in accordingly.
[00:12:27]
So in summary, let's look at the content security policies. Why do they exist? To stop cross-site scripting, right? Cuz again, as we saw, the CSP attacks that we saw, first of all, they all had earlier dates, right? I think a lot of the cookie policies protect you there.
[00:12:48]
The CSP is here to solve the idea they got the code in on your same site, right? And so, by only allowing things that you allow for, again, the name of the game is shrinking the surface area as low as possible. So you're already sanitizing inputs, you're already trying to strip out that onmouseover it.
[00:13:09]
But if they do something where it's like, I found out putting a new line between Java and script loads it. And now it's around your filter, you then, all right, welcome to my next line of defense, please go away, right? And at some points, if it's just become so hard to attack you, maybe there is a one one-thousandth of a chance, but at some point, the idea is to make it as unappealing as possible.
[00:13:35]
Cool.
>> Steve Kinney: Like I said, if you need to, you can actually set a second header called Content-Security-Policy-Report-Only, which will allow you to log them. You can actually give it a URL to send it to, and so you could collect them server-side. Again, if you can get away without doing this, don't do it because you're not necessarily getting the protection, you're just getting a way to start paying down tech debt to get all the way there.
[00:14:01]
There are some old headers, so check your code base for these, they're deprecated. Maybe they still work, but assume they don't, right? Cool, and again, if you can get it all the way down, you can get to the point where you don't necessarily have to worry about too much, but at least get to the point we're not letting.
[00:14:30]
You can do the refractor that gets your inline scripts out to script files that you load on your own domain. And so that at least, if someone does get another inline script, those are completely blocked. Inline style, which you can do on that XSS playground, you can also put a div with a background.
[00:14:46]
You do a div that is a style tag in it and it says body background red. You can do all sorts of stuff to that page that is all very rude and have fun. But you can also then try it on that page too, put a CSP on that page and see, of those attacks, which ones still work?
[00:15:01]
Almost none of them will.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops