1 example 0/2 challenges solved
Description
Ready for the full course?
Lifetime access of:
Next recipe
How To Prevent XSS With Code Reviews
0 Examples
0/3 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
video transcript
Hello, world! I'm Jesse from Chef Secure and I'm gonna show you how and why you might want to purposely allow users to inject HTML into your website.
And I'm not talking about how to create a secret backdoor or an elaborate scheme to steal money from your company that'll get you fired.
No-no-no! That was the exploit recipe.
I'm talking about how to make your site more interactive, dynamic and exciting, by allowing user-supplied images, custom formatting, and super-hip marquees.
(wow!)
Honestly, there's only one thing you really need to know to allow user-supplied HTML into your page.
Don't build this yourself!
Whelp! That's about it –
No! Really, listen carefully. Don't build this yourself.
There are so many gotchas and nuances that you really need to use a common module or library to allow user-supplied HTML into your page.
Otherwise, you're gonna wind up with a lot of issues and wasting a lot of time building something that, really, someone has already done better than you.
No offense.
HTML sanitizers on your server or in your framework do exactly this.
You specify what HTML tags and attributes are allowed, and the sanitizer parses the untrusted input, like a browser would, to product HTML only using your whitelist while removing or escaping everything else.
Notice that I never recommended trying to escape or filter untrusted input as it's being entered in the browser.
This isn't because I'm lazy.
...
This isn't ONLY because I'm lazy.
It's because client-side filtering can easily be bypassed with the right tools or by disabling some javascript or by simply making AJAX requests in the console to send unsanitized data.
Therefore, all the escaping and sanitization need to happen as the response HTML is being written, rather than when the input is received.
For your challenges, you're gonna use the sanitize-html package.
Now this was created for node, but your own server or framework will have its own version of an html sanitizer that should work similarly to this.
Let's go through this right now. Go to the HTML Sanitization example for this recipe below and we're gonna be working in the console again, so open that up.
The way to sanitize untrusted input is to call the function,
sanitizeHtml(
passing in the untrusted input as the first argument so we're just gonna add a paragraph element for now.
sanitizeHtml('<p>
and the contents will be
sanitizeHtml('<p>hi</p>',
And an object containing the whitelist options will be the second argument
sanitizeHtml('<p>hi</p>', {allowedTags: }
containing the HTML tags you want to allow Let's start off with just an empty array and see what happens.
sanitizeHtml('<p>hi</p>', {allowedTags: []})
Since we didn't allow any tags, sanitize-html only let the content pass through, but for scripts, styles and iframes, sanitize-html will filter everything out, So check with your own sanitizer to see what its default settings are when you need to use it.
Go ahead and change the allowedTags array to allow paragraphs by adding
sanitizeHtml('<p>hi</p>', {allowedTags: ['p']})
Now you see this time that the entire paragraph element was preserved.
But what about attributes?
In the console, push the up arrow to retrieve the last command. Then add a data-food attribute to the untrusted input string, and set it equal to your favorite food.
I'm gonna have cookies!
sanitizeHtml('<p data-food="cookies">hi</p>', {allowedTags: ['p']})
Then hit enter.
I'm sure it hurts deep down inside your heart when your favorite food gets taken away from you like this. ESPECIALLY if your favorite food is cookies.
But there is an important lesson to learn here.
Whenever you're adding in new security features you need to be aware of the full impact that your changes can have on the very, very, very... VERY IMPORTANT 🍪 –
BUSINESS-RELATED features of your application.
In other words, carelessly adding escaping or sanitization in parts of your application that actually require unescaped HTML, can break features that keep your company in business and keep you with a job.
Let's go back and fix this tragedy by adding an allowedAttributes property to our whitelist.
sanitizeHtml('<p data-food="cookies">hi</p>', {allowedTags: ['p'], allowedAttributes: })
And it's value is another object containing tag properties with their whitelisted attributes.
sanitizeHtml('<p data-food="cookies">hi</p>', {allowedTags: ['p'], allowedAttributes: {}})
then add the paragraph property, p
sanitizeHtml('<p data-food="cookies">hi</p>', {allowedTags: ['p'], allowedAttributes: {p: ['data-food']}})
Then hit enter and...
Hallelujah!
All is right in the world again.
For href and src attributes, sanitize-html, by default, only allows http, https, ftp and mailto URLs to be used. Other sanitizers may do similar, so, again, you'll need to double check if yours is different.
Let's try it out.
In your console, call sanitizeHtml with an anchor element that uses a JavaScript URL
sanitizeHtml('<a href="javascript:alert()">>click</a>', {allowedTags: ['a'], allowedAttributes: {a: ['href']}})
You see that the href attribute was stripped out automatically, because it contained a JavaScript URL.
Now if you change this use use HTTP or HTTPS, it'll work as expected.
sanitizeHtml('<a href="https://chefsecure.com">>click</a>', {allowedTags: ['a'], allowedAttributes: {a: ['href']}})
and it works as expected.
There are certainly more fine-tuning options available if you need, but this covers the basics of HTML sanitization and is all most websites really need to allow safe HTML code, while killing 99.99% of all bad input.
What about the .01 percent –
oh, there it is.