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 {
|
||||
// IPv6 loopback/unspecified
|
||||
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
|
||||
if (ip.startsWith("::ffff:")) ip = ip.slice(7);
|
||||
if (!net.isIPv4(ip)) return false;
|
||||
|
||||
const parts = ip.split(".").map(Number);
|
||||
if (parts[0] === 0) return true; // 0.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 {
|
||||
const { address } = await dns.lookup(parsed.hostname);
|
||||
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;
|
||||
}
|
||||
} catch {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue