通用原则实验
实验目的
理解安全编程的意义
了解安全编程的通用原则
实验原理
1.安全编程的概述
在处于繁忙紧张的软件开放周期今天,只有少量或者没有安全经验编程的人参加开发程序是很常见的,个人的在对应用程序中实现占有重要地位与安全很有关系,不光光个人会出现错误,一般组织,团体,为了短时间里完成产品开发,也很少会重视到安全问题,最后受到更多损失的还是团体。一个基本的原则,安全问题存在与任何类型的应用程序,不光光是一般应用程序,连一些有关安全的应用程序也会存在某些安全问题。学习安全编程以期减少缓冲区溢出、整数溢出、格式化字符串攻击、命令注入攻击、目录遍历等典型安全问题。
实验步骤
步骤1:对外部输入进行校验
1.1 对于外部输入(包括用户输入、外部接口输入、配置文件、网络数据和环境变量等)可能用于以下场景的情况下,需要检验入参的合法性:
输入会改变系统状态
输入作为循环条件
输入作为数组下标
输入作为内存分配的尺寸参数
输入作为格式化字符串
输入作为业务数据(如作为命令执行参数、拼装sql语句、以特定格式持久化)
输入影响代码逻辑
这些情况下如果不对用户数据作合法性验证,很可能导致DoS、内存越界、格式化字符串漏洞、命令注入、SQL注入、缓冲区溢出、数据破坏等问题。
1.2 对外部输入验证常见有如下几种方式:
(1)校验输入数据长度:
如果输入数据是字符串,通过校验输入数据的长度可以加大攻击者实施攻击的难度,从而防止缓冲区溢出、恶意代码注入等漏洞。
(2)校验输入数据的范围:
如果输入数据是数值,必须校验数值的范围是否正确,是否合法、在有效值域内,例如在涉及到内存分配、数组操作、循环条件、计算等安全操作时,若没有进行输入数值有效值域的校验,则可能会造成内存分配失败、数组越界、循环异常、计算错误等问题,这可能会被攻击者利用并进行进一步的攻击。
(3)输入验证前,对数据进行归一化处理以防止字符转义绕过校验:通过对输入数据进行归一化处理(规范化,按照常用字符进行编码),彻底去除元字符,可以防止字符转义绕过相应的校验而引起的安全漏洞。
(4)输入校验应当采用"白名单"形式:
"黑名单"和"白名单"是进行数据净化的两种途径。"黑名单"尝试排斥无效的输入,而"白名单"则通过定义一个可接受的字符列表,并移除任何不接受的字符来仅仅接受有效的输入。有效输入值列表通常是一个可预知的、定义良好的集合,并且其大小易于管理。
"白名单"的好处在于,程序员可以确定一个字符串中仅仅包含他认为安全的字符。
"白名单"比"黑名单"更受推荐的原因是,程序员不必花力气去捕捉所有不可接受的字符,只需确保识别了可接受的字符就可以了。这样一来,程序员就不用绞尽脑汁去考虑攻击者可能尝试哪些字符来绕过检查。
步骤2:禁止在日志中保存口令、密钥
2.1 在日志中不能保存口令和密钥,其中的口令包括明文口令和密文口令。对于敏感信息建议采取以下方法:
不打印在日志中;
若因为特殊原因必须要打印日志,则用"*"代替。
步骤3:及时清除存储在可复用资源中的敏感信息
3.1 存储在可复用资源中的敏感信息如果没有正确的清除则很有可能被低权限用户或者攻击者所获取和利用。因此敏感信息在可复用资源中保存应该遵循存储时间最短原则。可复用资源包括以下几个方面:
堆(heap)
栈(stack)
数据段(data segment)
数据库的映射缓存
存储口令、密钥的变量使用完后必须显式覆盖或清空。
步骤4:正确使用经过验证的安全的标准加密算法
4.1 禁用私有算法或者弱加密算法(如DES,SHA1等),应该使用经过验证的、安全的、公开的加密算法。
加密算法分为对称加密算法和非对称加密算法。推荐使用的常用对称加密算法有:
AES
推荐使用的常用非对称算法有:
RSA
数字签名算法(DSA)
此外还有验证消息完整性的安全哈希算法(SHA256)等。基于哈希算法的口令安全存储必须加入盐值(salt)。
密钥长度符合最低安全要求:
AES: 128位
RSA: 2048位
DSA: 1024位
SHA: 256位
步骤5:遵循最小权限原则
5.1 程序在运行时可能需要不同的权限,但对于某一种权限不需要始终保留。例如,一个网络程序可能需要超级用户权限来捕获原始网络数据包,但是在执行数据报分析等其它任务时,则可能不需要相同的权限。因此程序在运行时只分配能完成其任务的最小权限。过高的权限可能会被攻击者利用并进行进一步的攻击。
(1)撤销权限时应遵循正确的撤销顺序:
在涉及到set-user-ID和set-group-ID程序中,当有效的用户ID(user ID)和组ID(group ID)与真实的用户不同时,不但要撤销用户层面(user level)的权限而且要撤销组层面(group level)的权限。在进行这样的操作时,要保证撤销顺序的正确性。
权限撤销顺序的不正确操作,可能会被攻击者获得过高的权限而进行进一步的攻击。
(2) 完成权限撤销操作后,应确保权限撤销成功:
不同平台下所谓的"适当的权限"的意义是不相同的。例如在Solaris中,setuid()的适当的权限指的是PRIV_PROC_SETID权限在进程的有效权限集中。在BSD中意味着有效地用户ID(EUID)为0或者uid=geteuid()。而在Linux中,则是指进程具有CAP_SETUID能力并且当EUID不等于0、真正的用户ID(RUID)或者已保存的set-user ID(SSUID)中任何一个时,setuid(geteuid())是失败的。
正是由于权限行为的复杂性,所以所需的权限在撤销时可能会失败。这会被攻击者利用并进行进一步的攻击。例如Kernel版本在2.2.0-2.2.15的Linux就有一个权限撤销漏洞,当权限功能位置为0时,setuid(getuid())没有如预期的那样撤销权限成功。因此在进行权限撤销操作后,应该校验以保证权限撤销成功。
步骤6:删除或修改没有效果的代码
6.1 删除或修改一些即使执行后、也不会有任何效果的代码。
一些存在的代码(声明或表达式),即使它被执行后,也不会对代码的结果或数据的状态产生任何的影响,或者产生不是所预期的效果,这样的代码在可能是由于编码错误引起的,往往隐藏着逻辑上的错误。
步骤7:删除或修改没有使用到的变量或值
7.1 删除或修改没有使用到的变量或值。一些变量或值存在于代码里,但并没有被使用到,这可能隐含着逻辑上的错误,需要被识别出来,删除这类语句或做相应的修改。