Why setting window.location.href to local storage item value is considered a DOM XSS vulnerability?

Fortify SCA reports that the following code is DOM XSS vulnerable:

const returnUrl = sessionStorage.getItem('returnUrl') || '/';
window.location.href = returnUrl;

When a user enters my application by clicking a link for example, and the user is not logged in:

  1. The app stores window.location.href into returnUrl item in session storage
  2. It sets window.location.href to an external login page
  3. When the user enter their credentials, the external login page redirects to logged-in.html page of my app.
  4. logged-in.html contains the above code, setting window.location.href to the value stored in returnUrl item of session storage.

Why is this vulnerable?. How can I mitigate it?

EDIT:
I din’t execute Fortify, an external company did it, and reported the following:

CWE-80.
Input Validation and
Representation:
Cross-Site Scripting:
DOM

>Solution :

How can I mitigate it?

Don’t store a full URL, just store the necessary information (such as a page name, or even an enum-like value that identifies the page to go to). Then, when going back to the page, validate the data from session storage before building a URL:

const returnInfo = JSON.parse(sessionStorage.getItem("returnUrl"));
if (returnInfo && validatePageName(returnInfo.pageName) && /*...*/) {
    window.location.href = "/" + returnInfo.pageName;
}

…where validatePageName ensures that the string passed to it is just the name of a page in your app, and not (for instance) a full URL.

Or if that’s too much of a change, at least validate the URL before using it:

const returnUrl = new URL(
    sessionStorage.getItem("returnUrl") || "/"),
    location
);
if (
  returnUrl.protocol === location.protocol &&
  returnUrl.port === location.port &&
  returnUrl.hostname === location.hostname &&
  /*...other checks as necessary ...*/
) {
    window.location.href = returnUrl;
}

Leave a Reply