November 4, 2026
I Found an Interesting DOM-Based XSS Issue in Contact Form | by Samadhan Shimple
While exploring different client-side security issues, I came across an interesting DOM-based Cross-Site Scripting (XSS) vulnerability…

By Samadhan shimple
5 min read
While exploring different client-side security issues, I came across an interesting DOM-based Cross-Site Scripting (XSS) vulnerability reported on the website.
What caught my attention was not just the use of innerHTML, but how a simple JavaScript function intended to remove HTML tags could introduce a potential security issue.
The interesting part of this case was that the website had a Content Security Policy (CSP) in place, which blocked the execution of the injected inline JavaScript.
Let's understand how this issue worked, why innerHTML can be dangerous, and what developers can learn from it.
1. Understanding the Vulnerability
The issue was reported in the contact form available on:
https://www.example.com/#contact/
The contact form was automatically displayed whenever the string contact/ appeared in the URL fragment.
In this case, the vulnerable behavior was related to how the application handled user input in the Message textarea.
When a user clicked the Submit button, a JavaScript function called strip() was executed to remove HTML tags from the submitted message.
Here is the JavaScript code involved:
// Marketo Form Code
function strip(html) {
var tmp = document.createElement("DIV");
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || "";
}
$('form').submit(function() {
$('textarea').val(function() {
return strip($(this).val());
});
});// Marketo Form Code
function strip(html) {
var tmp = document.createElement("DIV");
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || "";
}
$('form').submit(function() {
$('textarea').val(function() {
return strip($(this).val());
});
});At first glance, this code might look harmless.
The developer's intention was simple: remove HTML tags from the user's input before submitting the form.
However, the implementation introduced an interesting security concern.
2. Why Is innerHTML Dangerous?
The main problem was this line:
tmp.innerHTML = html;tmp.innerHTML = html;The innerHTML property tells the browser to interpret the supplied string as HTML.
Instead of treating the input as plain text, the browser parses it as HTML markup.
For example, if someone provides HTML containing an image element, the browser may attempt to load the referenced image.
If the element contains an event handler, that handler may also become relevant to script execution, depending on how the browser processes the element and the applicable security policies.
This is why using innerHTML with untrusted user input requires careful handling.
A function designed to remove HTML tags should not introduce another opportunity for unsafe HTML processing.
3. Testing the Contact Form
The reported issue could be investigated through the Message textarea in the contact form.
The following proof-of-concept payload was used in the original report:
<img src=x onerror=alert(1) /><img src=x onerror=alert(1) />The purpose of this payload was to test whether the browser would process the HTML element and execute its event handler when the image failed to load.
After entering the payload into the Message field and clicking the Send Message button, the browser's developer console displayed two errors.
First error:
The browser attempted to request:
The request returned a 404 response because the image source pointed to a nonexistent resource.
Second error:
The browser reported a Content Security Policy violation.
This was particularly interesting because it showed that the browser attempted to process the event handler, but the website's CSP prevented the inline JavaScript from executing.
In other words, the security policy acted as an additional layer of protection.
4. How Did CSP Affect the Vulnerability?
Content Security Policy (CSP) is a browser security mechanism that helps reduce the risk of attacks such as Cross-Site Scripting.
It allows developers to define which sources of scripts and other resources are trusted by their applications.
In this case, the injected inline JavaScript was blocked by the website's CSP.
The original report also mentioned that the same behavior was tested in a browser environment without CSP enforcement, where the JavaScript executed.
This is an important distinction.
The vulnerable DOM operation and the successful execution of JavaScript are two separate things.
The use of innerHTML created the unsafe processing behavior, while CSP reduced the possibility of turning that behavior into an executable XSS attack in the tested environment.
However, CSP should never be treated as a replacement for proper input handling and output security.
5. Why Was This Called Self DOM-Based XSS?
One interesting aspect of this vulnerability was its classification as self DOM-based XSS.
Unlike a typical reflected or stored XSS vulnerability, the reported behavior required the user to interact with the contact form.
An attacker could not simply send a victim a link and expect the JavaScript to execute automatically.
The attacker would need to convince the victim to open the contact form, paste a crafted payload into the Message field, and submit it.
This introduced a social-engineering requirement.
Because of this additional interaction and the protection provided by CSP, the practical impact was significantly reduced.
It is important to understand that identifying unsafe JavaScript behavior does not automatically mean that a vulnerability is exploitable against every visitor.
The actual impact depends on the execution conditions, browser protections, and user interaction required.
6. Another Interesting Observation: Client-Side Sanitization Can Be Bypassed
There was another important security lesson in this report.
The strip() function modified the textarea value using JavaScript before the form was submitted.
But client-side JavaScript runs inside the user's browser, which means users can modify the behavior of that code.
For example, an intercepting proxy can be used to inspect and modify HTTP requests during authorized security testing.
If the browser removes HTML from the message before sending it, the submitted request can still be modified independently of that JavaScript processing.
This means client-side sanitization cannot be trusted as the only security control.
Any important validation or sanitization must also be enforced on the server.
Otherwise, an attacker may be able to bypass the browser-side checks entirely.
7. How Should Developers Fix This Issue?
There are several ways developers can prevent this type of problem.
1. Avoid using innerHTML with untrusted input
If the application expects plain text, it should handle the input as plain text rather than parsing it as HTML.
For example:
function displayMessage(userInput) {
var element = document.createElement("div");
element.textContent = userInput;
return element;
}function displayMessage(userInput) {
var element = document.createElement("div");
element.textContent = userInput;
return element;
}The textContent property treats the supplied value as text instead of interpreting it as HTML.
2. Perform validation on the server
Client-side validation is useful for improving the user experience, but it should never be the only security mechanism.
The server should independently validate and sanitize incoming data according to the application's requirements.
3. Use a trusted HTML sanitization library
If an application genuinely needs to accept HTML, developers should use a well-maintained HTML sanitization library such as DOMPurify.
It is much safer than trying to remove dangerous HTML using custom JavaScript or regular expressions.
4. Maintain a strong Content Security Policy
A properly configured CSP can reduce the impact of certain XSS vulnerabilities.
However, it should be treated as defense in depth, not as the primary solution.
8. What Did I Learn From This Case?
This case highlights something important about JavaScript security.
Sometimes, developers introduce security problems while trying to protect their applications.
A function created to remove HTML tags can become unsafe when it relies on innerHTML to process untrusted input.
I also found the CSP behavior particularly interesting. Even though the browser processed the injected HTML, the security policy prevented the inline JavaScript from executing in the tested environment.
For bug bounty hunters, this is a useful reminder:
Don't stop your investigation after finding a suspicious JavaScript function. Understand how the data flows, where it is processed, whether it reaches an executable context, and what browser security controls are in place.
A potential vulnerability and a practically exploitable vulnerability are not always the same thing.
Final takeaway: Never trust client-side sanitization alone, avoid unsafe DOM operations, and always validate untrusted data on the server.
Happy Hunting!
AI assistance:_ I used an AI-powered grammar and spelling checker to review this article. The ideas, research, technical analysis, and final content are my own._
Have questions or want to share your own workflow? Drop a comment below or connect with me on :
🔗Linkdin: https://www.linkedin.com/company/sam-shield/
🔗Youtube: https://www.youtube.com/@sam_shield-w7l
🔗Medium: https://medium.com/@samadhanshimple