安全编码总则 (General Secure Coding Principles)
分类: SDL规范文档 · 阅读时间: 约9分钟 · 最后更新: 2026-04-06

1. 概述

安全编码总则定义了与编程语言无关的通用安全编码原则。这些原则构成了所有语言特定安全编码规范的基础, 适用于任何技术栈的软件开发。开发人员在编写代码时应将这些原则内化为编码习惯, 而非仅作为检查清单使用。

定位
本文档是语言无关的通用原则。针对特定语言的详细规范, 请参考对应的安全编码规范文档 (如 Java安全编码规范)。

2. 输入验证 (Input Validation)

2.1 核心原则: 不信任任何外部输入

所有来自系统边界外的数据都应视为不可信, 包括但不限于: HTTP 请求参数、请求头、Cookie、上传文件、API 调用参数、数据库查询结果 (如果数据来源不可控)、环境变量和配置文件 (如果可被外部修改)。

2.2 白名单优于黑名单

验证策略应基于白名单 (允许已知安全的输入) 而非黑名单 (尝试过滤已知危险的输入)。黑名单方法存在固有缺陷: 攻击者总能发现未被列入黑名单的新攻击向量, 而绕过技术 (如编码变换、Unicode规范化) 使得完备的黑名单几乎不可能实现。

2.3 验证时机与位置

输入验证必须在服务端执行。客户端验证可以改善用户体验, 但绝不能作为安全控制手段, 因为攻击者可以轻易绕过客户端逻辑。验证应在数据进入业务逻辑层之前完成, 建议在框架层面统一实施。

3. 输出编码 (Output Encoding)

输入验证解决"进入系统的数据是否安全", 输出编码则解决"离开系统的数据是否会造成危害"。根据输出目标的不同, 需要采用不同的编码策略:

  • HTML Context: HTML Entity Encoding (如 < > &)
  • JavaScript Context: JavaScript Unicode Escaping
  • URL Context: Percent Encoding
  • CSS Context: CSS Hex Encoding
  • SQL Context: 使用参数化查询而非编码 (编码不是SQL注入的可靠防御)

上下文感知编码是防御 XSS 的关键。许多框架 (如 React, Angular) 提供自动输出编码, 但开发人员应理解其工作原理和边界条件, 特别是在使用 dangerouslySetInnerHTML 等绕过机制时。

4. 最小权限原则 (Principle of Least Privilege)

每个模块、进程和用户应仅拥有完成其功能所需的最小权限集。该原则应在多个层面实施:

  • 数据库层: 应用账号仅授予必要的表和操作权限, 避免使用 root/sa 账号
  • 文件系统层: 进程仅拥有必要目录的读写权限
  • 网络层: 服务仅开放必要的端口, 使用网络策略限制出入流量
  • API层: 每个 Token/Key 仅关联必要的 Scope
  • 容器层: 以非 root 用户运行容器, 使用 seccomp/AppArmor 限制系统调用

5. 纵深防御 (Defense in Depth)

不依赖单一安全控制, 而是在多个层面部署互补的安全措施。即使某一层防御被攻破, 其他层仍能提供保护。典型的纵深防御架构包括:

  • 网络层: WAF + DDoS防护 + 网络分段
  • 应用层: 输入验证 + 输出编码 + CSP
  • 数据层: 加密存储 + 访问控制 + 数据脱敏
  • 运维层: 日志监控 + 入侵检测 + 自动告警
实践建议
纵深防御不意味着无限叠加安全控制。应基于风险评估合理配置每一层的防御强度, 在安全性和系统性能之间取得平衡。

6. 安全失败 (Fail Securely)

当系统出现异常时, 应默认进入安全状态, 而非开放状态。这一原则体现在:

  • 认证失败: 默认拒绝访问, 而非默认放行
  • 权限检查异常: 返回403/401, 而非跳过权限检查继续执行
  • 配置缺失: 使用安全的默认值, 而非最宽松的配置
  • 异常处理: 捕获异常后进行安全处理, 避免信息泄露

一个常见的反模式是在 try-catch 块中捕获异常后静默忽略, 导致安全检查被绕过。所有安全相关异常都应被正确记录和处理。

7. 不要自己造轮子 (Don't Roll Your Own)

在密码学、认证、会话管理等安全关键领域, 应使用经过广泛审查的标准库和框架, 而非自行实现。历史反复证明, 自研加密算法、自研认证协议和自研随机数生成器几乎总是存在缺陷。

  • 使用框架内置的会话管理, 不要自行实现基于 Cookie 的会话
  • 使用标准加密库 (如 OpenSSL, libsodium, BouncyCastle), 不要实现自定义加密
  • 使用成熟的 JWT 库, 不要手动解析和验证 Token

8. 安全默认配置 (Secure by Default)

系统的默认配置应是安全的。用户需要主动操作才能降低安全级别, 而非需要主动操作才能提升安全级别。具体体现在:

  • Cookie 默认设置 SecureHttpOnlySameSite 属性
  • CORS 默认不允许跨域, 需显式配置白名单
  • API 默认需要认证, 公开接口需显式声明
  • 日志默认不记录敏感字段

9. 小结

安全编码总则的核心思想可以概括为: 不信任外部输入、最小化攻击面、纵深防御、安全失败、使用经过验证的方案。这些原则应贯穿于日常编码的每一个决策中。后续的语言特定编码规范将在这些通用原则的基础上, 提供更具操作性的编码指导。

陈雪 (Chen Xue)
Secure Coding Standards Lead
安全编码规范研究负责人, 在安全编码标准化和开发者安全教育领域有多年实践经验。主导编制了面向多种编程语言的安全编码规范, 致力于将安全编码从规范文档转化为开发者的编码直觉。