UNPKG

@kanadi/core

Version:

Multi-Layer CAPTCHA Framework with customizable validators and challenge bundles

72 lines (62 loc) 1.97 kB
import { createHash } from "crypto"; export interface ServerFingerprintData { ip: string; userAgent: string; acceptLanguage?: string; acceptEncoding?: string; connection?: string; headers: Record<string, string>; } function normalizeIP(ip: string): string { if (ip.startsWith("::ffff:")) { return ip.substring(7); } return ip; } function parseUserAgent(userAgent: string): string { const parts = userAgent.split(/[\s/()]+/); return parts .filter((p) => p && p.length > 2) .slice(0, 5) .join("|"); } export function generateServerFingerprint(data: { ip: string; userAgent?: string; headers?: Record<string, string | string[] | undefined>; }): { fingerprint: string; data: ServerFingerprintData } { const normalizedIP = normalizeIP(data.ip); const userAgent = data.userAgent || ""; const headers = data.headers || {}; const getHeader = (key: string): string => { const value = headers[key.toLowerCase()]; if (Array.isArray(value)) return value[0] || ""; return value || ""; }; const fingerprintData: ServerFingerprintData = { ip: normalizedIP, userAgent: parseUserAgent(userAgent), acceptLanguage: getHeader("accept-language"), acceptEncoding: getHeader("accept-encoding"), connection: getHeader("connection"), headers: { "accept-language": getHeader("accept-language"), "accept-encoding": getHeader("accept-encoding"), "sec-ch-ua": getHeader("sec-ch-ua"), "sec-ch-ua-mobile": getHeader("sec-ch-ua-mobile"), "sec-ch-ua-platform": getHeader("sec-ch-ua-platform"), }, }; const fingerprintString = JSON.stringify(fingerprintData); const fingerprint = createHash("sha256") .update(fingerprintString) .digest("hex"); return { fingerprint, data: fingerprintData }; } export function combineFingerprints( clientFingerprint: string, serverFingerprint: string, ): string { const combined = `${clientFingerprint}:${serverFingerprint}`; return createHash("sha256").update(combined).digest("hex"); }