第9章:安全性设计(建议113-121)

覆盖数值边界、MD5与文件完整性、混合加密、TLS、现代secret handling、标准密码库、强名称的身份边界及应用最小权限。

学习目标

  • 能分析numeric overflow、collision、tampering与file disclosure threat,设计checked value、digest、HMAC/signature或hybrid encryption control
  • 能比较TLS/certificate validation、legacy SecureString、secret manager与reviewed cryptographic API,判断secret lifetime和fail-closed边界
  • 能区分strong-name identity、publisher/package trust与runtime authorization,并设计service/filesystem/network least privilege

机制总览

第9章:安全性设计(建议113-121):机制路径

  1. 1

    为什么算法名称不能替代Threat Model

    “用了hash”“启用了SSL”“程序集已签名”都没有说明保护什么、对手有什么能力、key由谁持有、失败时是否fail closed。security control必须对应property:confidentiality、integrity、authenticity、authorization和a…

  2. 2

    Numeric Bound、Hash与File Prote…

    type宽度是security boundary:length、price、offset、permission bit和allocation size若overflow,可能绕过validation或导致undersized buffer。按domain范围选type,在外部输入处检查min/max…

  3. 3

    Transport、Secret Lifetime与Cry…

    原题中的SSL应更新为TLS。现代.NET使用HttpClient/SslStream并保持certificate hostname、chain、expiry/revocation policy;Microsoft建议优先让OS选择当前支持的TLS版本,避免硬编码过时protocol。绝不能在pro…

先按顺序建立机制,再进入实验切换阶段并检查失效证据。

章级决策实验

第9章:安全性设计(建议113-121):机制与证据

切换《第9章:安全性设计(建议113-121)》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。

选择推理阶段

当前阶段 · 为什么算法名称不能替代Threat Model

“用了hash”“启用了SSL”“程序集已签名”都没有说明保护什么、对手有什么能力、key由谁持有、失败时是否fail closed。security control必须对应property:confidentiality、integrity、authenticity、authorization和a…

可核验证据

固定当前 .NET、语言版本和输入规模,用编译诊断、分析器、自动化测试、基准或安全失败样本复核「为什么算法名称不能替代Threat Model」的收益与反例。

学完《第9章:安全性设计(建议113-121)》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。

失效—证据矩阵

第9章:安全性设计(建议113-121):失效与核验

为什么算法名称不能替代Threat Model

典型失效

若把「为什么算法名称不能替代Threat Model」当作脱离版本与上下文的硬规则,可能用过时的优化或风格替换了更重要的正确性、安全性与可维护性约束。

核验证据

固定当前 .NET、语言版本和输入规模,用编译诊断、分析器、自动化测试、基准或安全失败样本复核「为什么算法名称不能替代Threat Model」的收益与反例。

Numeric Bound、Hash与File Prote…

典型失效

若把「Numeric Bound、Hash与File Prote…」当作脱离版本与上下文的硬规则,可能用过时的优化或风格替换了更重要的正确性、安全性与可维护性约束。

核验证据

固定当前 .NET、语言版本和输入规模,用编译诊断、分析器、自动化测试、基准或安全失败样本复核「Numeric Bound、Hash与File Prote…」的收益与反例。

Transport、Secret Lifetime与Cry…

典型失效

若把「Transport、Secret Lifetime与Cry…」当作脱离版本与上下文的硬规则,可能用过时的优化或风格替换了更重要的正确性、安全性与可维护性约束。

核验证据

固定当前 .NET、语言版本和输入规模,用编译诊断、分析器、自动化测试、基准或安全失败样本复核「Transport、Secret Lifetime与Cry…」的收益与反例。

每个判断都必须能落到观测、测试或产物,不能只凭代码表面推测。

为什么算法名称不能替代Threat Model

“用了hash”“启用了SSL”“程序集已签名”都没有说明保护什么、对手有什么能力、key由谁持有、失败时是否fail closed。security control必须对应property:confidentiality、integrity、authenticity、authorization和availability彼此不同;unkeyed digest不证明发送者,strong name不授予trust,encryption也不自动防篡改。

先预测:攻击者能同时替换file和SHA-256文本时,hash校验是否有效;SecureString是否仍是现代.NET保存service secret的默认;strong-named assembly是否代表可信publisher。三个答案都是否。

Numeric Bound、Hash与File Protection(建议113-116)

建议113:声明变量前考虑最大值

type宽度是security boundary:length、price、offset、permission bit和allocation size若overflow,可能绕过validation或导致undersized buffer。按domain范围选type,在外部输入处检查min/max,跨type转换和算术使用checked context;不要用更大integer替代业务上限。

public static int ComputeBufferSize(int itemCount, int bytesPerItem)
{
    if (itemCount is < 0 or > 1_000_000)
        throw new ArgumentOutOfRangeException(nameof(itemCount));
    if (bytesPerItem is < 1 or > 4096)
        throw new ArgumentOutOfRangeException(nameof(bytesPerItem));
 
    return checked(itemCount * bytesPerItem);
}

测试0、max、max+1、negative、conversion和multiplication boundary。allocation前还要有application quota,避免合法integer造成resource exhaustion。

建议114:MD5不再安全

MD5存在实用collision攻击,不得用于signature、certificate、artifact authenticity或任何attacker-controlled security decision;.NET安全规则也将MD5列为broken cryptographic algorithm。legacy content-address或非对抗checksum即使暂时保留,也应标注不提供security并规划迁移。

byte[] digest = SHA256.HashData(content);

换成SHA-256只解决collision-resistant digest primitive,不自动解决expected digest来源是否可信、password hashing、key derivation或message authenticity。

建议115:通过HASH来验证文件是否被篡改

unkeyed hash只能在expected digest来自独立可信channel时验证bytes一致;若攻击者可同时替换file和hash,两者会一起通过。共享secret双方可用HMAC;由publisher向任意verifiers发布artifact时用digital signature,并验证certificate/key provenance、algorithm、metadata和rollback policy。

byte[] expectedMac = Convert.FromHexString(manifest.Mac);
byte[] actualMac = HMACSHA256.HashData(key, fileBytes);
 
if (!CryptographicOperations.FixedTimeEquals(expectedMac, actualMac))
    throw new SecurityException("Artifact authentication failed.");

HMAC key不能与file一起公开;signature verification则保护private signing key并分发可信public key/certificate。hash comparison用fixed-time API,且限制file size与path。

建议116:避免用非对称算法加密文件

RSA/ECC不用于直接加密大文件:size受限、性能差且padding/protocol容易误用。采用hybrid encryption:随机生成symmetric data key,以AEAD(如AES-GCM)加密content,再由KMS/public-key mechanism包装data key;保存algorithm/version/nonce/tag/wrapped key等metadata。

byte[] key = RandomNumberGenerator.GetBytes(32);
byte[] nonce = RandomNumberGenerator.GetBytes(12);
byte[] ciphertext = new byte[plaintext.Length];
byte[] tag = new byte[16];
 
using var aes = new AesGcm(key, tagSizeInBytes: 16);
aes.Encrypt(nonce, plaintext, ciphertext, tag, associatedData);
CryptographicOperations.ZeroMemory(key);

同一key下nonce必须唯一,decrypt先验证tag再release plaintext;key wrapping/rotation交给成熟KMS/library,不手写envelope protocol。

分步1 / 3

切换numeric、MD5、SHA-256、HMAC与hybrid AEAD

Transport、Secret Lifetime与Crypto API(建议117-119)

建议117:使用SSL确保通信中的数据安全

原题中的SSL应更新为TLS。现代.NET使用HttpClient/SslStream并保持certificate hostname、chain、expiry/revocation policy;Microsoft建议优先让OS选择当前支持的TLS版本,避免硬编码过时protocol。绝不能在production用“接受任何证书”的callback修复连接失败。

using var client = new HttpClient
{
    BaseAddress = new Uri("https://api.example.com"),
};
 
using HttpResponseMessage response = await client.GetAsync("orders", cancellationToken);
response.EnsureSuccessStatusCode();

TLS保护in-transit channel,不验证application authorization、endpoint业务合法性或data-at-rest。需要mTLS/pinning时必须有rotation与backup policy,避免一次certificate更新导致全量outage。

建议118:使用SecureString保存密钥等机密字符串

这是另一条必须现代化的旧建议:Microsoft明确不建议在新.NET开发中使用SecureString。多数现代API最终仍需plain string/bytes,跨平台保护不一致,SecureString无法修复process compromise。仅在legacy Windows API能直接消费它时保留。

新设计使用OS keychain/cloud secret manager/KMS与workload identity,尽量晚取、短暂缓存、禁止log/dump,API允许时用可清除byte/char buffer并CryptographicOperations.ZeroMemory。string无法可靠zero,因此最重要的是避免不必要copies与长期field。

建议119:不要使用自己的加密算法

不仅不要自创cipher,也不要自行拼接“encrypt then hash”、nonce、padding、key derivation和serialization protocol。使用平台reviewed APIs、标准AEAD/signature/KDF和managed KMS,启用analyzers/dependency updates,并由security review验证mode/parameters/key lifecycle。

byte[] salt = RandomNumberGenerator.GetBytes(16);
byte[] derived = Rfc2898DeriveBytes.Pbkdf2(
    passwordBytes,
    salt,
    iterations,
    HashAlgorithmName.SHA256,
    outputLength);

password storage应使用专门password hashing policy和可升级cost参数,不把示例常数当永久标准。算法agility要有version field和migration,不允许silent downgrade。

分步1 / 3

切换TLS、pinning、SecureString、secret store与custom crypto

Artifact Identity与Least Privilege(建议120-121)

建议120:为程序集指定强名称

strong name在.NET Framework/GAC/legacy library compatibility中提供assembly unique identity和binding能力,但Microsoft明确说明不能依赖strong name提供安全信任,现代.NET 5+通常无实质安全收益。是否strong-name由consumer/target framework compatibility决定,不作为tamper/publisher authorization gate。

需要publisher provenance时使用Authenticode、NuGet/package signing、trusted feed、hash/signature verification、SBOM和build provenance;签名key放在受控CI/KMS并定义rotation/revocation。即使artifact签名有效,runtime仍需least privilege。

建议121:为应用程序设定运行权限

让process/container/service identity只拥有完成use cases所需的filesystem、network、database、cloud roles和OS capabilities,默认deny并按环境拆分identity。不要以admin/root运行来规避permission错误;.NET Framework Code Access Security不是现代security boundary。

// Application code depends on a narrow storage capability.
public interface IInvoiceReader
{
    Task<Invoice?> FindAsync(InvoiceId id, CancellationToken cancellationToken);
}

部署层把reader identity限制为指定database/table的read action,filesystem只开放data volume,network egress只到approved endpoints。记录authorization denied metrics但不自动升级权限;break-glass流程独立审计。

分步1 / 3

切换strong name、package signing、service identity、filesystem与network

本章回顾:每个Control只证明一件事

  1. 数值type与checked arithmetic保护domain bounds,但仍需quota防resource exhaustion。
  2. MD5不能用于security;unkeyed digest只有trusted expected value时验证bytes,authenticity使用HMAC/signature。
  3. 大文件用AEAD symmetric data key与KMS/public-key wrapping的hybrid scheme,不直接RSA加密content。
  4. SSL更新为TLS并保持certificate validation;modern .NET secret handling不以SecureString为默认。
  5. 使用reviewed crypto/API和managed key lifecycle,不自创algorithm或protocol composition。
  6. strong name是identity/compatibility,不是trust;runtime用独立workload identity和least privilege。

练习

问题 1:下载更新包时,SHA-256、HMAC和digital signature分别能证明什么?

问题 2:现代.NET service应怎样处理TLS与database secret?

问题 3:strong-named assembly为何仍需要package signing和runtime least privilege?

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

security property
authenticity proof
hybrid encryption
secret lifetime
least privilege boundary

原版目录概念补充核对

以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。

建议114:MD5不再安全:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议114:MD5不再安全跨越输入信任或资源生命周期边界,建议只有在明确威胁模型、所有权和失败路径后才成立。用恶意/畸形输入、失败注入和资源计数检查拒绝行为、敏感数据暴露与最终释放,而不是只验证顺利路径。

建议115:通过HASH来验证文件是否被篡改:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议115:通过HASH来验证文件是否被篡改是一条需要上下文的工程建议,而不是无条件规则。先声明适用的语言/运行时版本、代码约束与反例,再以编译器诊断、分析器、自动化测试或基准结果证明采用该建议确实改善正确性、可维护性或性能。

建议116:避免用非对称算法加密文件:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议116:避免用非对称算法加密文件是一条需要上下文的工程建议,而不是无条件规则。先声明适用的语言/运行时版本、代码约束与反例,再以编译器诊断、分析器、自动化测试或基准结果证明采用该建议确实改善正确性、可维护性或性能。

建议117:使用SSL确保通信中的数据安全:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议117:使用SSL确保通信中的数据安全跨越输入信任或资源生命周期边界,建议只有在明确威胁模型、所有权和失败路径后才成立。用恶意/畸形输入、失败注入和资源计数检查拒绝行为、敏感数据暴露与最终释放,而不是只验证顺利路径。

建议118:使用SecureString保存密钥等机密字符串:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议118:使用SecureString保存密钥等机密字符串涉及类型语义、分配和枚举成本,不能凭代码长度或旧版微优化判断。固定运行时、构建模式和输入规模,用基准、分配统计与多组边界数据比较替代方案,同时验证结果语义一致。

建议119:不要使用自己的加密算法:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议119:不要使用自己的加密算法是一条需要上下文的工程建议,而不是无条件规则。先声明适用的语言/运行时版本、代码约束与反例,再以编译器诊断、分析器、自动化测试或基准结果证明采用该建议确实改善正确性、可维护性或性能。

建议120:为程序集指定强名称:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议120:为程序集指定强名称是一条需要上下文的工程建议,而不是无条件规则。先声明适用的语言/运行时版本、代码约束与反例,再以编译器诊断、分析器、自动化测试或基准结果证明采用该建议确实改善正确性、可维护性或性能。

建议121:为应用程序设定运行权限:机制、边界与证据

  1. 安全性设计(建议113-121)中的建议121:为应用程序设定运行权限是一条需要上下文的工程建议,而不是无条件规则。先声明适用的语言/运行时版本、代码约束与反例,再以编译器诊断、分析器、自动化测试或基准结果证明采用该建议确实改善正确性、可维护性或性能。

讨论

评论区加载中…