Insecure Processing of Data
This category covers the following issues:
Fixing Cross-Site Scripting
About XSS
What is Cross-Site Scripting?
Cross-site scripting (XSS) is a type of security vulnerability that allows attackers to inject malicious code into web pages viewed by other users. This occurs when an application fails to properly sanitize user input and injects untrusted data into a web page without validating or encoding it.
XSS attacks can occur in different forms, such as reflected, stored, or DOM-based, and can affect various types of web applications, including social media, e-commerce, and online banking sites.
Attackers can use various techniques, such as phishing emails, web forms, or malicious links, to trick users into visiting or interacting with web pages that contain malicious code.
Check out this video for a high-level explanation:
What is the impact of Cross-Site Scripting?
Cross-site scripting (XSS) can lead to various security threats and risks, such as:
- Information theft: XSS attacks can steal sensitive information, such as login credentials, credit card details, or other personally identifiable information, by injecting malicious code that collects and transmits user data to attackers.
- Account hijacking: XSS attacks can hijack user sessions by injecting malicious code that takes over user sessions, allowing attackers to perform unauthorized actions on the website or gain access to user accounts.
- Data tampering: XSS attacks can modify or delete data on a website by injecting malicious code that alters the content of web pages, leading to data tampering, data corruption, or other types of malicious activities.
- Malware distribution: XSS attacks can distribute malware by injecting malicious code that downloads and installs malicious software on the user's computer, leading to malware infections or other types of cyber attacks.
Overall, XSS attacks can compromise the confidentiality, integrity, and availability of data, leading to a range of security threats and risks.
How to prevent Cross-Site Scripting?
To prevent XSS vulnerabilities, you can take the following steps:
- Input validation and sanitization: Ensure that all user input is validated and sanitized before being used to generate web pages or manipulate other data. Input validation should include validating the data type, length, and format, and filtering out any special characters that could be used to inject malicious code.
- Output encoding: Encode all output to protect against XSS attacks. Use output encoding techniques such as HTML entity encoding, URL encoding, and JavaScript encoding to encode user input and output to the browser.
- Use security headers: Implement security headers to protect against XSS attacks, such as Content-Security-Policy (CSP) and X-XSS-Protection. CSP can help prevent XSS attacks by allowing only trusted sources of content to be loaded on a page, while X-XSS-Protection can help prevent the browser from rendering pages that contain malicious scripts.
- Session management: Implement secure session management mechanisms, such as session cookies, to protect against session hijacking and other attacks.
- Secure coding practices: Use secure coding practices to prevent XSS vulnerabilities. This includes using libraries and frameworks that are designed to prevent XSS attacks, testing code for vulnerabilities, and using secure coding practices such as input validation and output encoding.
- Regular security audits: Regularly audit your system for security vulnerabilities, including XSS vulnerabilities. Use automated tools and manual testing to identify potential issues and fix them before they can be exploited.
References
Taxonomies
- OWASP Top 10 - A03 Injection
- CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Explanation & Prevention
Related CVEs
Training
Category-specific resources:
Cross-Site Scripting (Handlebars)
Rule-specific resources:
Option A: Don't use Handlebars.SafeString
(Backend)
Using Handlebar's default behavior of treating the input as pure text is the safest way to fix this vulnerability.
Note that if you want HTML content to appear, you should look at option B and sanitize the user input.
Go through the issues that GuardRails identified, for a pattern similar to the following:
app.get("/render", function(req, res) {
let template = Handlebars.compile('This is some bold text: <b>{{input}}</b>')
// Insecure example
let value = new Handlebars.SafeString(req.body.untrustedValue)
res.set("Content-Type", "text/html")
res.send(template({input: value}))
})Remove
Handlebars.SafeString
app.get("/render", function(req, res) {
let template = Handlebars.compile('This is some bold text: <b>{{input}}</b>')
// Secure example
let value = req.body.untrustedValue
res.set("Content-Type", "text/html")
res.send(template({input: value}))
})Test it
Ship it 🚢 and relax 🌴
Option B: Don't use Handlebars.SafeString
(Frontend)
The safest way to fix this vulnerability is by using Handlebars' default behavior of treating any input as pure text.
Note that if you want HTML content to appear, you should look at option B and sanitize the user input.
Locate the vulnerable pattern (example below):
function render() {
let template = Handlebars.compile('This is some bold text: <b>{{input}}</b>')
let request = fetch("http://example.com/example.txt")
.then(body => body.text())
.then(text => new Handlebars.SafeString(text))
.then(sstring => template({input: sstring}));
}Replace it with one of the following patterns (example below):
Example #1
function render() {
let template = Handlebars.compile('This is some bold text: <b>{{input}}</b>')
let request = fetch("http://example.com/example.txt")
.then(body => body.text())
.then(text => template({input: text}));
}Example #2
function render() {
let template = Handlebars.compile('This is some bold text: <b>{{input}}</b>')
let request = fetch("http://example.com/example.txt")
.then(body => body.text())
.then(Handlebars.Utils.escapeExpression)
.then(text => new Handlebars.SafeString(text))
.then(text => template({input: text}));
}Test it
Ship it 🚢 and relax 🌴
Option B: Sanitize your input
This option should be used if you want some input to be interpreted as HTML content. You can use a sanitizer such as DOMPurify to make sure any HTML is sanitized before it is embedded into the DOM.
Some possible options for sanitizing HTML include:
- The builtin
Sanitizer.sanitize(...)
andSanitizer.sanitizeFor(...)
experimental - The
DOMPurify.sanitize(...)
from the isomorphic-dompurify library htmlSanitize(...)
from the html-sanitize libraryxss(...)
from the xss library
Go through the issues that GuardRails identified, for a pattern similar to the following:
// Backend example
app.get("/render", function(req, res) {
let template = Handlebars.compile('This is some bold text: <b>{{input}}</b>')
// Insecure-example
let value = new Handlebars.SafeString(req.body.untrustedValue)
res.set("Content-Type", "text/html")
res.send(template({input: value}))
})// Frontend example
function render() {
let template = Handlebars.compile('I want custom HTML: <b>{{input}}</b>')
let request = fetch("http://example.com/example.txt")
.then(body => body.text())
.then(text => new Handlebars.SafeString(text))
.then(sstring => template({input: sstring}));
}Sanitize user input before passing it to
Handlebars.SafeString
// Backend example
const DOMPurify = require('isomorphic-dompurify');
app.get("/render", function(req, res) {
let template = Handlebars.compile('This is some bold text: <b>{{input}}</b>')
// Secure examples
let value = DOMPurify.sanitize(req.body.untrustedValue)
value = new Handlebars.SafeString(value)
res.set("Content-Type", "text/html")
res.send(template({input: value}))
})// Frontend example
function render() {
let template = Handlebars.compile('I want custom HTML: <b>{{input}}</b>')
let request = fetch("http://example.com/example.txt")
.then(body => body.text())
.then(DOMPurify.sanitize)
.then(text => new Handlebars.SafeString(text))
.then(sstring => template({input: sstring}));
}Test it
Ship it 🚢 and relax 🌴
Cross-Site Scripting (Frontend)
Injecting unsanitized HTML from untrusted sources into the DOM without prior sanitization can lead to Cross-Site Scripting vulnerabilities.
Option A: Treat all data purely as text
The safest way to fix this kind of vulnerability is to simply use methods/properties that will treat all your data as text (not as HTML). These include:
document.createTextNode(<UNTRUSTED_INPUT>)
Node.textContent = <UNTRUSTED_INPUT>
Note that document.createTextNode(...)
creates a Node
object that can be used in the DOM
whereas the other properties simply overwrite the content inside the selected element/node.
selected.
Another option that you can use is innerText
or outerText
, note, however, that these elements
are susceptible to forms of XSS
HTMLElement.innerText = <UNTRUSTED_INPUT>
HTMLElement.outerText = <UNSTRUSTED_INPUT>
Locate the vulnerable pattern (example below):
async function updateComment(id) {
let data = await fetch(`api.example.com/v1/comments/${id}`);
// Vunerable line of code here
document.getElementById("comments").innerHTML = await data.text();
}Replace it with one of the following patterns (example below):
Example #1 (using
HTMLElement.textContent
)async function updateComment(id) {
let data = await fetch(`api.example.com/v1/comments/${id}`);
// Fixed
document.getElementById("comments").textContent = await data.text();
}Example #2 (using document.createTextNode(...)
async function updateComment(id) {
let data = await fetch(`api.example.com/v1/comments/${id}`);
// Fixed
let node = document.createTextNode(await data.text());
document.getElementById("comments").appendChild(node)
}Test it
Ship it 🚢 and relax 🌴
Option B: Sanitize your HTML
This option should be used if you want some input to be interpreted as HTML content. You can use a sanitizer such as DOMPurify to make sure any HTML is sanitized before it is embedded into the DOM.
Some possible options for sanitizing HTML include:
- the builtin
Element.setHTML(...)
experimental - the builtin
Sanitizer.sanitize(...)
andSanitizer.sanitizeFor(...)
experimental - the
DOMPurify.sanitize(...)
from the DOMPurify library htmlSanitize(...)
from the html-sanitize libraryxss(...)
from the xss library
Locate the vulnerable pattern (example below):
async function updateComment(id) {
let data = await fetch(`api.example.com/v1/comments/${id}`);
// Vunerable line of code here
document.getElementById("comments").innerHTML = await data.text();
}Replace it with one of the following patterns (example below):
async function updateComment(id) {
let data = await fetch(`api.example.com/v1/comments/${id}`);
// Fixed
document.getElementById("comments").innerHTML = DOMPurify.sanitize(await data.text());
}Test it
Ship it 🚢 and relax 🌴
Cross-Site Scripting (React)
Inserting raw HTML via dangerouslySetInnerHTML
can lead to
Cross Site Scripting vulnerabilities.
Rule-specific references:
Option A: Don't use dangersouslySetInnerHTML
The safest way to fix this kind of vulnerability is to simply not
use dangerouslySetInnerHTML
. If you don't require your input to
be able to customize HTML then you're fine.
Locate the vulnerable pattern (example below):
function Comment({username, message}) {
let inner = `<b>${username}</b>: <span style='color: blue'>${message}</span>`
return <div dangerouslySetInnerHTML={{__html: inner}}/>
}Replace it with a pattern similar to the one below:
function Comment({username, message}) {
return (<div>
<b> {username} </b>
<span style='color: blue'>{message}</span>
</div>);
}Test it
Ship it 🚢 and relax 🌴
Option B: Sanitize the user input
Some possible options for sanitizing HTML include:
- the builtin
Sanitizer.sanitize(...)
andSanitizer.sanitizeFor(...)
experimental - the
DOMPurify.sanitize(...)
from the DOMPurify library htmlSanitize(...)
from the html-sanitize libraryxss(...)
from the xss library
Locate the vulnerable pattern (example below):
function Comment({username, message}) {
let inner = `<b>${username}</b>: <span style='color: blue'>${message}</span>`
return <div dangerouslySetInnerHTML={{__html: inner}}/>
}Replace it with one of the following patterns (example below):
import * as DOMPurify from 'isomorphic-dompurify';
function Comment({username, message}) {
let inner = `<b>${username}</b>: <span style='color: blue'>${message}</span>`
return <div dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(inner)}}/>
}Test it
Ship it 🚢 and relax 🌴
Option C: Use a different form of customization
This option should be used if you want HTML content to be
customized by your input. Instead of allowing input to use
HTML for customization, you can switch to using something
like react-markdown
, and the input can be embedded through
markdown (much safer).
Locate the vulnerable pattern (example below):
function Comment({username, message}) {
let inner = `<b>${username}</b>: <span style='color: blue'>${message}</span>`
return <div dangerouslySetInnerHTML={{__html: inner}}/>
}Replace it with one of the following patterns (example below):
import ReactMarkdown from 'react-markdown'
function Comment({username, message}) {
return (<b>{username}:</b>
<ReactMarkdown>{message}</ReactMarkdown>);
}Test it
Ship it 🚢 and relax 🌴
Cross-Site Scripting (Generic)
Option A: Ensure Unescaped Variables are Safe
Go through the issues that GuardRails identified in the PR/MR
Identify the code with one of these patterns:
// Dust.js
{variable}
// Pug.js
!{variable}
// ECT templates
<% ... >
// EJT templates
<% ... >And ensure that all of these unescaped variables don't contain any unsanitized user input
Test it, ship it 🚢 and relax 🌴
Option B: Ensure XSS Security Headers are enabled
Go through the issues that GuardRails identified in the PR/MR
Identify the code with one of these patterns:
// XSS Security Headers
lusca.xssProtection(false);
X-XSS-Protection = 0;And ensure the headers are enabled. Note: It may be possible that this is handled on the load balancer or web server level
Test it, ship it 🚢 and relax 🌴
Option C: Ensure No User Input is Passed to res()
Go through the issues that GuardRails identified in the PR/MR
Identify the code with one of these patterns:
res.write(req.body.evil);
res.send();And ensure that no user input is passed to these functions
Test it, ship it 🚢 and relax 🌴
Option D: Perform Output Encoding
Go through the issues that GuardRails identified in the PR/MR
Identify code that matches
*.escapeMarkup = false
:var template: Object = new Object();
template.escapeMarkup = false;And replace it with
*.escapeMarkup = true
,template.escapeMarkup = true;
Setting
escapeMarkup = false
can be used with some template engines to disable escaping of HTML entities which could potentially lead to Cross-Site Scripting (XSS) vulnerabilities.Identify code that matches any of the following patterns:
document.write(variable);
document.writeln(variable);
Element.innerHTML = variable;
Element.outerHTML = variable;
el.insertAdjacentHTML(variable);For example:
var element: Element = document.getElementById("mydiv");
var content: String = "some content";
Element.innerHTML = content;Ensure that the content is not coming from request parameters, and is sanitized and escaped before being passed to the affected method
Test it
Ship it 🚢 and relax 🌴
Fixing Server-Side Request Forgery
About SSRF
What is Server-Side Request Forgery (SSRF)?
Server-Side Request Forgery (SSRF) is a type of web application vulnerability that occurs when an attacker can manipulate the server-side code to make it perform unauthorized HTTP requests to other web applications. SSRF attacks can allow an attacker to send requests from the vulnerable server to internal or external network resources, which can result in data leaks, access to sensitive data or even compromise the entire network.
An SSRF attack typically involves an attacker tricking a server into making a request to a specific URL that the attacker controls. The attacker may do this by manipulating the parameters of a URL or by using other techniques to inject malicious code into the server-side code.
What is the impact of Server-Side Request Forgery (SSRF)?
Attackers can use this vulnerability to perform a variety of attacks, such as:
- Information disclosure: The attacker can use an SSRF attack to access sensitive data, such as configuration files, internal documentation or even credentials, which can be used to compromise the entire network.
- Port scanning: The attacker can use an SSRF attack to scan the network for open ports, which can be used to identify potential vulnerabilities in the system.
- Bypassing authentication and authorization: An SSRF attack can be used to bypass authentication and authorization controls by sending requests to internal services that do not enforce these controls.
- Exploiting internal APIs: An SSRF attack can be used to exploit unprotected internal APIs, which can result in unauthorized access to sensitive information or actions that the user is not authorized to perform.
- Denial-of-service (DoS): The attacker can use an SSRF attack to flood the targeted web application with requests or to perform a request to a resource that is not available, causing the application to crash or slow down.
How to prevent Server-Side Request Forgery (SSRF)?
Here are some best practices to prevent SSRF attacks:
- Use secure APIs: Use secure APIs that do not allow arbitrary URLs to be passed as parameters. This can prevent attackers from manipulating the server-side code to perform unauthorized HTTP requests.
- Input validation and sanitization: Validate and sanitize all user input, especially for URLs, to ensure that they are valid and safe. This can prevent attackers from injecting malicious URLs into the server-side code.
- Access controls: Implement access controls to limit access to only authorized users. This can prevent attackers from exploiting unprotected internal APIs or cloud infrastructure.
- Server hardening: Harden servers by removing unnecessary software and services, and limiting access to administrative interfaces. This can prevent attackers from exploiting vulnerabilities in the server-side code or gaining access to sensitive information.
- Network segmentation: Use network segmentation to prevent web applications from accessing internal resources or those that they do not need to access. This can limit the impact of an SSRF attack by preventing the attacker from accessing sensitive resources.
References
Taxonomies
Explanation & Prevention
Related CVEs
Training
Option A: Ensure that User Controlled Data is Sanitized
Go through the issues that GuardRails identified in the PR/MR
Identify the code with one of these patterns:
request(req.body.evil);
request.get();
needle.get();And replace any dynamic user input with either a list of allowed options that is referenced by a UUID, or alternatively, re-write the code to ensure no dangerous or unauthorized functionality can be triggered by users
Test it, ship it 🚢 and relax 🌴