Fix SSRF vulnerability: Add IPv6 link-local blocking and update error message
Some checks failed
Deploy to Production / Deploy to Server (push) Failing after 20s
Some checks failed
Deploy to Production / Deploy to Server (push) Failing after 20s
- Add fe80::/10 (IPv6 link-local) detection to isPrivateIP() - Update error message to match specification: 'URL resolves to a private/internal IP address' - Existing protections already covered all required IPv4 ranges and IPv6 localhost
This commit is contained in:
parent
86f8da62ec
commit
7b55a1ddc6
1 changed files with 7 additions and 1 deletions
|
|
@ -8,9 +8,15 @@ import net from "node:net";
|
||||||
function isPrivateIP(ip: string): boolean {
|
function isPrivateIP(ip: string): boolean {
|
||||||
// IPv6 loopback/unspecified
|
// IPv6 loopback/unspecified
|
||||||
if (ip === "::1" || ip === "::") return true;
|
if (ip === "::1" || ip === "::") return true;
|
||||||
|
|
||||||
|
// IPv6 link-local (fe80::/10)
|
||||||
|
if (ip.toLowerCase().startsWith("fe8") || ip.toLowerCase().startsWith("fe9") ||
|
||||||
|
ip.toLowerCase().startsWith("fea") || ip.toLowerCase().startsWith("feb")) return true;
|
||||||
|
|
||||||
// IPv4-mapped IPv6
|
// IPv4-mapped IPv6
|
||||||
if (ip.startsWith("::ffff:")) ip = ip.slice(7);
|
if (ip.startsWith("::ffff:")) ip = ip.slice(7);
|
||||||
if (!net.isIPv4(ip)) return false;
|
if (!net.isIPv4(ip)) return false;
|
||||||
|
|
||||||
const parts = ip.split(".").map(Number);
|
const parts = ip.split(".").map(Number);
|
||||||
if (parts[0] === 0) return true; // 0.0.0.0/8
|
if (parts[0] === 0) return true; // 0.0.0.0/8
|
||||||
if (parts[0] === 10) return true; // 10.0.0.0/8
|
if (parts[0] === 10) return true; // 10.0.0.0/8
|
||||||
|
|
@ -160,7 +166,7 @@ convertRouter.post("/url", async (req: Request & { acquirePdfSlot?: () => Promis
|
||||||
try {
|
try {
|
||||||
const { address } = await dns.lookup(parsed.hostname);
|
const { address } = await dns.lookup(parsed.hostname);
|
||||||
if (isPrivateIP(address)) {
|
if (isPrivateIP(address)) {
|
||||||
res.status(400).json({ error: "URL resolves to private/reserved IP" });
|
res.status(400).json({ error: "URL resolves to a private/internal IP address" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue