You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用AES-256-GCM生成访问令牌时,公开拼接的IV与Auth Tag是否安全?

将IV和Auth Tag随令牌公开是否安全?

核心结论

公开IV和Auth Tag本身是安全的,但你的令牌实现存在其他需要修复的安全隐患,下面详细说明:

为什么IV和Auth Tag可以公开?

  • IV(初始化向量):AES-GCM中IV的作用是确保相同明文每次加密生成不同密文,它不需要保密,只需要保证每次加密使用唯一的IV(你的代码里用crypto.randomBytes(8)生成随机IV,满足唯一性要求)。公开IV不会泄露任何明文或密钥相关信息。
  • Auth Tag(认证标签):GCM模式的Auth Tag用于验证密文的完整性和真实性,它是由密钥、IV和密文计算得出的,本身不包含明文内容。没有密钥的情况下,攻击者无法通过Auth Tag还原明文,也无法伪造有效的Auth Tag篡改密文。

你的代码存在的关键安全问题

  1. IV长度不符合最佳实践
    AES-GCM推荐使用12字节(96位)的IV,这是NIST指定的最优长度,能最大化安全性和性能。你当前使用的是8字节IV,虽然不会直接导致漏洞,但会降低加密方案的安全性上限,建议修改为:

    const iv = crypto.randomBytes(12).toString('hex');
    
  2. 缺少Cookie安全属性
    令牌存储在浏览器Cookie中时,必须设置以下属性防止常见攻击:

    • HttpOnly:禁止JavaScript读取Cookie,防范XSS攻击
    • Secure:仅在HTTPS连接下传输Cookie(生产环境必须开启)
    • SameSite: 'strict':限制Cookie仅在同源请求中发送,防范CSRF攻击
    • maxAge:设置与令牌过期时间一致的Cookie有效期
  3. 未强制验证令牌过期时间
    你的代码在解密时只解析了expires_at,但没有主动验证当前时间是否超过过期时间。攻击者可能篡改客户端时间或Cookie有效期,导致过期令牌仍被使用。建议在decryptAccessToken函数中添加验证逻辑:

    const payload = JSON.parse(payloadStr);
    if (dayjs().isAfter(dayjs(payload.expires_at))) {
      throw new Error("Access token has expired");
    }
    

完整优化后的代码示例

import crypto from 'crypto';
import dayjs from 'dayjs';
import { env } from './env';

type User = { /* 你的User类型定义 */ };
type Result<T> = { ok: true; value: T } | { ok: false; error: unknown };
type AccessTokenPayload = {
  created_at: string;
  expires_at: string;
  user: User;
};

export const encryptAccessToken = (user: User): Result<string> => {
  try {
    // 使用推荐的12字节IV
    const iv = crypto.randomBytes(12).toString('hex');
    const cipher = crypto.createCipheriv('aes-256-gcm', env.CIPHER_SECRET, iv);
    const payload = {
      created_at: dayjs().format('YYYY-MM-DDTHH:mm:ss'),
      expires_at: dayjs().add(1, 'day').format('YYYY-MM-DDTHH:mm:ss'),
      user,
    };
    const cipherStr = cipher.update(JSON.stringify(payload), 'utf-8', 'hex') + cipher.final('hex');
    const accessToken = [iv, cipherStr, cipher.getAuthTag().toString('hex')].join('.');

    return {
      ok: true,
      value: accessToken,
    };
  } catch (error) {
    return { ok: false, error };
  }
};

export const decryptAccessToken = (accessToken: string): Result<AccessTokenPayload> => {
  try {
    const [iv, cipher, authTag] = accessToken.split('.');
    if (!iv || !cipher || !authTag) {
      throw new Error("Invalid access token format");
    }

    const decipher = crypto.createDecipheriv('aes-256-gcm', env.CIPHER_SECRET, iv);
    decipher.setAuthTag(Buffer.from(authTag, 'hex'));
    const payloadStr = decipher.update(cipher, 'hex', 'utf-8') + decipher.final('utf-8');
    const payload = JSON.parse(payloadStr) as AccessTokenPayload;

    // 强制验证令牌过期时间
    if (dayjs().isAfter(dayjs(payload.expires_at))) {
      throw new Error("Access token has expired");
    }

    return {
      ok: true,
      value: payload,
    };
  } catch (error) {
    return { ok: false, error };
  }
};

内容的提问来源于stack exchange,提问作者xqcccccccccc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:23:27