术语定义
通用规则
规则1:对外部输入进行校验
说明:软件最为普遍的缺陷就是对来自客户端或者外部环境的数据没有进行正确的合法性校验。这种缺陷可以导致几乎所有的程序弱点,例如Dos、内存越界、命令注入、SQL注入、缓冲区溢出、数据破坏、文件系统攻击等。这些不可信数据可能来自:
/
- 用户输入
- 外部调用的参数
- 进程间的通信数据
- 网络连接(甚至是一个安全的连接)、
- 用户态输入(对于内核程序)
- 上层应用(业务)输入
当这些不可信输入用于如下场景时(包括但不局限于),需要校验其合法性:
/
- 作为循环条件 – 可能会引发缓冲区溢出、内存越界读/写、死循环等问题。
- 作为数组下标 – 可能导致超出数组上限,从而造成非法内存访问。
- 作为内存偏移地址 – 指针偏移访问内存,可能造成非法内存访问,并可以造成进一步的危害,如任意地址读/写。
- 作为内存分配的尺寸参数 – 请参考规则
C3.1
、C3.2
和C4.5
。
- 作为业务数据 – 如作为命令执行参数、拼装sql语句、拼接格式化字符串等,这会导致命令注入、SQL注入、格式化漏洞等问题。详细请参考规则
C2.1
、C5.2
和C6.3
。
- 用于数据拷贝操作 – 当作为拷贝长度时,极易造成目标缓冲区溢出。详细请参考规则
C1.1
、C1.2
和C1.3
。
- 影响代码逻辑 – 比如基于不可信输入做安全决策,影响代码逻辑走向。
- 会改变系统状 – 比如未加校验直接打开不可信路径,可能会导致目录遍历攻击,操作了攻击者无权操作的文件,使得系统被攻击者所控制。
输入校验可能包括如下内容(包括但不局限于):
/
- 校验数据长度
- 校验数据范围
- 校验数据类型和格式
- 校验输入只包含可接受的字符(可以采用『白名单』形式),尤其需要注意一些特殊情况下的特殊字符。了解更多关于特殊字符,可以参考附录A和附录B。
规则2:禁止在日志中保存口令、密钥
说明:在日志中不能保存口令和密钥,其中的口令包括明文口令和密文口令。对于敏感信息建议采取以下方法,
/
- 不打印在日志中;
- 若因为特殊原因必须要打印日志,则用
*
代替(不要显示出敏感信息的长度)。
规则3:及时清除存储在可复用资源中的敏感信息
说明
:存储在可复用资源中的敏感信息如果没有正确的清除则很有可能被低权限用户或者攻击者所获取和利用。因此敏感信息在可复用资源中保存应该遵循存储时间最短原则。可复用资源包括以下几个方面:
/
- 堆(heap)
- 栈(stack)
- 数据段(data segment)
- 数据库的映射缓存
存储口令、密钥的变量(包括加密后的变量)使用完后必须显式覆盖或清空
。
规则4:正确使用经过验证的安全的标准加密算法
说明:禁用私有算法或者弱加密算法(如DES,SHA1等),应该使用经过验证的、安全的、公开的加密算法。加密算法分为对称加密算法和非对称加密算法。推荐使用的
/
- 常用对称加密算法有:
- AES
- 推荐使用的常用非对称算法有:
- RSA
- 推荐使用的数字签名算法有:
- 数字签名算法(DSA)
- ECDSA
- 此外还有验证消息完整性的安全哈希算法(SHA256)等。基于哈希算法的口令安全存储必须加入盐值(参见
规则5
)。密钥长度符合最低安全要求:
- AES: 128位
- RSA: 2048位
- DSA: 2048位
规则5:基于哈希算法的口令安全存储必须加入盐值(salt)
说明:单向哈希是在一个方向上工作的哈希函数,从预映射的值很容易计算其哈希值,但要根据特定哈希值产生一个预映射的值却是非常困难的。单向哈希主要应用于加密、消息完整性校验、冗余校验等。
假如没有加入盐值,则加密原理是:密文=哈希算法(明文)
此时,若攻击者获取到密文,同时知道哈希算法,则就可以通过字典攻击来探测和获取口令。
加入盐值之后:密文= 哈希算法(明文+盐值)
其中盐值可以随机设置,这样即使相同的口令,但盐值不同,密文也不同,从而增加了口令的破解难度、增强安全性。
规则6:不要硬编码敏感信息
说明:
/
- 硬编码口令、服务器IP地址以及加密密钥等敏感信息可能会将这些信息暴露给攻击者。任何人都可以反编译并发现这些敏感信息。因此,除了一些特殊情况(例如在TPM环境下)之外,程序中禁止硬编码任何敏感信息。
- 硬编码敏感信息还会增加维护管理成本,当修改代码时,需要额外管理并适配这些修改。例如,要更改一个已经部署了的程序的硬编码口令,可能需要下发一个补丁。
规则7:不要在共享目录中创建临时文件
说明:程序员经常会在共享目录里创建临时文件。临时文件通常作为不需要或者不能驻留在内存中的数据的一种辅助存储方式,同时也可以作为与其它进程通过文件系统进行通信的一种方式。例如,一个进程会以一个公认的命名或者与合作进程协商好的名字在共享目录里创建临时文件,然后这些临时文件便可以在这些合作进程间共享信息。
但是,这是一个非常危险的操作。一个在共享目录里大家都知道名字的文件是很容被攻击者控制和操纵的。以下列出了几种可能的规避方法:
/
- 使用其它低级别进程间通信(IPC)机制,如使用sockes或者共享内存;
- 使用高级别IPC机制,如远程过程调用(remote procedure call);
- 使用一个安全的目录或者设置一个只能被程序应用实例访问的jail(确保同一平台下的多个应用程序实例不会产生竞争)。
IPC机制中有些需要使用临时文件,但是其它的不需要。例如,需要使用临时文件的IPC机制有POSIX的mmap()函数。而伯克利套接字(Berkeley Sockets)、POSIX本地IPC套接字和System V共享内存却不需要临时文件。因为共享目录的多用户属性使得它具有与生俱来的危险,因此,利用共享临时文件来实现IPC是不推荐的。
当2个以上或者一组用户对目录具有写权限时,其危险和欺骗性比少量文件的共享访问更为严重。因此,当确实需要在共享目录中创建临时文件时,必须满足如下条件:
/
- 创建不可预测的文件名称;
- 创建唯一的文件名称;
- 原子打开;
- 独占打开;
- 使用合适的权限打开;
- 程序退出前必须删除。
规则8:遵循最小权限原则
说明:程序在运行时可能需要不同的权限,但对于某一种权限不需要始终保留。例如,一个网络程序可能需要超级用户权限来捕获原始网络数据包,但是在执行数据报分析等其它任务时,则可能不需要相同的权限。因此程序在运行时只分配能完成其任务的最小权限。过高的权限可能会被攻击者利用并进行进一步的攻击。因此,权限在使用完毕后应该及时撤销。在撤销权限时,应该尤其注意以下两点:
/
- 撤销权限时应遵循正确的撤销顺序;
- 完成权限撤销操作后,应确保权限撤销成功。
C安全编程
字符串操作安全
规则C1.1:确保有足够的空间存储字符串的字符数据和’