服务端开发安全 Checkpoint 手册

读者对象:Java 高级开发工程师 / 架构师
文档版本:v1.0
标准时效:基于 OWASP Top 10:2025、NIST SP 800-131A Rev2、NIST FIPS 203/204/205、OWASP ASVS v5.0
最后更新:2026-07-31

第 0 章 文档导论

0.1 文档目的与使用方式

本文档面向 Java 服务端开发与架构评审场景,提供系统性的安全自检参考。每节采用统一的三段式结构:关键要点阐述核心原则与选型依据,Checkpoint 列出代码评审与架构评审中需逐项确认的检查点,雷区清单以「禁止…」句式列出高风险反模式。第 4 章密码学为核心重点,额外补充基于 JCA(Java Cryptography Architecture)与 Bouncy Castle 的规范实现代码范例。

文档可作为代码评审 checklist、架构评审 checkpoint 与安全培训材料使用。附录 C 提供可勾选版自检清单,便于上线评审时直接使用。

0.2 标准依据与版本声明

标准来源 版本 关键变更
OWASP Top 10 2025(2025-11 发布) A03 升级为「软件供应链失败」;A10 新增「异常条件处理不当」
OWASP ASVS v5.0 V11 密码学、V12 安全通信、V14 数据保护
NIST SP 800-131A Rev2 SHA-1/RSA-1024/3DES/DES/RC4 已禁用或弃用
NIST FIPS 203/204/205 2024 正式发布 后量子密码标准 ML-KEM/ML-DSA/SLH-DSA
NIST SP 800-63B Rev3 数字身份指南,口令策略长度优先
NIST SP 800-90A Rev1 随机数生成器标准
CWE — CWE-321/327/329/330/338/780/916 等

0.3 术语表

术语 含义
CIA 三性 机密性(Confidentiality)、完整性(Integrity)、可用性(Availability)
AEAD 认证加密,同时提供机密性与完整性(如 AES-GCM、ChaCha20-Poly1305)
前向保密(PFS) 会话密钥泄露不影响历史会话安全,依赖临时密钥交换(ECDHE)
KDF 密钥派生函数,从口令或共享密钥派生密钥(如 PBKDF2、HKDF、Argon2)
CSPRNG 密码学安全随机数生成器(如 Java SecureRandom)
HSM / KMS 硬件安全模块 / 密钥管理服务,用于密钥的安全存储与运算
MAC 消息认证码,带密钥的完整性校验(如 HMAC)
salt 口令哈希的随机盐,每用户唯一,非秘密
pepper 全局秘密盐,存于 HSM/配置,与数据库分离
nonce / IV 一次性随机数 / 初始化向量,加密中使用,不可重复
JCA Java Cryptography Architecture,JDK 内置密码学框架
JCE Java Cryptography Extension,JCA 的扩展部分
PQC 后量子密码,抵御量子计算机攻击的密码算法

0.4 安全设计总原则


第 1 章 认证与授权安全

对应 OWASP Top 10:2025 A01(访问控制失效)、A07(身份认证失效)

1.1 身份认证基础与认证因素

认证(Authentication)解决「你是谁」的问题,授权(Authorization)解决「你能做什么」。两者不可混淆,认证通过不等于授权通过。

认证因素分为三类:知识因素(口令、PIN)、持有因素(令牌、设备)、固有因素(生物特征)。强认证要求至少两种不同类型的因素组合(MFA)。

认证失败的响应必须统一,避免通过错误消息差异或响应时间差异推断用户是否存在(用户枚举攻击)。

1.2 口令策略与口令存储

对应 OWASP A04(加密失败)、A07(身份认证失效)、CWE-916、CWE-759/760

口令存储必须使用「加盐 + 自适应慢哈希」。工作因子可随硬件升级定期提升,使 GPU/ASIC 爆破成本始终高于攻击收益。

算法推荐优先级(OWASP A04 明确建议):

优先级 算法 说明
首选 Argon2id PHC 获奖算法,抗 GPU/ASIC,内存困难型
次选 scrypt 内存困难型,抗 ASIC
可选 bcrypt 广泛使用,cost 可调
兼容 PBKDF2-HMAC-SHA-512 NIST 标准化,但抗 GPU 不如前三者

口令策略应遵循 NIST SP 800-63B:重视长度(建议 ≥12 位)而非强制复杂度规则(大小写+数字+特殊字符的强制组合已弃用)。维护已知泄露口令黑名单,注册/修改口令时比对拦截。

1.3 多因素认证与无密码认证

TOTP(基于时间的一次性密码,如 Google Authenticator)优于短信验证码,因短信存在 SIM 劫持与拦截风险。高敏感场景不应将短信 OTP 作为唯一第二因素。

MFA 注册与重置流程本身的认证强度需与登录等同,否则成为绕过入口。攻击者可通过「忘记设备」流程关闭 MFA。

WebAuthn / Passkey 基于公钥密码学,原生抗钓鱼,是无密码认证的未来方向。服务端存储公钥,私钥永不离开设备。

1.4 授权模型与访问控制

对应 OWASP A01(访问控制失效)

授权遵循默认拒绝原则:权限显式授予,未明确允许即禁止。服务端必须强制执行授权校验,不可信任前端的隐藏菜单、按钮或路由守卫。

对象级授权(IDOR,Insecure Direct Object Reference)是高频漏洞:当资源通过自增 ID 或可猜测标识符暴露时,攻击者通过遍历 ID 访问他人数据。每个资源访问必须校验当前用户对该资源的归属权限。

横向越权指同级别用户间的越权访问,纵向越权指普通用户访问管理功能。两者均需在服务端拦截。

1.5 OAuth2 / OIDC 协议安全

授权码模式(Authorization Code)配合 PKCE 是推荐流程,公开客户端(SPA、移动端)必须使用 PKCE 防止授权码拦截。

state 参数防止 CSRF 攻击,nonce 参数防止 OIDC 重放攻击。两者均需校验。

redirect_uri 必须严格白名单匹配,禁止通配或路径前缀匹配导致开放重定向。

1.6 JWT 与令牌安全

JWT 的签名算法必须在服务端显式指定,禁止信任 JWT header 中的 alg 字段(防 alg=none 攻击与算法混淆攻击)。

access_token 应短生命周期(如 15-30 分钟),refresh_token 可较长但必须可撤销且绑定设备。JWT 本身无状态,撤销需配合黑名单或短时效设计。

JWT payload 仅做 Base64 编码,非加密,禁止存储敏感数据。


第 2 章 会话管理

对应 OWASP A07(身份认证失效)

2.1 会话生命周期

会话需具备创建、续期、过期、销毁的完整闭环。登录成功后必须重新生成会话 ID(防会话固定攻击,CWE-384)。登出时服务端必须真正失效令牌,而非仅清除前端存储。

会话应设置绝对超时(如 8 小时)与空闲超时(如 30 分钟),超时后强制重新认证。

2.2 会话令牌生成与传输

会话令牌必须使用 CSPRNG 生成,长度不低于 128 位熵。令牌通过 Cookie 传输时需同时设置 HttpOnly(防 XSS 读取)、Secure(仅 HTTPS 传输)、SameSite(防 CSRF)。

禁止在 URL 参数中传递会话令牌(Referer 头与日志会泄露)。

2.3 会话固定、劫持与超时

检测 IP 或 User-Agent 的突变可辅助发现会话劫持。并发会话数应有限制。敏感操作(如修改密码、转账)前应要求重新认证(Step-up Authentication)。

权限提升后(如普通用户切换为管理员)必须轮换会话令牌。

2.4 CSRF 防护

SameSite Cookie 属性为首选防御,配合 anti-CSRF token 实现双重防御。状态变更请求必须使用 POST / PUT / DELETE 方法,并校验 Origin / Referer 头。

2.5 分布式会话与 Redis 会话安全

Redis 存储会话需设置 TTL,敏感会话数据应加密或仅存储会话 ID。Redis 实例需配置密码、网络隔离、禁用危险命令(如 FLUSHALL、CONFIG)。


第 3 章 输入输出安全

对应 OWASP A05(注入)、A03(供应链,含 XSS)

3.1 输入验证与数据净化原则

白名单验证优于黑名单。所有外部输入(表单、HTTP Header、Cookie、URL 参数、文件内容)均需在服务端校验,前端校验仅为用户体验,不可作为安全防线。

校验维度包括类型、长度、范围、字符集、业务格式。验证不通过的输入应拒绝,而非尝试净化后使用。

3.2 注入防御

对应 OWASP A05、CWE-89(SQL 注入)、CWE-78(命令注入)

SQL 注入:使用参数化查询或 MyBatis #{} 占位符,禁止字符串拼接 SQL 或使用 ${} 接收用户输入。

命令注入:避免 Runtime.getRuntime().exec() 拼接用户输入,必须执行时使用参数数组形式。

表达式注入:SpEL(Spring Expression Language)、OGNL、MVEL 等表达式引擎直接求值用户输入可导致 RCE,禁止将用户输入作为表达式求值。

NoSQL 注入:校验查询操作符,参数化查询。

3.3 XSS 防御

输出编码需按上下文区分:HTML 上下文用 HTML 实体编码,JavaScript 上下文用 JS 编码,URL 上下文用 URL 编码,CSS 上下文用 CSS 编码。

Content-Security-Policy(CSP)作为深度防御,限制脚本来源。富文本场景使用白名单净化库(如 OWASP Java HTML Sanitizer),禁止黑名单过滤标签。

3.4 SSRF 防御

对应 OWASP Top 10:2025 A01(访问控制失效;2025 起 SSRF 并入 A01)

服务端发起请求前必须校验目标主机,禁止访问内网地址段(127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、169.254.169.254 等)。

需禁用或校验重定向跟随,防止通过 302 跳转绕过地址校验。DNS 重绑定攻击需通过解析后校验 IP、再使用解析结果连接的方式防护。

3.5 文件上传与下载安全

文件校验应基于文件头魔数(magic bytes)而非扩展名。上传文件需重命名、存储于独立目录或域名下、禁止可执行权限。

下载需防止路径穿越(../ 序列),Zip 解压需防 ZipSlip 攻击(解压路径逃逸)。

3.6 反序列化安全

对应 OWASP A08(软件或数据完整性失效)、CWE-502

Java 原生序列化(ObjectInputStream)反序列化不可信数据可导致 RCE。优先使用 JSON 等安全格式。必须使用原生反序列化时,需通过 ObjectInputFilter 设置白名单类。

关注 Jackson、Fastjson、XStream 等组件的反序列化漏洞,及时升级。

3.7 输出编码与响应头安全

安全响应头是深度防御的重要组成:

响应头 作用
Content-Security-Policy 限制脚本与资源来源,防 XSS
X-Content-Type-Options: nosniff 防止 MIME 类型嗅探
X-Frame-Options / CSP frame-ancestors 防点击劫持
Referrer-Policy 控制 Referer 泄露
Strict-Transport-Security 强制 HTTPS(HSTS)

API 响应应遵循最小化原则,不返回多余字段,不暴露内部堆栈或调试信息。


第 4 章 密码学

对应 OWASP Top 10:2025 A04(加密失败)、OWASP ASVS v5.0 V11/V12、NIST SP 800-131A Rev2

本章为核心重点章节。4.2-4.9 每节在概念说明后补充基于 JCA / Bouncy Castle 的 Java 规范实现代码范例,展示正确 API 用法与参数选择,错误用法以 // 错误: 注释对照。

4.1 密码学设计原则与威胁模型

密码学工程的第一原则是「不要自己实现密码学」(Don't roll your own crypto)。应用层应使用经审计的标准库(JCA、Bouncy Castle、Conscrypt),而非自实现算法或协议。

Kerckhoffs 原则要求安全性依赖密钥保密而非算法保密。任何依赖「算法不公开」来保障安全的设计(混淆)均不符合现代密码学要求。

密码学原语各司其职,不可混用:

安全属性 提供者 说明
机密性 加密(对称/非对称) 防止数据被读取
完整性 MAC / 哈希 / 签名 检测数据是否被篡改
真实性 MAC / 签名 验证数据来源
不可否认性 数字签名 签名者无法否认(MAC 无法提供,因对称密钥双方共享)

加密不提供完整性。裸加密(如 AES-CBC 无 MAC)是可篡改的——攻击者可翻转密文位导致明文对应位翻转。始终优先使用认证加密(AEAD,如 AES-GCM),同时提供机密性与完整性。


4.2 对称加密

对称加密使用同一密钥进行加密与解密,适用于大块数据的机密性保护。

模式 是否 AEAD 推荐度 说明
GCM 是 首选 同时提供机密性与完整性,性能好,硬件加速
ChaCha20-Poly1305 是 推荐 纯软件实现性能优,无硬件加速依赖
CTR + HMAC 否(需手动 MAC) 可用 需自行实现 Encrypt-then-MAC
CBC 否 不推荐 无内置完整性,易受 Padding Oracle 攻击
ECB 否 禁用 相同明文产生相同密文,泄露数据模式
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.SecureRandom;
import java.util.Base64;

public class AesGcmExample {

    private static final int AES_KEY_SIZE = 256;   // AES-256
    private static final int GCM_IV_LENGTH = 12;    // 96-bit IV (GCM 推荐)
    private static final int GCM_TAG_LENGTH = 128;   // 认证标签位数

    /** 生成 AES 密钥 */
    public static SecretKey generateKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(AES_KEY_SIZE);
        return keyGen.generateKey();
    }

    /** AES-GCM 加密:返回 IV + 密文+Tag(拼接) */
    public static byte[] encrypt(SecretKey key, byte[] plaintext) throws Exception {
        // 每次加密生成随机 IV,不可复用
        byte[] iv = new byte[GCM_IV_LENGTH];
        new SecureRandom().nextBytes(iv);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);
        byte[] ciphertext = cipher.doFinal(plaintext);

        // IV 与密文一同返回(IV 非秘密,但解密时需要)
        byte[] combined = new byte[iv.length + ciphertext.length];
        System.arraycopy(iv, 0, combined, 0, iv.length);
        System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
        return combined;
    }

    /** AES-GCM 解密:校验认证标签,失败则抛 AEADBadTagException */
    public static byte[] decrypt(SecretKey key, byte[] combined) throws Exception {
        byte[] iv = new byte[GCM_IV_LENGTH];
        byte[] ciphertext = new byte[combined.length - GCM_IV_LENGTH];
        System.arraycopy(combined, 0, iv, 0, GCM_IV_LENGTH);
        System.arraycopy(combined, GCM_IV_LENGTH, ciphertext, 0, ciphertext.length);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        // 认证标签校验由 JCA 自动完成,校验失败抛异常
        return cipher.doFinal(ciphertext);
    }
}

关键要点:每次加密生成随机 IV(SecureRandom);GCM 自动计算并校验认证标签;解密时标签校验失败抛 AEADBadTagException,调用方必须捕获并拒绝该数据。


4.3 非对称加密与密钥协商

非对称加密使用公钥/私钥对,适用于密钥交换、数字签名与小数据加密。

import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;

public class RsaOaepExample {

    private static final int RSA_KEY_SIZE = 2048;  // 最低 2048 位

    /** 生成 RSA 密钥对 */
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
        gen.initialize(RSA_KEY_SIZE);
        return gen.generateKeyPair();
    }

    /** RSA-OAEP 加密 */
    public static byte[] encrypt(PublicKey publicKey, byte[] plaintext) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        // 显式指定 MGF1 的哈希算法与 OAEP 哈希一致
        OAEPParameterSpec oaepParams = new OAEPParameterSpec(
                "SHA-256",                        // OAEP 哈希
                "MGF1", MGF1ParameterSpec.SHA256,  // MGF1 掩码哈希
                PSource.PSpecified.DEFAULT);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
        return cipher.doFinal(plaintext);
        // // 错误:Cipher.getInstance("RSA/ECB/PKCS1Padding") — 易受 Bleichenbacher 攻击
        // // 错误:gen.initialize(1024) — 密钥长度不足
    }
}
import java.security.Signature;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PSSParameterSpec;
import java.security.spec.MGF1ParameterSpec;

public class RsaPssExample {

    /** RSA-PSS 签名 */
    public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {
        Signature signer = Signature.getInstance("RSASSA-PSS");
        PSSParameterSpec pssParams = new PSSParameterSpec(
                "SHA-256",                          // 消息摘要算法
                "MGF1", MGF1ParameterSpec.SHA256,    // 掩码生成函数
                32,                                   // 盐长度 = 哈希输出长度
                1);
        signer.setParameter(pssParams);
        signer.initSign(privateKey);
        signer.update(data);
        return signer.sign();
        // // 错误:Signature.getInstance("SHA256withRSA") — 使用 PKCS#1 v1.5 填充
    }

    /** RSA-PSS 验签 */
    public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws Exception {
        Signature verifier = Signature.getInstance("RSASSA-PSS");
        verifier.setParameter(new PSSParameterSpec(
                "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1));
        verifier.initVerify(publicKey);
        verifier.update(data);
        return verifier.verify(signature);
    }
}
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.Security;

public class EcdsaExample {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * ECDSA 签名 — 使用确定性 nonce(RFC 6979)
     * Bouncy Castle 的 SHA256withECDSA 默认使用确定性 nonce
     */
    public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {
        Signature signer = Signature.getInstance("SHA256withECDSA", "BC");
        signer.initSign(privateKey);
        signer.update(data);
        return signer.sign();
        // // 错误:使用 JDK 默认 provider 的 ECDSA,若随机数 k 可预测则私钥泄露
    }
}

4.4 哈希函数

哈希函数将任意长度输入映射为固定长度输出,提供完整性校验与数字指纹。

算法 推荐度 说明
SHA-256 / SHA-384 / SHA-512 推荐 SHA-2 家族,当前通用标准
SHA-3-256 / SHA-3-512 推荐 Keccak 结构,与 SHA-2 互补
BLAKE2 / BLAKE3 可用 高性能替代,适合非 NIST 合规场景
SHA-1 禁用 已被破解,数字签名 2014 年起禁用
MD5 禁用 碰撞已实战利用

哈希函数用途包括:完整性校验、数据指纹、HMAC 构造、KDF 派生。通用哈希函数(SHA-256)速度快,不适合口令存储(需慢哈希,见 4.6)。

import java.security.MessageDigest;

public class HashExample {

    /** SHA-256 哈希 */
    public static byte[] sha256(byte[] data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        return md.digest(data);
        // // 错误:MessageDigest.getInstance("MD5") — 已被破解
        // // 错误:MessageDigest.getInstance("SHA-1") — 已被破解
    }

    /** SHA-3-256 哈希(JDK 9+ 支持) */
    public static byte[] sha3_256(byte[] data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA3-256");
        return md.digest(data);
    }
}

关键要点:MessageDigest 是线程不安全的,每次调用应新建实例或使用线程局部变量。


4.5 消息认证码与数字签名

MAC 与签名的关键区别:MAC 使用对称密钥,双方共享,无法提供不可否认性;数字签名使用非对称密钥,私钥签名、公钥验签,提供不可否认性。

顺序 安全性 说明
Encrypt-then-MAC 安全 先加密再对密文做 MAC,推荐
MAC-then-Encrypt 不安全 Padding Oracle 攻击风险
Encrypt-and-MAC 不安全 明文可能从 MAC 泄露

使用 AEAD(如 GCM)时无需手动组合,AEAD 内部已正确处理。

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.security.MessageDigest;

public class HmacExample {

    /** 计算 HMAC-SHA-256 */
    public static byte[] hmacSha256(SecretKey key, byte[] data) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(key);
        return mac.doFinal(data);
    }

    /** 常量时间比较 MAC(防时序攻击) */
    public static boolean verifyMac(byte[] expected, byte[] actual) {
        return MessageDigest.isEqual(expected, actual);
        // // 错误:Arrays.equals(expected, actual) — 非常量时间,存在时序攻击风险
        // // 错误:expected.equals(actual) — 非常量时间
    }
}

关键要点:MessageDigest.isEqual() 在所有 JDK 版本中均为常量时间实现;Arrays.equals() 在发现第一个不匹配字节时即返回,泄露前缀信息。

4.6 口令哈希与密钥派生函数

口令哈希与密钥派生是密码学应用中最易出错的领域。通用哈希函数(SHA-256)速度快,不适合口令存储——GPU 每秒可计算数十亿次 SHA-256,使口令爆破成本极低。必须使用自适应慢哈希函数(KDF),通过内存与计算开销使暴力破解不可行。

优先级 算法 内存困难 说明
首选 Argon2id 是 PHC 获奖算法,抗 GPU/ASIC,参数可调(内存、迭代、并行度)
次选 scrypt 是 内存困难型,抗 ASIC
可选 bcrypt 否 广泛使用,cost 可调,但仅 CPU 困难
兼容 PBKDF2-HMAC-SHA-512 否 NIST 标准化,迭代次数需 ≥600000
import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.params.Argon2Parameters;
import java.security.MessageDigest;
import java.security.SecureRandom;

public class Argon2idExample {

    private static final int ITERATIONS = 3;       // 迭代次数
    private static final int MEMORY_KB = 65536;    // 64 MB 内存
    private static final int PARALLELISM = 4;      // 并行度
    private static final int HASH_LENGTH = 32;     // 输出长度 32 字节
    private static final int SALT_LENGTH = 16;      // salt 长度

    /** 生成 salt */
    public static byte[] generateSalt() {
        byte[] salt = new byte[SALT_LENGTH];
        new SecureRandom().nextBytes(salt);
        return salt;
    }

    /** Argon2id 哈希 */
    public static byte[] hash(String password, byte[] salt) {
        Argon2Parameters params = new Argon2Parameters.Builder(
                Argon2Parameters.ARGON2_id)
                .withVersion(Argon2Parameters.ARGON2_VERSION_13)
                .withIterations(ITERATIONS)
                .withMemoryAsKB(MEMORY_KB)
                .withParallelism(PARALLELISM)
                .withSalt(salt)
                .build();

        Argon2BytesGenerator generator = new Argon2BytesGenerator();
        generator.init(params);
        byte[] hash = new byte[HASH_LENGTH];
        generator.generateBytes(password.toCharArray(), hash);
        return hash;
    }

    /** 验证口令:常量时间比较 */
    public static boolean verify(String password, byte[] salt, byte[] expectedHash) {
        byte[] actualHash = hash(password, salt);
        return MessageDigest.isEqual(expectedHash, actualHash);
    }
}
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;

public class Pbkdf2Example {

    private static final int ITERATIONS = 210000;  // OWASP 2025 建议值
    private static final int KEY_LENGTH = 512;      // 512 位 = 64 字节

    /** PBKDF2-HMAC-SHA-512 派生密钥 */
    public static byte[] deriveKey(char[] password, byte[] salt) throws Exception {
        PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
        byte[] key = factory.generateSecret(spec).getEncoded();
        spec.clearPassword();  // 清除内存中的口令
        return key;
        // // 错误:iterations = 1000 — 迭代次数过低,GPU 秒级爆破
        // // 错误:使用 PBKDF2WithHmacSHA1 — SHA-1 已弃用
    }
}
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.params.HKDFParameters;

public class HkdfExample {

    /**
     * HKDF-SHA-256 派生密钥
     * 用于从已有共享密钥(如 ECDH 协商结果)派生会话密钥
     * 不适用于口令派生(口令需用 PBKDF2/Argon2)
     */
    public static byte[] deriveKey(byte[] ikm, byte[] salt, byte[] info, int length) {
        HKDFBytesGenerator generator = new HKDFBytesGenerator(new SHA256Digest());
        generator.init(HKDFParameters.skipExtractParameters(ikm, salt, info));
        byte[] key = new byte[length];
        generator.generateBytes(key, 0, length);
        return key;
    }
}

4.7 随机数与熵源

密码学场景的随机数必须使用 CSPRNG(密码学安全随机数生成器)。Java 中对应 java.security.SecureRandom。

SecureRandom 默认自动播种,从操作系统熵源获取种子(/dev/urandom),无需手动调用 setSeed。手动调用 setSeed 可能降低熵——若传入可预测值(如时间戳),则输出可预测。

场景 最小长度 说明
会话令牌 128 位(16 字节) 防暴力枚举
对称密钥 与算法匹配(AES-256 = 32 字节) KeyGenerator 生成
IV / Nonce GCM: 96 位(12 字节) 每次唯一随机
salt 128 位(16 字节) 每用户唯一
JWT ID 128 位 防碰撞

java.util.Random 与 Math.random() 使用线性同余生成器,仅统计随机,输出可预测,禁止用于安全场景。UUID.randomUUID() 内部使用 SecureRandom,可用于生成标识符,但不应作为会话令牌(UUID v4 格式固定,熵利用不充分)。

import java.security.SecureRandom;
import java.util.Base64;

public class SecureRandomExample {

    private static final SecureRandom SECURE_RANDOM = new SecureRandom();

    /** 生成会话令牌(Base64 编码) */
    public static String generateToken() {
        byte[] bytes = new byte[32];  // 256 位
        SECURE_RANDOM.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }

    /** 生成 salt */
    public static byte[] generateSalt(int length) {
        byte[] salt = new byte[length];
        SECURE_RANDOM.nextBytes(salt);
        return salt;
    }

    /** 生成 GCM IV(12 字节) */
    public static byte[] generateGcmIv() {
        byte[] iv = new byte[12];
        SECURE_RANDOM.nextBytes(iv);
        return iv;
        // // 错误:iv = new byte[12]; Arrays.fill(iv, (byte)0) — 固定全零 IV
        // // 错误:new Random().nextBytes(iv) — 非密码学随机
    }
}

关键要点:SecureRandom 实例可复用(线程安全);获取实例时优先使用 new SecureRandom() 或 SecureRandom.getInstanceStrong()(后者在 Linux 上可能阻塞等待熵,不推荐用于高频场景)。


4.8 密钥管理

密钥管理覆盖密钥的完整生命周期:生成、存储、分发、使用、轮换、撤销、销毁。密钥泄露使所有基于该密钥的密码学保护失效。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.KeyStore.SecretKeyEntry;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class KeyStoreExample {

    /** 将密钥存入 KeyStore */
    public static void storeKey(SecretKey key, String alias, String keystorePass)
            throws Exception {
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(null, null);  // 新建空 KeyStore

        SecretKeyEntry entry = new SecretKeyEntry(key);
        ks.setEntry(alias, entry,
                new KeyStore.PasswordProtection(keystorePass.toCharArray()));

        try (FileOutputStream fos = new FileOutputStream("keys.p12")) {
            ks.store(fos, keystorePass.toCharArray());
        }
    }

    /** 从 KeyStore 读取密钥 */
    public static SecretKey loadKey(String alias, String keystorePass)
            throws Exception {
        KeyStore ks = KeyStore.getInstance("PKCS12");
        try (FileInputStream fis = new FileInputStream("keys.p12")) {
            ks.load(fis, keystorePass.toCharArray());
        }
        SecretKeyEntry entry = (SecretKeyEntry) ks.getEntry(alias,
                new KeyStore.PasswordProtection(keystorePass.toCharArray()));
        return entry.getSecretKey();
    }
}
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class KeyCleanupExample {

    public void processWithKey(byte[] keyBytes) {
        try {
            SecretKey key = new SecretKeySpec(keyBytes, "AES");
            // ... 使用密钥加解密 ...
        } finally {
            // 使用后立即清零,防止内存中被读取
            Arrays.fill(keyBytes, (byte) 0);
        }
        // // 错误:String keyStr = new String(keyBytes) — String 不可变,无法清零
    }
}

4.9 证书与 TLS

TLS(Transport Layer Security)保障传输层机密性与完整性。TLS 1.3 是当前推荐版本,相比 1.2 移除了不安全算法(RSA 密钥交换、CBC、RC4、SHA-1),握手更快更安全。TLS 1.2 仍可用但需正确配置密码套件。

项目 推荐 禁用
TLS 版本 TLS 1.3(首选)、TLS 1.2 SSLv3、TLS 1.0、TLS 1.1
密钥交换 ECDHE(前向保密) RSA 密钥交换(无 PFS)
对称加密 AES-GCM、ChaCha20-Poly1305 CBC、RC4、3DES
证书签名 RSA-PSS、ECDSA RSA-PKCS#1v1.5、MD5、SHA-1
import javax.net.ssl.SSLContext;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.TrustManagerFactory;
import java.net.URL;
import java.security.KeyStore;

public class TlsClientExample {

    /**
     * 正确的 TLS 连接:使用 JVM 默认 TrustManager 校验证书
     */
    public static void connect(String urlStr) throws Exception {
        // 使用 JVM 默认信任库(cacerts),严格校验证书链与主机名
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm());
        tmf.init((KeyStore) null);  // null 表示使用默认 cacerts

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);

        URL url = new URL(urlStr);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setSSLSocketFactory(sslContext.getSocketFactory());
        // 主机名校验默认开启,不要禁用
        // conn.setHostnameVerifier((h, s) -> true) — 错误:禁用主机名校验
        conn.connect();

        // // 错误写法:信任所有证书
        // TrustManager[] trustAll = new TrustManager[]{
        //     new X509TrustManager() {
        //         public void checkClientTrusted(...) {}
        //         public void checkServerTrusted(...) {}  // 不校验,直接通过
        //         public X509Certificate[] getAcceptedIssuers() { return null; }
        //     }
        // };
        // sslContext.init(null, trustAll, null);  // CWE-295:MITM 风险
    }
}

4.10 密码算法雷区总清单

以下为本章核心汇总。每行为一条禁用项,标注风险与推荐替代方案及标准依据,可作为代码评审速查表使用。

类别 禁用 / 不推荐项 风险 推荐替代 依据
对称算法 DES / 3DES / RC4 / Blowfish 弱密钥、已破解 AES-128 / AES-256 NIST SP 800-131A
对称模式 ECB 相同明文→相同密文,泄露模式 GCM / CTR CWE-327
对称模式 CBC(无认证) Padding Oracle GCM(AEAD) OWASP A04
IV / Nonce 固定 / 复用 / 全零 密钥恢复、信息泄露 每次随机且唯一 CWE-329/323
裸加密 无 MAC 的加密 密文可篡改 AEAD(GCM)或 Encrypt-then-MAC OWASP A04
哈希 MD5 碰撞已实战利用 SHA-256+ NIST
哈希 SHA-1 已被破解 SHA-256 / SHA-3 NIST SP 800-131A
口令哈希 MD5 / SHA 直接哈希 GPU 秒级爆破 Argon2id / bcrypt / PBKDF2 OWASP A04, CWE-916
口令哈希 无 salt / 共享 salt 彩虹表攻击 每用户随机 salt CWE-759/760
口令哈希 工作因子过低 爆破成本过低 bcrypt cost≥10, PBKDF2≥210000 OWASP A04
非对称 RSA <2048 位 已可破解 RSA≥2048(推荐 3072) NIST, CNSA 2.0
非对称填充 RSA PKCS#1 v1.5 Bleichenbacher 攻击 RSA-OAEP / PSS CWE-780
非对称 DSA 已弃用 ECDSA / EdDSA NIST
签名 ECDSA 随机 k 复用 / 弱随机 私钥泄露 确定性签名 RFC 6979 —
随机数 java.util.Random / Math.random() 可预测 SecureRandom CWE-338
随机数 固定种子 / 时间戳种子 可预测 不手动 setSeed CWE-336/335
MAC 比较 == / Arrays.equals() 时序攻击 MessageDigest.isEqual() —
MAC 组合 MAC-then-Encrypt Padding Oracle Encrypt-then-MAC 或 AEAD —
密钥存储 硬编码 / 配置明文 / 默认密钥 永久泄露 HSM / KMS / KeyStore CWE-321/260
密钥类型 String 存储密钥 无法清零 byte[] + Arrays.fill —
密钥使用 一钥多用 跨用途泄露 密钥分离,各用途独立 —
TLS 版本 1.0 / 1.1 / SSLv3 协议漏洞 ≥1.2(优选 1.3) NIST
TLS 套件 RSA 交换 / CBC / RC4 无 PFS / 漏洞 ECDHE + GCM OWASP A04
证书校验 trustAll / 忽略校验 MITM 严格校验链 + 主机名 + 有效期 CWE-295
TLS 协议 STARTTLS 降级攻击 隐式 TLS —

第 5 章 错误处理与日志

对应 OWASP Top 10:2025 A09(安全日志与告警失效)、A10(异常条件处理不当)

5.1 安全错误处理与异常管理

OWASP Top 10:2025 新增 A10「异常条件处理不当」,将异常路径列为高危区域。许多安全漏洞发生在异常处理代码中——开发者在正常路径中谨慎处理安全,却在异常路径中放松了校验。

失败安全原则要求:异常或错误发生时,系统默认进入拒绝状态。加密校验失败、签名验证异常、解密标签不匹配时,必须拒绝请求或中止操作,而非降级为明文或跳过校验。

统一异常处理应避免将内部堆栈、SQL 语句、文件路径等技术信息暴露给客户端。生产环境返回通用错误信息,详细信息记录于服务端日志。

异常捕获不应吞掉异常(empty catch),也不应捕获过宽(catch (Exception e) 或 catch (Throwable t))掩盖真实问题。异常不应作为业务流程控制手段。

5.2 安全日志实践

日志需记录安全事件以支持审计与取证:登录成功/失败、权限变更、关键业务操作、异常拦截。每条安全日志应包含 who(主体)、what(操作)、when(时间)、result(结果)。

日志中禁止记录敏感数据:口令、密钥、令牌、完整卡号、身份证号、完整手机号。需要对敏感字段做脱敏处理(如卡号仅保留后四位)。

日志应防篡改——独立存储于攻击者无法修改的系统,或使用 WORM(Write Once Read Many)存储。仅记录成功操作而忽略失败操作的日志会丢失攻击痕迹。

5.3 敏感信息泄露防护

信息泄露不仅发生在 HTTP 响应中,还存在于日志、异常信息、监控指标、调试端点。全链路均需脱敏。

生产环境必须关闭或鉴权保护调试端点:Spring Boot Actuator、Swagger / OpenAPI 文档、Druid 监控页面。错误页面不应暴露框架版本、服务器路径、组件信息。


第 6 章 API 安全

对应 OWASP API Security Top 10、OWASP Top 10:2025 A03(供应链)

6.1 API 认证与授权

API 鉴权应独立于 Web 会话,常用 OAuth2 / JWT / API Key。每个端点强制授权校验,对象级授权防 IDOR。API Key 不可硬编码于前端代码或出现在 URL 参数中。

6.2 API 限流、配额与防滥用

限流(令牌桶 / 漏桶算法)防止暴力破解与 DoS 攻击。限流维度应包含用户、IP、接口多层级。超过限制时返回 HTTP 429 状态码与 Retry-After 头。

6.3 API 数据保护与响应最小化

API 响应应按调用者角色裁剪字段,不返回超权限数据。敏感字段加密或脱敏。批量查询接口必须分页,防止全量数据爬取。

6.4 API 版本管理与生命周期

API 版本化便于安全修复与废弃管理。含已知漏洞的旧版本应有明确下线计划,不可无限期保留。

6.5 第三方集成与供应链安全

对应 OWASP Top 10:2025 A03(软件供应链失败)

OWASP 2025 将 A03 升级为「软件供应链失败」,涵盖依赖漏洞、构建产物完整性、第三方组件安全。应用需通过 SCA(软件成分分析)工具扫描依赖中的已知 CVE,维护 SBOM(软件物料清单)。

第三方接口调用需设置超时与重试控制,严格校验 TLS 证书。构建产物(如 Docker 镜像、JAR 包)需校验签名或哈希完整性。

SCA 工具实操指南

SCA(Software Composition Analysis,软件成分分析)是供应链安全的核心手段,用于扫描项目依赖中的已知漏洞(CVE)与许可证合规风险。以下是主流 SCA 工具的实操方案。

① OWASP Dependency-Check(Maven 集成)

开源 SCA 工具,扫描 Maven/Gradle 依赖,匹配 NVD(National Vulnerability Database)CVE 数据库。

<!-- pom.xml -->
<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>9.2.0</version>
    <configuration>
        <failBuildOnCVSS>7</failBuildOnCVSS>  <!-- CVSS ≥ 7 构建失败 -->
        <suppressionFile>owasp-suppressions.xml</suppressionFile>
        <nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>  <!-- NVD API Key 提速 -->
    </configuration>
    <executions>
        <execution>
            <goals><goal>check</goal></goals>
        </execution>
    </executions>
</plugin>

执行命令:mvn dependency-check:check,生成报告在 target/dependency-check-report.html。CI/CD 中建议配置 NVD API Key(免费申请)避免限流,首次扫描需下载完整 NVD 数据库(约 500MB)。

② Trivy(容器镜像 + 文件系统扫描)

Aqua Security 开源的多维扫描工具,支持容器镜像、文件系统、Git 仓库、Kubernetes 配置扫描。

# 扫描 Docker 镜像
trivy image --severity HIGH,CRITICAL myapp:latest

# 扫描项目文件系统(含 pom.xml / package.json / go.sum)
trivy fs --severity HIGH,CRITICAL ./myproject

# 扫描并生成 JSON 报告(CI/CD 集成)
trivy fs --format json --output trivy-report.json ./myproject

# CI/CD 中阻断构建(发现 CRITICAL 漏洞则退出码 1)
trivy image --exit-code 1 --severity CRITICAL --ignore-unfixed myapp:latest

# 生成 SBOM(CycloneDX 格式)
trivy image --format cyclonedx --output sbom.json myapp:latest

Trivy 的优势:无需联网即可扫描(内置漏洞数据库)、扫描速度快(秒级)、支持多种语言生态。CI/CD Pipeline 中建议在 Build Image 阶段和部署前各执行一次。

③ Syft + Grype(SBOM 生成 + 漏洞匹配)

Anchore 开源的 SBOM 工具链:Syft 生成 SBOM(软件物料清单),Grype 基于 SBOM 匹配已知漏洞。

# 生成 SBOM(SPDX 格式)
syft myapp:latest -o spdx-json > sbom.json

# 基于 SBOM 扫描漏洞
grype sbom://sbom.json --fail-on high

# 直接扫描镜像
grype myapp:latest --only-fixed  # 只显示有修复版本的漏洞

SBOM 是供应链安全的基石:欧盟网络与信息安全局(ENISA)和美国行政令 14028 均要求软件供应商提供 SBOM。生产环境建议每次构建自动生成 SBOM 并归档,便于漏洞爆发时快速排查影响范围。

④ Maven Enforcer 插件(依赖治理)

在 SCA 之外,Maven Enforcer 插件可在构建期强制依赖规则,防止依赖冲突与安全隐患。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>3.4.1</version>
    <executions>
        <execution>
            <id>enforce</id>
            <goals><goal>enforce</goal></goals>
            <configuration>
                <rules>
                    <dependencyConvergence/>  <!-- 依赖版本统一 -->
                    <bannedDependencies>
                        <excludes>
                            <exclude>log4j:log4j:*:1.*</exclude>  <!-- 禁止 Log4j 1.x -->
                            <exclude>commons-collections:commons-collections:*:3.*</exclude>
                        </excludes>
                    </bannedDependencies>
                    <requireUpperBoundDeps/>  <!-- 强制使用最高版本 -->
                </rules>
                <fail>true</fail>
            </configuration>
        </execution>
    </executions>
</plugin>

6.6 GraphQL 安全

对应 OWASP GraphQL 安全建议、CWE-200(信息泄露)、CWE-770(资源耗尽)

GraphQL 灵活的查询能力也引入了新的攻击面:客户端可构造任意深度、任意复杂度的查询,导致服务端资源耗尽或信息泄露。

GraphQL 特有安全风险

防护措施


第 7 章 威胁建模与安全架构

对应 OWASP A06(不安全设计)、Microsoft STRIDE 模型

7.1 STRIDE 威胁建模概述

STRIDE 是 Microsoft 提出的威胁分类框架,将常见安全威胁分为 6 类,每类对应 CIA 三性 + 认证/授权/不可抵赖性的一个维度。威胁建模在系统设计阶段进行,目标是在编码前系统性识别和消减威胁。

字母 威胁类型 安全属性 含义 典型攻击
S 伪装(Spoofing) 认证性 攻击者冒充合法用户或系统身份 弱口令爆破、JWT 伪造、证书仿冒、Kerberoasting
T 篡改(Tampering) 完整性 未经授权修改数据或代码 中间人篡改、数据库注入、文件篡改、供应链投毒
R 抵赖(Repudiation) 不可抵赖性 用户否认执行过某操作,系统无法证明 无审计日志、日志可篡改、缺少数字签名
I 信息泄露(Information Disclosure) 机密性 敏感信息被未授权方获取 越权访问、错误信息泄露、日志泄露敏感数据、侧信道攻击
D 拒绝服务(Denial of Service) 可用性 攻击者使系统不可用或降低服务质量 SYN Flood、慢速攻击、资源耗尽、放大攻击
E 权限提升(Elevation of Privilege) 授权性 攻击者获得超出授权的权限 提权漏洞、绕过访问控制、注入执行任意代码

7.2 按组件的 STRIDE 威胁映射

威胁建模的核心方法是对系统架构图的每个组件(外部实体、进程、数据流、数据存储)逐一进行 STRIDE 分析。以下是典型微服务架构的威胁映射:

组件 S 伪装 T 篡改 R 抵赖 I 信息泄露 D 拒绝服务 E 提权
Web 前端 钓鱼网站冒充 XSS 篡改页面 — 敏感数据存 localStorage JS 资源阻塞 —
API 网关 伪造 API Key 请求篡改 无访问日志 Header 泄露内部信息 SYN Flood JWT alg=none
应用服务 Session 劫持 SQL 注入 无操作审计 异常堆栈泄露 线程池耗尽 反序列化 RCE
数据库 弱口令登录 直接改库 无变更日志 未加密敏感字段 慢查询拖垮 UDF 提权
消息队列 伪造生产者 消息篡改 — Topic 未授权访问 堆积 OOM —
Redis — Flushall — 未加密内存数据 KEYS 阻塞 CONFIG SET 写文件
CI/CD 凭证泄露 供应链投毒 — 构建日志泄密 — 恶意 PR 执行任意命令

7.3 DREAD 风险评级

识别威胁后需评估优先级。DREAD 模型从 5 个维度对每个威胁打分(1-10),取平均值确定风险等级。

维度 含义 评分标准
Damage 潜在损害 1=无影响 → 10=完全瘫痪/数据全丢
Reproducibility 可复现性 1=极难复现 → 10=每次必现
Exploitability 可利用性 1=需高级技能 → 10=脚本小子可用
Affected Users 影响范围 1=极少数 → 10=所有用户
Discoverability 可发现性 1=深藏内部 → 10=公开可见

风险等级:平均分 1-3 低风险(后续迭代修复)、4-7 中风险(本迭代修复)、8-10 高风险(阻断发布,立即修复)。

7.4 威胁建模工作流

  1. 绘制数据流图(DFD):标出外部实体、进程、数据存储、数据流边界(信任边界)
  2. 识别信任边界:网络边界(DMZ/内网)、进程边界(应用/DB)、权限边界(用户/管理员)
  3. 逐组件 STRIDE 分析:对每个组件按 STRIDE 六类逐一提问"是否存在该类威胁?"
  4. DREAD 评级:对识别出的威胁打分,确定优先级
  5. 制定消减措施:高/中风险必须在本迭代消减,低风险记录跟踪
  6. 定期复审:每次重大架构变更(新增组件、修改数据流)时更新威胁模型

实践建议:威胁建模不是一次性活动,而是持续的安全工程实践。建议每个 Epic/特性迭代做一次轻量级威胁建模(30 分钟会议),每次大版本发布做一次完整威胁建模(2-4 小时工作坊)。

附录 A:OWASP Top 10:2025 映射对照表

OWASP 2025 编号 类别 本文对应章节
A01 访问控制失效(Broken Access Control) 1.4 授权模型、6.1 API 授权、3.4 SSRF
A02 安全配置错误(Security Misconfiguration) 5.3 信息泄露防护、6.4 API 生命周期
A03 软件供应链失败(Software Supply Chain Failures) 6.5 供应链安全
A04 加密失败(Cryptographic Failures) 第 4 章全部
A05 注入(Injection) 3.2 注入防御
A06 不安全设计(Insecure Design) 0.4 安全设计总原则
A07 身份认证失效(Authentication Failures) 1.1-1.3 认证、第 2 章会话
A08 软件或数据完整性失效(Software or Data Integrity Failures) 3.6 反序列化、4.5 MAC 与签名、6.5 供应链
A09 安全日志与告警失效(Security Logging Failures) 5.2 安全日志
A10 异常条件处理不当(Mishandling of Exceptional Conditions) 5.1 错误处理

附录 B:算法推荐与废弃速查表

对称加密

算法 / 模式 状态 推荐
AES-256-GCM 推荐 首选 AEAD
AES-128-GCM 推荐 可用 AEAD
ChaCha20-Poly1305 推荐 无硬件加速场景首选
AES-CTR + HMAC 可用 需手动 Encrypt-then-MAC
AES-CBC 不推荐 无完整性,Padding Oracle
AES-ECB 禁用 泄露数据模式
DES / 3DES / RC4 / Blowfish 禁用 NIST 已禁用

哈希函数

算法 状态 用途
SHA-256 / SHA-512 推荐 通用哈希、HMAC
SHA-3-256 / SHA-3-512 推荐 与 SHA-2 互补
BLAKE2 / BLAKE3 可用 高性能场景
SHA-1 禁用 已破解
MD5 禁用 碰撞已实战

口令哈希

算法 优先级 工作因子建议
Argon2id 首选 内存 64MB+,迭代 3+
scrypt 次选 N=2^16, r=8, p=1
bcrypt 可选 cost ≥10
PBKDF2-HMAC-SHA-512 兼容 迭代 ≥210000
MD5 / SHA 直接哈希 禁用 GPU 秒级爆破

非对称加密与签名

算法 状态 参数要求
RSA-OAEP 推荐 ≥2048 位,优选 3072
RSA-PSS 推荐 ≥2048 位
ECDSA (P-256/P-384) 推荐 确定性 nonce (RFC 6979)
EdDSA (Ed25519) 推荐 确定性签名
RSA-PKCS#1 v1.5 禁用 Bleichenbacher 攻击
DSA 禁用 已弃用
RSA <2048 位 禁用 NIST 已禁用

随机数

API 状态 用途
SecureRandom 推荐 所有安全场景
KeyGenerator 推荐 密钥生成
java.util.Random 禁用 可预测
Math.random() 禁用 可预测

TLS

项目 推荐 禁用
TLS 版本 1.3(首选)、1.2 1.0、1.1、SSLv3
密钥交换 ECDHE RSA 交换
对称加密 AES-GCM、ChaCha20-Poly1305 CBC、RC4、3DES
证书签名 RSA-PSS、ECDSA PKCS#1 v1.5、SHA-1
证书校验 严格校验 trustAll

附录 C:Checkpoint 自检清单汇总

以下为全文 Checkpoint 的可勾选汇总,用于代码评审与上线评审。

认证与授权

会话管理

输入输出安全

密码学

错误处理与日志

API 安全


附录 D:参考资料与权威链接

来源 说明
OWASP Top 10:2025 https://owasp.org/Top10/
OWASP ASVS v5.0 https://owasp.org/www-project-application-security-verification-standard/
OWASP Cheat Sheet Series https://cheatsheetseries.owasp.org/
NIST SP 800-131A Rev2 密码算法迁移指南
NIST FIPS 203 / 204 / 205 后量子密码标准(ML-KEM / ML-DSA / SLH-DSA)
NIST SP 800-63B 数字身份指南(口令策略)
NIST SP 800-90A 随机数生成器标准
CWE-321 硬编码密钥
CWE-327 使用已破解或风险的密码算法
CWE-329 IV 不可预测性失败
CWE-338 使用密码学不安全的 PRNG
CWE-780 RSA PKCS#1 v1.5 填充
CWE-916 口令哈希使用不足迭代次数