---

title: GraphQL持久化查询与Allowlist门禁(SHA256/签名/回退)最佳实践

keywords:

  • Persisted Query
  • Allowlist
  • SHA256
  • 签名
  • 回退策略

description: 通过SHA256持久化查询与Allowlist门禁、签名校验与受控回退,阻断任意查询注入并优化性能。

categories:

  • 文章资讯
  • 技术教程

---

背景与价值

持久化查询可减少负荷并防止注入。结合Allowlist与签名治理,保证入口仅执行受控查询。

统一规范

  • 哈希标识:统一使用 sha256 标识持久化查询。
  • 门禁策略:只允许白名单哈希;可选签名校验。
  • 受控回退:无哈希时可选择拒绝或受限回退。

核心实现

哈希与白名单

type Persisted = { hash: string; query: string }
const allow = new Map<string, string>() // hash -> query

function validHash(h: string): boolean { return /^[a-f0-9]{64}$/.test(h) }
function registered(h: string): boolean { return allow.has(h) }

签名校验(可选)

async function importKey(spki: ArrayBuffer): Promise<CryptoKey> { return crypto.subtle.importKey('spki', spki, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']) }
function enc(s: string): Uint8Array { return new TextEncoder().encode(s) }
async function verifySig(key: CryptoKey, payload: string, sigB64: string): Promise<boolean> { const sig = Uint8Array.from(atob(sigB64.replace(/-/g,'+').replace(/_/g,'/')), c => c.charCodeAt(0)).buffer; return crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, key, sig, enc(payload)) }

执行门禁

type Req = { headers: Record<string, string | undefined>; body: { hash?: string; query?: string; variables?: Record<string, any>; sig?: string } }

async function gate(req: Req, pub?: CryptoKey): Promise<{ ok: boolean; query?: string }> {
  const h = req.body.hash || ''
  if (!validHash(h)) return { ok: false }
  if (!registered(h)) return { ok: false }
  const q = allow.get(h)!
  if (pub && req.body.sig) { const ok = await verifySig(pub, h, req.body.sig); if (!ok) return { ok: false } }
  return { ok: true, query: q }
}

落地建议

  • 默认仅接受持久化查询;必要时开启签名校验以防哈希被劫持。
  • 无哈希请求按受控回退策略处理(例如拒绝或仅允许少量读查询)。

验证清单

  • 哈希是否有效且命中白名单;签名是否验证通过;回退策略是否受控。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部