ASVS 5.0 requirement V3.5.7 says data that requires authorization must not be included in script resource responses, such as JavaScript files, to prevent Cross-Site Script Inclusion (XSSI). It is a Level 3 requirement.
Why it matters
Any page on any site can include your script with <script src="https://app.example.com/config.js">. The browser sends the visitor's cookies with that request and runs the response. Same-origin policy stops the attacker's page from reading your JSON through fetch, but it does not stop the script from executing and defining global variables, overriding built-in functions or leaking values the attacker's page can then read.
So a response like this is a data leak:
var CURRENT_USER = {"id": 42, "email": "a@b.test", "apiKey": "k_live_..."};
Dynamic scripts that embed per user data are the usual cause: bootstrap config files, window.__STATE__ bundles, localization files that include account names, and JSONP style endpoints, see V3.5.6.
How to implement it
Serve scripts as static, identical-for-everyone files. Deliver per user data from an authenticated JSON endpoint that is not script includable.
// static: /static/app.js has no user data
// data: JSON endpoint, strict type, requires session and a custom header
app.get("/api/me", requireAuth, (req, res) => {
if (req.get("X-Requested-With") !== "app") return res.status(403).end();
res.type("application/json").json({ id: req.user.id, email: req.user.email });
});
Add X-Content-Type-Options: nosniff so a JSON response cannot be treated as script, see V3.4.4. For authenticated assets, Cross-Origin-Resource-Policy: same-origin blocks cross-site loading outright.
If you must inline initial state into HTML, do it in the page itself (a document, not a script resource), encode it safely, and send it only to the signed in user.
How to test it
Fetch every script URL with a valid session and without one, and diff:
for u in /static/app.js /config.js /i18n/en.js; do
curl -s https://app.example.com$u -b cookies.txt | sha256sum
curl -s https://app.example.com$u | sha256sum
done
Hashes must match, meaning the response does not depend on who asks. Grep script responses for emails, names, tokens and ids from a test account. Then include a script from another origin on a test page and check that no private global appears.
Common mistakes
Bootstrap scripts with user data. Move them to an authenticated JSON call.
API keys in config.js. Treat anything in a script as public.
Array or JSON responses served as script. Use correct JSON content type and nosniff.
Quick check
Is a script that only the logged in user can load safe?
No. The attacker's page makes the logged in visitor's browser load it, using their cookies.
Sources
- OWASP ASVS 5.0, requirement V3.5.7
- MDN: Cross-Origin-Resource-Policy