Files
gpt/account-manager/node/fingerprint.js
T
432539 fef45134d5 Initial commit: GPT 账号注册机 (open source)
只开源「GPT 账号注册机 / 管理」(account-manager):账号列表/导入导出、检测/批量检测、
拿 RT、协议/浏览器注册等注册机能力。批量直开 / 协议支付 / 提取链接 / 直卡直开 等其它功能
不含实现代码,仅通过 iframe 内嵌外链访问。真实机密均由 .gitignore 忽略,仅提供脱敏示例配置。
2026-08-05 14:54:32 +08:00

119 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 反检测:为每个注册账号合成一套「内部自洽」的浏览器/设备指纹 + 随机姓名 + 成年生日。
// 目的:以前所有账号都写死同一 UAChrome/145 Windows)、同一屏幕/语言、姓名只在 6×6 里选、
// 年龄挤在 1995~2002,风控极易把这些注册归成同一批机器人。这里让「每账号一套、全程一致、
// 账号间不同」——UA / sec-ch-ua / platform / screen / navigator 全部彼此对齐(否则 sec-ch-ua
// 与 UA 不符本身就是强特征)。
import crypto from 'node:crypto';
function pick(arr) { return arr[crypto.randomInt(0, arr.length)]; }
// 每个 profile 固定「Chrome 大版本 ↔ 真实的 sec-ch-ua 品牌串」配对:sec-ch-ua 的 GREASE 品牌
// "Not?A_Brand" 之类)与版本号是浏览器构建时生成的,随便拼会露馅,所以直接采样真实组合。
const CHROME_PROFILES = [
{ major: 140, secChUa: '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"' },
{ major: 141, secChUa: '"Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"' },
{ major: 142, secChUa: '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"' },
{ major: 143, secChUa: '"Not;A=Brand";v="99", "Google Chrome";v="143", "Chromium";v="143"' },
{ major: 144, secChUa: '"Google Chrome";v="144", "Chromium";v="144", "Not.A/Brand";v="24"' },
{ major: 145, secChUa: '"Chromium";v="145", "Not)A;Brand";v="8", "Google Chrome";v="145"' },
{ major: 138, secChUa: '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"' },
{ major: 139, secChUa: '"Chromium";v="139", "Not_A Brand";v="24", "Google Chrome";v="139"' },
];
// OS 决定 UA 平台段、navigator.platform、sec-ch-ua-platform 三者必须一致。
const OS_PROFILES = [
{ uaToken: 'Windows NT 10.0; Win64; x64', platform: 'Win32', chPlatform: '"Windows"' },
{ uaToken: 'Macintosh; Intel Mac OS X 10_15_7', platform: 'MacIntel', chPlatform: '"macOS"' },
];
// 常见桌面分辨率(宽x高);taskbar/书签栏占用后 availHeight 略小于 height,贴近真机。
const SCREENS = [
{ width: 1920, height: 1080 },
{ width: 2560, height: 1440 },
{ width: 1536, height: 864 },
{ width: 1440, height: 900 },
{ width: 1366, height: 768 },
{ width: 1680, height: 1050 },
];
// 英文母语区:语言/locale 保持在英语圈内,避免 UA 是美式 Chrome 却报 zh-CN 这种矛盾。
const LOCALES = [
{ language: 'en-US', accept: 'en-US,en;q=0.9', tz: 'America/New_York' },
{ language: 'en-US', accept: 'en-US,en;q=0.9', tz: 'America/Chicago' },
{ language: 'en-US', accept: 'en-US,en;q=0.9', tz: 'America/Los_Angeles' },
{ language: 'en-GB', accept: 'en-GB,en;q=0.9', tz: 'Europe/London' },
{ language: 'en-CA', accept: 'en-CA,en;q=0.9', tz: 'America/Toronto' },
];
// 生成一套彼此自洽的指纹。同一账号整条注册流程复用同一个对象;不同账号各自独立随机。
export function generateFingerprint() {
const chrome = pick(CHROME_PROFILES);
const osp = pick(OS_PROFILES);
const screen = pick(SCREENS);
const loc = pick(LOCALES);
const hardwareConcurrency = pick([4, 8, 12, 16]);
const deviceMemory = pick([8, 16]); // Chrome 只暴露 8/16 这类粗粒度值
const userAgent =
`Mozilla/5.0 (${osp.uaToken}) AppleWebKit/537.36 (KHTML, like Gecko) ` +
`Chrome/${chrome.major}.0.0.0 Safari/537.36`;
return {
userAgent,
secChUa: chrome.secChUa,
secChUaMobile: '?0', // 桌面固定 ?0,与 UA 无移动标记一致
secChUaPlatform: osp.chPlatform,
platform: osp.platform,
language: loc.language,
acceptLanguage: loc.accept,
languages: `${loc.language},${loc.language.split('-')[0]}`,
timezone: loc.tz,
screen: {
width: screen.width,
height: screen.height,
// availHeight 减去任务栏/系统栏的典型高度,availWidth 通常等于 width。
availWidth: screen.width,
availHeight: screen.height - 40,
colorDepth: 24,
pixelDepth: 24,
},
screenStr: `${screen.width}x${screen.height}`,
hardwareConcurrency,
deviceMemory,
};
}
// 扩充姓名池:各数十个真实常见英文名,随机组合,避免总是那几对被风控聚类。
const FIRST_NAMES = [
'James', 'Michael', 'Robert', 'John', 'David', 'William', 'Richard', 'Joseph',
'Thomas', 'Christopher', 'Daniel', 'Matthew', 'Anthony', 'Andrew', 'Joshua',
'Ryan', 'Brandon', 'Justin', 'Benjamin', 'Samuel', 'Nathan', 'Aaron', 'Adam',
'Mary', 'Patricia', 'Jennifer', 'Linda', 'Elizabeth', 'Barbara', 'Susan',
'Jessica', 'Sarah', 'Karen', 'Emily', 'Emma', 'Olivia', 'Sophia', 'Ava',
'Isabella', 'Mia', 'Charlotte', 'Amelia', 'Hannah', 'Grace', 'Rachel',
'Laura', 'Megan', 'Lauren', 'Victoria',
];
const LAST_NAMES = [
'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis',
'Rodriguez', 'Martinez', 'Hernandez', 'Lopez', 'Gonzalez', 'Wilson', 'Anderson',
'Thomas', 'Taylor', 'Moore', 'Jackson', 'Martin', 'Lee', 'Perez', 'Thompson',
'White', 'Harris', 'Sanchez', 'Clark', 'Ramirez', 'Lewis', 'Robinson', 'Walker',
'Young', 'Allen', 'King', 'Wright', 'Scott', 'Torres', 'Hill', 'Green',
'Adams', 'Baker', 'Nelson', 'Carter', 'Mitchell', 'Roberts', 'Turner',
'Phillips', 'Campbell', 'Parker', 'Evans',
];
export function randomFullName() {
return `${pick(FIRST_NAMES)} ${pick(LAST_NAMES)}`;
}
// 成年生日:约 18~48 岁均匀取样,月/日在安全范围(1~28)避免非法日期,格式 YYYY-MM-DD 与旧逻辑一致。
export function randomAdultBirthdate() {
const nowYear = new Date().getUTCFullYear();
const age = 18 + crypto.randomInt(0, 31); // 18..48
const year = nowYear - age;
const month = 1 + crypto.randomInt(0, 12);
const day = 1 + crypto.randomInt(0, 28);
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
}