2 examples 0/3 challenges solved
Description
Ready for the full course?
Lifetime access of:
Next recipe
Exploiting Web Pages That Have A CSP
1 Example
0/2 challenges solved
All recipes
Hacking Websites With Cross-Site Scripting
1 Example
0/3 challenges solved
XSS Attacks From HTML Attributes
2 Examples
0/3 challenges solved
XSS Attacks From URLs
1 Example
0/2 challenges solved
XSS Filter Evasion
2 Examples
0/2 challenges solved
How To Use Event Handlers For XSS Exploits
2 Examples
0/3 challenges solved
XSS Attacks Inside JavaScript
1 Example
0/4 challenges solved
Polyglots: The Ultimate XSS Payloads
1 Example
0/1 challenges solved
How To Create Real XSS Exploits To Attack Websites
1 Example
0/3 challenges solved
How To Fix XSS Vulnerabilities In Code
0 Examples
0/3 challenges solved
How To Allow Safe HTML Injection
1 Example
0/2 challenges solved
How To Prevent XSS With Code Reviews
0 Examples
0/3 challenges solved
Automatic XSS Prevention
2 Examples
0/3 challenges solved
Exploiting Web Pages That Have A CSP
1 Example
0/2 challenges solved
Blind XSS
2 Examples
0/0 challenges solved
Resources
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
https://www.srihash.org
https://www.bootstrap.com
video transcript
In this recipe, you're gonna learn about CSP and SRI for XSS when SHTF.
*smh
Hello, world! I'm Jesse from Chef Secure, and Content Security Policy, or CSP, is a way to protect your website from XSS attacks even when you have a vulnerability.
And SRI is a way to stop third-party scripts from causing damage even when the host gets taken over by an attacker and they change the script.
Both of these are essential to security in your application in depth, because when something goes wrong or fails, and, trust me, that will happen, your application is not left completely vulnerable and open to attackers.
Content Security Policy works by whitelisting the resources allowed to load on your webpage, like scripts and styles, and anything not on that list will be blocked.
CSP is set in a response header set by your web server named Content-Security-Policy or in an HTML meta tag with an http-equiv attribute set to the same value.
Your policy whitelist becomes the header value or the content attribute value in the meta tag.
Content-Security-Policy: policy
<meta http-equiv="Content-Security-Policy" content="policy">
This policy defines the valid resources for your webpage, with directives, which are mostly rules for elements and their allowed sources.
Now, normally you start with the default-src
directive,
which is just a fallback in case any of the other directives go...
🛸
missing.
So if you've got some trust issues, you can block all resources by
specifying 'none'
.
This is, by far, the most secure option. But if you at least trust
yourself, and any uploads you may have hosted on your site, you can
specify 'self'
instead.
Next, the script-src
directive is likely the most
important part of your CSP when defending against XSS attacks, because
it tells your browser exactly what scripts are allowed to run.
And if you can block an attacker's script from executing, there's a lot less damage they can do.
Since default-src
is just a fallback for missing
directives, your script
src needs to list all resources
used, even if it's already included in the default-src
.
Most websites need to allow scripts from their own host by using
'self'
and to include third-party scripts, like CDNs, list
the full hostname with our without the scheme.
So you can have
htts://cdn.example.com
cdn.example.com
*.example.com
(matches any domain.example.com)
*
(matches EVERYTHING)
But be extra careful with wildcards because they can match everything.
Locking down your script-src directive will prevent attackers from pulling scripts from their own servers to deliver something like a cryptominer or a ransomware download
But what about injecting scripts directly into the page?
Thankfully, CSP is secure by default and will prevent XSS attacks like this.
But... it's probably gonna break your webpages too.
While it's almost always a bad idea to disable security features, I'll show you how to do it temporarily
TEMPORARILY!
so you can get rolling with your own CSP, then I'll show you the right way to fix your site so you can lock it down properly.
Adding 'unsafe-eval'
as a source allows you to run
potentially dangerous JavaScript functions that evaluate strings as
code.
Again, these functions are
eval('{{ escapeJS(data) }}')
new Function('{{ escapeJS(data) }}')
setTimeout('{{ escapeJS(data) }}',0)
setInterval('{{ escapeJS(data) }}',0)
setImmediate('{{ escapeJS(data) }}',0)
Note that the last three functions aren't blocked without
'unsafe-eval'
if they're called safely using a function
argument, rather than a string argument.
setTimeout(myFn,0)
setInterval(function(){...},0)
setImmediate(()=>{...},0)
Some of your dependencies, like older JavaScript frameworks, may need
'unsafe-eval'
to run properly. And if that's the case, be
sure and go through your own code, and look for any unsafe uses of the
eval-family of functions.
Then you can work on upgrading your framework or dependency, so you can
remove 'unsafe-eval'
.
The next insecure source option is 'unsafe-inline'
which
removes most of CSP's XSS defenses.
By default, CSP blocks inline scripts from executing – like inline
event handlers and script tags containing code – and, more than likely,
you have both in your code somewhere, so you'll probably want to add
'unsafe-inline'
as a source –
Let's... fix this problem so you don't have to do that.
Fix your inline event handlers like this:
Move untrusted or dynamic data into a new data attribute.
Then make a new JavaScript file, and put your event handler code inside a new function.
And update your code to use the data attribute.
Next, add the event listener.
And, finally, load the JavaScript file in your page.
Let's get some hands-on experience with this.
Go to the Refactoring Inline Event Handlers example for this recipe below.
For this example, you're gonna copy the HTML into a new file on your computer.
Create a new file called refactor.html. Paste in the code. And look at the end of the inline event handler and switch out my name for your own.
Then save the file, and open it in your browser.
You see that once you touch the cookie, sadly, it escapes out of your hands and an alert pops up asking you to kindly stop.
Go back to the code now.
Now, within the head of the document, add a meta tag for the CSP with
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'">
Refresh the page, and you'll see that it's still working.
But we want to lock down the script-src, so go back to the CSP and each directive is separated with a semicolon, so add that.
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'; script-src 'self'">
Refresh the page again, and you'll see that the cookie no longer runs away.
This is because the inline event handler is blocked by the CSP, and you can even verify this with the error message in the console.
Let's fix this now. Remember, we're considering your name as dynamic data, so the first step is to move it into a new data attribute.
<span id="cookie" ... data-name="YOUR_NAME"
And this, of course, would need to be HTML escaped in your framework.
The next step is to make a new JavaScript file. I'll name it cookiesec.js Then move the event handler into a new function.
So define the function with function then the function name. I'll call it hoverHandler. Then paste the inline event handler inside. I'm gonna fix up the spacing here really quick.
function hoverHandler() {
this.style.top=`${innerHeight-100)*Math.random()}px`;
this.style.left=`${innerWidth-100)*Math.random()}px`;
alert('YOUR_NAME! Please stop!');
}
And the next step is to use the data attribute. So change the alert string to a template literal And replace your name with the data attribute using
alert(`${this.dataset.name}! Please stop!`);
After this, you just need to add the event handler with
document.getElementById('cookie').addEventListener('mouseover', hoverHandler)
Finally, just add the script to the page with
<script src="cookiesec.js"></script>
Save it. Refresh the page, and everything is now working again with a
locked down script-src
. And there are no more errors in
the console.
By fixing inline event handlers like this you get three bonuses:
Similar to this, JavaScript URLs are also blocked without
'unsafe-inline'
so they just need to be replaced with
event handlers.
As I said earlier, script tags containing code are also considered unsafe inline scripts.
To fix these, move untrusted or dynamic data to a data attribute.
Then move your code inside a JavaScript file.
Update it to use the data attribute.
Then load the file from the script tag.
Let's do this in the Refactoring Inline Scripts example below, and, as I promised in the escaping recipe, I'll show you how to properly handle working with JSON objects.
Again, for this example, you're gonna copy the HTML into a new file. Then I'll create refactor2.html Paste the code inside. And switch out my name in the JSON object with your own.
Now let's suppose this JSON object right here is dynamically added to your script in your framework with proper JSON encoding.
Save the file and open it in your browser. And you see you get a nice, friendly greeting.
Now we're gonna add the CSP, so open the file. And add the meta tag.
And we'll just add the script-src
this time with
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
Refresh the page now, and you'll see that you've got an unthoughtful, generic greeting that doesn't make you feel good.
Open your console again and you'll see that the CSP blocked the inline script.
So we've got to fix this now. The first step is to move the JSON object to a new data attribute.
<span id="name" ... data-user="{"name":"YOUR_NAME"}"
Now comes escaping. There isn't any JSON escaping to do, so next we need to HTML escape the double quotes by turning them into
<span id="name" ... data-user="{"name":"YOUR_NAME"}"
Do it for all of them. By the way, in your framework, you'll, of course, use your own escaping functions instead of doing it manually like this.
Now that the JSON data's been relocated, create a new JavaScript file in the same directory. I'll call it friendly.js. And let's set a variable to the span element with
let name = document.getElementById('name')
Then update it's value with
name.textContent =
And now we need to parse the JSON to turn it from a regular 'ol string back to a real JavaScript object
name.textContent = JSON.parse(name.dataset.user)
Then, finally, from that user object we need to get the name, so just type in
name.textContent = JSON.parse(name.dataset.user).name
Then remove the contents of the script tag in the HTML file. And set it's source equal to your file.
<script src="friendly.js"></script>
Save this. Refresh the page now, and you'll see everything is fix with a personalized greeting and you've locked down your CSP to prevent unsafe inline scripts from executing.
For extra practice, you can pause the video right now and try the other recommendation I gave you in the escaping recipe, which is to use a script tag with the type set to application/json instead of using a data attribute.
Depending on how many inline scripts you have, this may take a bit of time.
So, while you work on that, CSP gives you two ways to allow inline
script tags without having to break security with
'unsafe-inline'
.
The first is to use a hash.
With a sha256, 384 or 512 hash of your entire inline script code, you can whitelist inline scripts.
Add this as a source in single quotes, by adding the sha-algorithm used, followed by a dash, then the base64 encoded hash value.
'sha256-base64(sha256(...))'
'sha384-base64(sha384(...))'
'sha512-base64(sha512(...))'
The second way is to use a nonce, which is short for a number used once.
(kind of: https://en.wiktionary.org/wiki/nonce)
It's basically a random token generated with every response.
Every inline script tag will need to set a nonce attribute to this value,
<script nonce=RANDOM_TOKEN></script>
and the CSP source will have, in single quotes, nonce, -, then the nonce value.
'nonce-RANDOM_TOKEN'
Look for a library or module that does this for you, instead of building it yourself, so you can save time and make sure it's done right.
Moving on from scripts, another directive that can use
'unsafe-inline'
is style-src
.
If you recall, attackers can use styles to make exploiting a page easier, so, by default, CSP blocks inline CSS styles that use the style attribute or style tags.
Fixing blocked styles works like this:
Move the CSS from style tags into a new file.
Afterward, move any style attributes into a new CSS class within the same file.
Add this class to the element's class attribute.
Then, finally, link this file in the HEAD of the document with a link tag containing a rel attribute set to stylesheet and an href attribute set to your file.
And, again, hashes and nonces can be used for inline style tags too.
Locking down your default-src
, scripts-src
and style-src
will do a lot in limiting XSS attacks on
your site, but there are still several other options available for
extra security.
I'm just gonna vomit them all out to you right now, starting with the
ones that fall back to default-src
.
And if you need to dive deeper into any of them, I'll provide you with a link to Mozilla's excellent CSP documentation for reference below.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
Here we go!
connect-src
tells your browser what sources scripts can
connect to in the background.
This effects things like AJAX
, fetch()
,
beacons
, websockets
and
<a ping="URL">
font-src
specifies what font resources can be loaded on
your page.
frame-src
is for embedded iframes.
img-src
is for images.
manifest-src
is for the manifest.json
file used in progressive web applications
media-src
is for <audio>
,
<video>
and <track>
object-src
is for <object>
,
<embed>
and <applet>
and their
nested contexts.
worker-src
is for Workers
,
Shared Workers
and Service Workers
If your CSP doesn't have any of those directives listed, your
default-src
will be used instead.
Now let's look at what's not covered by default-src
.
The next two directives are for securing connections, only one should be set at a time, and they don't have any source list because they're just flags.
The first is block-all-mixed-content
which prevents
loading resources over http when the page is using https.
The second is upgrade-insecure-requests
and will
automatically upgrade http requests for resources to https.
Here are the last of the remaining directives:
base-url
tells your browser what sources are allowed to be
used for the base element. Doesn't sound too exciting, but later we'll
go over how to exploit this if it's missing from a CSP.
The frame-ancestors
directive handles the embedding of the
page within an by other webpages. And since this doesn't fall back to
default-src
, missing this directive will allow any site to
secretly, or not so secretly, embed yours unless you have other
protections set, like the X-Frame-Options
header.
And, finally, the sandbox
directive works in the same way
as an iframe sandbox attribute which essentially restricts several
actions on the page. I won't go into this, but if you think you'll
need them, feel free to look further into this, and, if you do, know
that you'll need to use a header for your CSP and it won't work in the
meta tag.
This should be more than enough to secure your application with CSP but if you find you need a little more fine-tuning, check out the CSP documentation link below.
The last defense we'll go over in this recipe is Subresource Integrity (SRI).
Remember hashing inline scripts for CSP?
It's essentially the same thing.
Scripts hosted on other sites, especially scripts you just randomly found on GitHub or someone's blog need to be verified that they do what you think they do and nothing else behind the scenes.
Once you trust what you're putting into your website and delivering to every one of your users SRI will help make sure it doesn't change unexpectedly.
The shasum or openssl commands can help you generate this hash, and then you'll need to encode it in base64.
But Mozilla, again, came to the rescue and made it easier by creating an online service for this.
I like easy!
Open the link below, or go to srihash.org.
You see all you have to do is enter a URL for the resource you want to use and it'll generate a hash for you.
So we're gonna use BootstrapCDN as an example. So go to bootstrapcdn.com as well.
Select the Complete Javascript link. It's copied to the clipboard, now go back to the SRI hash generator.
Paste in the link, and click on hash.
You see that it generates a script with the integrity attribute and its value set to the algorithm, a dash, then the encoded hash.
In addition, a crossorigin attribute is set to anonymous, which prevents your browser from sending any credentials to BootstrapCDN while SRI performs its background fetch request to verify the integrity of the hash.
Now there are two cases where you can't use SRI for scripts.
The first is fixable: your CDN doesn't have CORS enabled, so SRI can't verify the integrity from your website.
To fix this, you simply need to add the CORS headers in your CDN so the script integrity can be verified.
Access-Control-Allow-Origin: https://example.com
The second situation is, unfortunately, not fixable.
If you're referencing a JavaScript resource that's constantly updated with new features and fixes, any new change will break the integrity of the hash.
Your script will probably look something like this.
<script src="https://js.example.com/v3"></script>
From here, you have two options: you can either use SRI and wait for your site to break with every update. or, just leave it off.
Always remember that user experience in security is very important. Security should enhance applications and not just break them, but, ultimately, it's up to you and your team to decide what risks you're willing to take.
However!
This doesn't mean you can use this as an excuse when you just want to be lazy and procrastinate because there's a lot of upfront work involved.
(sigh)
Just so you know.
*JSYK