ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

capjs-core 无状态 CAPTCHA 挑战生成与校验:Cap 服务端核心库的完整 API 实战指南

capjs-core 无状态 CAPTCHA 挑战生成与校验:Cap 服务端核心库的完整 API 实战指南 网络安全应用安全后端【免费下载链接】capFree, open-source and self-hosted CAPTCHA alternative to reCAPTCHA. Privacy-first and powered by proof-of-work and instrumentation challenges.项目地址https://gitcode.com/gh_mirrors/cap13/cap点击查看免费下载导读capjs-core 是自托管 CAPTCHA 项目 Cap 的无状态服务端核心库它负责生成工作量证明Proof-of-Work挑战、校验客户端提交的解并将 instrumentation 挑战元数据以加密形式封装进 JWT服务端无需任何存储即可完成校验。读完本文你将掌握generateChallenge/validateChallenge两个核心 API 的全部参数语义、JWT 与 AES-256-GCM 的底层实现原理、防重放replay接入方式、从旧版cap.js/server的迁移路径以及各配置项的性能影响与调优方向。一、定位Stateless 的挑战生成与验证capjs-core 是 Cap 生态中纯服务端、纯密码学的一层。它的设计目标可以从 package.json 的描述中直接读出Stateless server-side challenge generator and verifier for Cap. No storage, no filesystem — bring your own.安装方式bun add capjs-core # or: npm install capjs-core包内以 ESM 形式提供入口为 src/index.js类型声明见 src/index.d.ts。最基础的两步用法如下import { generateChallenge, validateChallenge } from capjs-core; // long, random, high-entropy. keep this consistent across processes. const SECRET process.env.CAP_SECRET; // 1) Server route: create a challenge const challenge await generateChallenge(SECRET, { scope: signup, // optional instrumentation: true, // optional }); // → { challenge: { c, s, d }, token, expires, instrumentation? } // 2) Server route: validate the redeemed challenge const result await validateChallenge(SECRET, { token: req.body.token, solutions: req.body.solutions, instr: req.body.instr, }, { scope: signup, // optional: prevent replay consumeNonce: async (sigHex, ttlMs) await myStore.setIfNotExists(cap:${sigHex}, 1, ttlMs), }); if (result.success) { // result.token, result.tokenKey, result.expires, result.scope }无状态体现在三个层面均可在源码中找到直接证据没有文件系统与进程级状态generateChallenge不依赖任何fs、process.on(SIGINT)或内存 token 列表挑战 token 是 HS256 签名的 JWT实现见 src/crypto.js 的jwtSign/jwtVerify校验是纯密码学操作instrumentation 元数据加密进 JWT启用 instrumentation 时服务端把expectedVals、vars等标准答案用 AES-256-GCM 加密后写入 token payloadpayload.ei服务端无需记忆任何内容即可在验证时解密比对src/index.js防重放是可选回调通过consumeNonce回调接入你自己的 KVRedisSET NX EX等核心库本身不内置 token 存储。二、generateChallenge生成一次挑战2.1 参数语义generateChallenge(secret, opts?) → { challenge, token, expires, instrumentation? }secret— string 或 Buffer至少 16 字节。它是 JWT 签名与 instrumentation 元数据加密的主 HMAC 密钥。源码 src/index.js 的assertSecret会强制校验空值、非 string/Buffer、长度小于 16 字节都会直接抛错opts.challengeCount— 默认50源码常量DEFAULT_CHALLENGE_COUNT即客户端需要求解的子挑战个数opts.challengeSize— 默认32每个子挑战盐salt的十六进制长度opts.challengeDifficulty— 默认4即要求 SHA-256 哈希以多少个0开头的十六进制前缀opts.expiresMs— 默认60000010 分钟挑战 JWT 的有效期opts.scope— 可选字符串绑定到挑战验证时opts.scope必须一致opts.extra— 可选对象原样嵌入 JWT payloadopts.instrumentation—true或{ blockAutomatedBrowsers, obfuscationLevel }对象opts.instrumentationGenerator— 可选的自定义生成器用于把开销较大的 instrumentation 生成卸载到 worker 池。2.2 参数边界与生成流程从源码看参数不仅有默认值还有严格的边界校验src/index.jschallengeCount必须是[1, 1000]的整数challengeSize必须是[1, 256]的整数challengeDifficulty必须是[1, 16]的整数越界直接抛错。生成流程format-1默认格式如下生成随机 noncerandomHex(25)与exp、iat可选写入skscope与xextra若开启 instrumentation调用generateInstrumentation生成混淆后的客户端脚本并把它对应的元数据{ id, expectedVals, vars, blockAutomatedBrowsers, expires }用encryptGcm(..., secret)加密为payload.ei压缩后的脚本本体deflate base64随结果返回给客户端jwtSign用 HMAC-SHA256 签发 token返回{ challenge: { c, s, d }, token, expires, instrumentation? }。2.3 instrumentation 选项opts.instrumentation传对象时支持两个子选项src/index.d.tsblockAutomatedBrowsers— 布尔值开启后会在客户端脚本中嵌入 20 项自动化检测Selenium / Playwright / PhantomJS 等标记检查、webdriver属性检测、UA token 匹配、WebGL 渲染器特征、[native code]完整性等标记表见 src/instrumentation.jsobfuscationLevel— 数值默认3范围 110level ≤ 3 仅做空白压缩47 做字符串提取混淆 esbuild minify≥ 8 调用可选的javascript-obfuscator字符串数组 RC4/base64 编码、控制流扁平化、死代码注入等见 src/instrumentation.js。需要特别注意的是性能权衡源码注释明确提示 Instrumentation generation at level 3 blocks the event loopsrc/index.jsobfuscationLevel 越高单次生成耗时越大README 实测 level 8 约为 5 ops/s。若对吞吐有要求应传入自定义instrumentationGenerator在 fork 出的子进程或 worker 中执行。三、validateChallenge验证并换发赎回 token3.1 请求体与选项validateChallenge(secret, body, opts?) → { success, ... }body字段token— 来自generateChallenge的挑战 tokensolutions— 数字数组长度必须等于challenge.cinstr— instrumentation 结果若启用instr_blocked、instr_timeout— widget 上报的标志位。opts字段scope— 必须与原始挑战的 scope 一致tokenTtlMs— 换发出的赎回 token 的 TTL默认 20 分钟consumeNonce(sigHex, ttlMs)— 通过你的存储做防重放signToken(data)— 异步函数返回自定义的赎回 token 格式。3.2 校验顺序源码级校验按严格顺序短路执行任一失败立即返回{ success: false, reason }src/index.jsbody 非对象 →invalid_body缺 token →missing_tokensolutions 非数组 →missing_solutionsJWT 验签失败/畸形/参数越界 →invalid_token验签使用timingSafeEqual常数时间比较src/crypto.jsscope 不匹配 →scope_mismatchexp Date.now()→expiredsolutions 长度不等于c、或含非数字 →invalid_solutionsPoW 验证对每个子挑战用 token 的 FNV-1a 哈希派生 salt 与 targetfnv1aResume与prngFromHashsrc/prng.js计算sha256(salt solution)并比对目标前缀不满足 →invalid_solution若 token 含加密的ei解密后校验 instrumentationinstr_corrupted/instr_expired/instr_automated_browser/instr_timeout/instr_missing/instr_failed均带instr_error: true若传了consumeNonce以 JWT 签名的十六进制sigHex为键执行防重放回调抛错 →nonce_store_error返回false→already_redeemed全部通过后换发赎回 token。3.3 默认赎回 token 机制成功时默认返回{ success: true, token, tokenKey, expires, scope, iat }token格式为id:secretrandomHex(8) : randomHex(15)tokenKey为id:HMAC(secret)对 secret 做 SHA-256使用建议README 原话把tokenKey存进数据库、把token交给用户后续验证时对用户提交的 secret 重算tokenKey id:HMAC(submittedSecret)再查库即可——核心库不做任何存储由调用方自行完成。3.4 失败原因速查表reasonmeaninginvalid_bodybody isnt an objectmissing_tokenno token providedmissing_solutionssolutions missing or not an arrayinvalid_tokenJWT signature mismatch / malformed / out-of-bounds paramsscope_mismatchtokens scope doesnt matchopts.scopeexpiredchallenge JWT expiredinvalid_solutionslength mismatch or non-numbersnonce_store_erroryourconsumeNoncecallback threwalready_redeemedconsumeNoncereturnedfalseinvalid_solutionsolutions dont satisfy the PoWinstr_*instrumentation failed (withinstr_error: true)四、PoW 的底层原理如何做到 Stateless 验证format-1 的 PoW 巧妙之处在于验证所需的 salt 与 target 不必随 token 传输而是由 token 本身确定性派生。相关实现分布在 src/prng.js 与 src/crypto.jsfnv1a(token)得到 token 的 FNV-1a 初始哈希对第i个子挑战fnv1aResume(tokenFnv, String(i 1))派生 salt 种子fnv1aResume(saltSeed, d)派生 target 种子prngFromHash(seed, size)用 xorshift 式状态机生成十六进制 saltprngFromHash(targetSeed, difficulty)生成difficulty位十六进制 target校验sha256(salt solutions[i])的哈希是否满足目标前缀powMatchesPrefixsrc/crypto.js。客户端求解时做同样派生并暴力递增 nonce直到sha256Hex(salt n)以 target 开头test/benchmark.js 的solveChallenge给出了标准求解写法。五、防重放Replay Protection接入validateChallenge接受consumeNonce(sigHex, ttlMs)回调完成幂等消费。核心库会以 JWT 签名的十六进制作为唯一键以剩余 TTLmax(1, payload.exp - Date.now())作为过期时间调用你的存储。Redis 的标准写法consumeNonce: async (sigHex, ttlMs) await redis.set(cap:${sigHex}, 1, NX, EX, Math.ceil(ttlMs / 1000)),SET NX EX语义即不存在则写入并设置过期写入成功返回true首次消费、放行重复键返回false→already_redeemed。README 中给的同构示例为myStore.setIfNotExists(cap: sigHex, 1, ttlMs)。核心库只负责调用存储选型完全由你决定。六、性能基准与调优依据README 给出了官方基准环境Apple M 系列、bun 1.3、单核、无 instrumentation。可用bun test/benchmark.js在当前机器上复现test/benchmark.js 实测项目内标准参数集并额外覆盖 c200 s64 d4 大挑战与 scope 场景。无 instrumentationoperationparamsops/sgenerateChallengedefaults (c50 s32 d4)~350,000generateChallengesmall (c5 s16 d2)~450,000validateChallengedefaults~4,400validateChallengesmall~36,000validateChallengeinvalid token (early-exit)~2,400,000validateChallengebad signature (HMAC reject)~310,000带 instrumentationoperationlevelops/sgenerateChallenge instrumentation1 (no obfuscator)~10,700generateChallenge instrumentation3 (default)~44generateChallenge instrumentation8 (string array control flow)~5调优要点均有源码佐证验证是计算密集的默认参数下每个 token 要跑 50 次 SHA-256 前缀比对这是 ~4,400 ops/s 的直接原因非法 token 在验签阶段就被常数时间比较拦截~2.4M ops/s这解释了先验签后算 PoW的顺序设计generation 几乎免费不启用 instrumentation 时generateChallenge只是随机数 JWT 签名可达 35 万 ops/sinstrumentation 是主要成本level 3 起事件循环会被阻塞源码注释原话生产环境高吞吐场景应通过instrumentationGenerator卸载到子进程/worker。七、从cap.js/server迁移旧版 Cap 服务端库内置了 token 存储而capjs-core刻意去掉了存储层。对绝大多数用户官方建议直接使用 Cap StandaloneDocker开箱即用不能跑 Docker 时才用核心库自建。迁移对照表cap.js/servercapjs-corenew Cap({ ... })(no constructor — pass your secret per call)cap.createChallenge(opts)generateChallenge(secret, opts)cap.redeemChallenge({ token, solutions })validateChallenge(secret, { token, solutions })cap.validateToken(redeemToken)look uptokenKeyin your DB yourselfconfig.storage.tokens.*use your own KVconfig.tokens_store_pathgone (no filesystem)SIGINT/beforeExitcleanupgone (TTL via JWTexpand your KVs expiry)关键差异是无构造函数secret 每次调用传入——这带来横向扩展的便利多进程/多实例共享同一个CAP_SECRET即可互验 token不需要共享内存。防重放则显式传入consumeNonce回调核心库以 JWT 签名 hex 和剩余 TTL 调用你的 KV如SET cap:sig 1 NX EX ttl重复则返回false。八、进阶format-2 多协议挑战源码补充在 README 之外核心库还提供 opt-in 的format: 2多协议模式src/index.js适合希望混合不同难度模型的接入方。它支持四种协议可通过protocols数组组合sha256-pow— 与 format-1 相同的哈希前缀 PoWsalt 随机生成rsw— Rivest–Shamir–Wagner 时间锁谜题generateRswKeypair生成 2048 位默认 RSA 风格模数生成约需 700ms 至数秒README 与 src/rsw.js 均建议启动时生成一次并持久化buildRswMinter用 CRT 快速预计算y g^(2^t) mod N客户端必须做t次默认 75,000串行平方才能得到 yhashwx— 基于内嵌 WebAssembly 的 hash-window 哈希src/hashwx.js难度默认 1,000,000拆分为 4 个子挑战每个哈希函数覆盖 65,536 个 nonceinstrumentation— 与 format-1 相同的浏览器环境检测。format-2 中标准答案expected 数组整体用 AES-256-GCM 加密进 JWTpayload.ev依旧保持无状态。需要预生成 RSW 密钥对的完整示例可参考类型声明中的GenerateChallengeOptionssrc/index.d.ts与generateRswKeypair/serializeRswKeypair的文档注释。九、许可证与进一步阅读capjs-core 以 Apache-2.0 许可发布core/LICENSE。相关实现与测试文件均在仓库内核心入口与参数校验core/src/index.js密码学原语JWT HS256、AES-256-GCM、常数时间比较core/src/crypto.jsinstrumentation 生成与校验core/src/instrumentation.js自动化检测detectAutomationcore/src/detect.js类型声明含 format-2 全部接口core/src/index.d.ts基准测试脚本core/test/benchmark.jsbun test/benchmark.js可复现单元测试core/test/core.test.js客户端 widget 与独立部署服务widget/src/src/cap.js、standalone/src/server.js赞分享网络安全应用安全后端【免费下载链接】capFree, open-source and self-hosted CAPTCHA alternative to reCAPTCHA. Privacy-first and powered by proof-of-work and instrumentation challenges.项目地址https://gitcode.com/gh_mirrors/cap13/cap点击查看免费下载相关推荐capjs-core 深入指南Cap 的无状态服务端挑战生成与验证库JWT 工作量证明 插桩capjs core 深入指南Cap 的无状态服务端挑战生成与验证库JWT 工作量证明 插桩 capjs core 是 Cap 项目中面向服务端的一套网络安全应用安全后端Cap 服务端库 cap.js/server 实战指南挑战生成、Token 验证与 capjs-core 迁移路径Cap 服务端库 cap.js/server 实战指南挑战生成、Token 验证与 capjs core 迁移路径 cap.js/server 是开源自托网络安全应用安全后端CodeIgniter CAPTCHA Helper 使用指南从验证码生成到数据库校验的完整实战CodeIgniter CAPTCHA Helper 使用指南从验证码生成到数据库校验的完整实战 CAPTCHA全自动区分计算机和人类的图灵测试是表单防机后端Web框架上一篇GG - Gui for JJ重新定义Git工作流的终极可视化工具下一篇重构右键菜单体验从臃肿到高效的ContextMenuManager焕新指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表