Stored XSS


The application is vulnerable to stored cross site scripting attacks. Attackers can insert JavaScript into requests which are then stored by the application.  When the page is eventually loaded, it will be modified to include the malicious script and executed under the context of the user. This malicious data persists and continues to execute each time a user loads the page.

Custom Description

The following pages and parameters were vulnerable to XSS:

[Insert URLs and parameters]


An attacker who can trick a user into clicking on a vulnerable link that contains injected JavaScript is able to run scripts within the browser. Once an attacker is able to execute scripts on behalf of the user, there is very little they are unable to do. Cross-site scripting attacks can be used to steal passwords and session IDs, redirect users to malicious sites, or download malware. The impact is especially significant if the vulnerability is available externally or prior to authentication where it is more easily discoverable and exploited by attackers.

Stored XSS is particularly pernicious for a number of reasons. For one, stored XSS doesn’t require a victim to click on a link as is necessary with reflected XSS. Simply loading a page on an exploited website is enough to trigger the attack.  In addition, the attack is not targeted at any one user but at all users that load the infected page.  As a result, an attacker can potentially exploit a large number of users based on the amount of traffic the page receives. Lastly, this attack is not time-dependent. It is possible that the payload could be days or months after the original attack depending on how long the page contents are left unmodified.

Risk Rating 

Likelihood – Medium

Impact – High

Overall Risk – High


To ensure that stored user input is not executed by the browser, the application should output encode user data into HTML entities. This ensures that the browser will only display user input and not interpret it as a command. At a minimum, output encode the following characters:

  • “<” = “&lt;”
  • “>” = “&gt;”
  • “&” = “&amp;”
  • “/” = “&#47;”
  • single quote = “&#39;”
  • double quote = “&quot;”

Encoding is also context specific. For example, inserting into JavaScript requires JavaScript escaping, CSS requires CSS escaping, and URL requires URL escaping.

It is advisable to utilize a standard library for output encoding. There are libraries available for most platforms, including the OWASP Enterprise Security API (ESAPI).

How To Test

Automated testing

  1. Configure Burp Suite as a proxy and configure a browser to use it.
  2. Identify requests that contain user input.
  3. In Burp under the Proxy tab ->HTTP history, right click on these requests and send them to Scanner. Under Options in Scanner, make sure Stored XSS is checked.

Manual testing

  1. Identify requests that store user input in the database such as user comments.
  2. For each parameter in a form, insert a unique string such as “pizza”.
  3. If the field is returned, try an actual XSS payload such as ‘><svg/onload=alert(‘field 1’)>.
  4. Submit the form and load the results. If the JavaScript triggers, the field is vulnerable.
  5. If the string is not returned or is encoded, begin testing filter evasion techniques as indicated in the Reflected XSS finding.

Sample Report Screenshots

Time Saving Tips

When submitting strings in stored parameters, give each string a unique name (instead of alert(0)) so if the string appears in another part of the application, you’ll know where it came from. Appending a string to the name of the parameter is how we approach it..


In some cases, a parameter on a form might be vulnerable to stored cross site scripting, but you may never know because you don’t have access to the output. An example might be an admin page that shows log files. Unless you have the admin account, you might not know the page is vulnerable. Sometimes this is referred to as blind or out-of-band XSS. One way to test is to use Burp Collaborator to generate a payload that will make a call out to the Burp Collaborator server when fired. Burp Collaborator will even indicate if a DNS lookup has occurred indicating that the server attempted to resolved the domain. In other cases, the payload might not fire for days or weeks depending on how often the page is loaded. In this case, it is helpful to use a service such as XSS Hunter, which will give you a subdomain to use as part of your payload and then email you if the payload is triggered at some point in the future.





Want access to the full AppSec Findings Database? Click here to subscribe today.



Have a thought or question? Leave a comment below.

Leave a Reply