fix(security): authorize site uploads and harden tokens, media and request identity
This commit is contained in:
1 parent
52f6d1491f
commit
8abfe352ef
70 files changed
+1609
-204
No files matched your search
@@ -0,0 +1,37 @@
|
||||
import { isIP } from "node:net";
|
||||
|
||||
export const UNKNOWN_CLIENT_IP = "0.0.0.0";
|
||||
|
||||
/** Accept bare addresses only, so ports, hostnames and zone IDs cannot become keys. */
|
||||
export function normalizeClientIp(
|
||||
value: string | null | undefined,
|
||||
): string | null {
|
||||
const address = value?.trim();
|
||||
if (!address || address.length > 45 || address.includes("%")) return null;
|
||||
const version = isIP(address);
|
||||
if (version === 4) return address;
|
||||
if (version !== 6) return null;
|
||||
const canonical = new URL(`http://[${address}]/`).hostname.slice(1, -1);
|
||||
// Treat an IPv4-mapped IPv6 address as the same client as its dotted form.
|
||||
const mapped = /^::ffff:([a-f0-9]{1,4}):([a-f0-9]{1,4})$/.exec(canonical);
|
||||
if (mapped) {
|
||||
const high = Number.parseInt(mapped[1], 16);
|
||||
const low = Number.parseInt(mapped[2], 16);
|
||||
return `${high >> 8}.${high & 255}.${low >> 8}.${low & 255}`;
|
||||
}
|
||||
return canonical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwarded headers must be overwritten by a trusted ingress and the origin must
|
||||
* reject direct public access. Header syntax alone cannot establish peer trust.
|
||||
* Never consume x-real-client-ip: API routes bypass the proxy that once set it.
|
||||
*/
|
||||
export function resolveClientIp(headers: Pick<Headers, "get">): string {
|
||||
return (
|
||||
normalizeClientIp(headers.get("cf-connecting-ip")) ??
|
||||
normalizeClientIp(headers.get("x-forwarded-for")?.split(",", 1)[0]) ??
|
||||
normalizeClientIp(headers.get("x-real-ip")) ??
|
||||
UNKNOWN_CLIENT_IP
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user