1代码安全介绍2第一章、序言1.1背景1.2编码中的安全问题据美国IDC公司2006年对企业信息安全的调查和统计,70%以上的信息安全问题是因为应用系统自身的安全问题而遭到黑客的攻击,而不是因为网络;同时NIST对近几年的信息安全的事故的跟踪调查显示,几乎92%的信息安全事故都与软件相关。3在软件的编码阶段,主要软件安全问题来源于如下几个方面:软件自身的代码缺陷用户恶意输入不期望的连接在整个软件系统中主要体现在如下几个方面带来安全隐患:输入验证与表示API误用安全特征时间与状态错误处理代码质量封装和环境等安全漏洞第一章、序言1.1背景1.2编码中的安全问题4这些威胁主要是由于开发人员缺乏安全编码的意识和自身对软件安全知识了解不足、所适用的对开发语言和开发技术的缺陷了解不足、没有以黑客思维去看待软件而导致的,表现为攻击者可以利用开发技术自身的漏洞、恶意输入以及异常连接等问题,对应用软件系统进行攻击,发现并利用软件中存在的安全漏洞。第一章、序言1.1背景1.2编码中的安全问题5目前软件代码中的风险非常多,根据CERT的统计,最近几年都稳定在5000多个。对这些安全漏洞的分类目前有很多种,本文主要采用了McGraw提出的分类方法。本文将软件代码中存在的安全漏洞分为8类,分别是:输入验证与表示,API误用,安全特征,时间与状态,错误处理,代码质量、环境和封装。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装6输入验证和表示问题通常是由特殊字符、编码和数字表示所引起的,这类安全问题的发生是对输入的信任所造成的。一些较大的安全问题往往都是由于对输入的信息过度信任造成的。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装7API是调用者与被调用者之间的一个约定,大多数的API误用是由于调用者没有理解约定的目的所造成的。比如如果一个编码人员继承了SecureRandom,返回的却是一个非随机值,那么他就违背了约定。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装8安全特征主要关系认证,访问控制,机密性,密码,权限管理等方面的内容。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装9分布式计算是与时间和状态有关的。也就是说,为了让多个部件之间交互,需要状态共享,而这要花费时间。大多数程序员将他们的工作人格化。他们认为控制器的一个线程会按照他们所想的方式来执行整个程序。然而,现在的计算机能在多任务之间快速切换,采用多核、多CPU或者说分布式系统,两个事件甚至能在同一时间发生。这样一来,在程序员所想的程序执行模式和实际发生的情况之间就会产生问题。这些问题可能涉及到线程、进程、时间和信息之间的非法交互。这些交互通过共享的状态产生:如信号量、变量、文件系统以及任何能够存储信息的东西。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装10错误和错误处理代表了一类API。与错误处理有关的错误是很常见的。与API误用相比,和错误处理相关的安全漏洞一般是两种方式造成的:第一种是:根本忘记处理错误或者只是简单的处理,并没有彻底解决;第二种则是:程序对可能的攻击者泄露了过多的信息或者涉及面太广没有人愿意去处理这些问题。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装11低劣的代码质量会导致不可预测的行为。从用户的角度来看,这通常会表现为低劣的可用性。对于攻击者而言,低劣的代码使他们可以以意想不到的方式威胁系统。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装12封装就是划定强力的分界线。在web浏览器中,这就意味着你的代码模块不能被其他代码模块滥用。在服务端,这意味着要区分校验过的数据和未经校验的数据,区分不同用户的数据,或者区分用户能看到的和不能看到的数据。第二章、代码安全问题分类介绍2.1输入验证与表示2.2API误用2.3安全特性2.4时间与状态2.5错误处理2.6代码质量2.7环境和封装13把输入验证作为软件框架的一部分。集中处理输入验证框架带来的好处如下:1.所有的输入使用一致的输入验证:如果每个模块各自独立实现自己的输入验证,将会很难建立一个统一的输入验证策略。2.有效降低工作量:输入验证比较灵活,分别实现输入验证会导致其工作量成倍的增加。3.对输入验证进行统一的升级和修改:这样如果在输入验证逻辑里发现问题时就可以比较方便的修改。4.失败控制:如果一个集中处理输入验证框架收到它不知道如何处理的输入,很容易就拒绝掉该输入,如果没有采取集中输入验证,开发者很容易忘记进行输入验证。第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击14确保在输入验证之前,新输入的数据不要被添加到程序中(输入的数据不能进入程序代码中被执行)。也就是说,程序默认情况下就要对输入的信息进行验证,不能通过验证的数据将会被拒绝。这样的话,开发者就不会忘记进行输入验证了。第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击15将可信和不可信数据分别储存来保证输入验证总是被执行。可信边界可以被认为是在程序中划定的一条分隔线,一边的数据是不可信的而另一边则是可信的。当数据要从不可信的一侧到可信一侧的时候,需要使用验证逻辑进行判断。需要在程序中定义清晰的可信边界。在一些代码中使用的保存可信数据的数据结构,不能被用来在其它代码中存储不可信数据。使数据穿越可信边界的次数降到最低。当程序混淆了可信和不可信数据的界限时会导致安全边界发生问题,最容易导致这种错误的情况是把可信和不可信数据混合在一个数据结构里。这里举了一个例子:第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击16验证的时候应该验证允许输入的最小和最大长度如果对最大输入长度进行限制,攻击者就很难对系统的其它弱点进行攻击。譬如,对于一个很可能被用来进行跨站脚本攻击的输入域,如果攻击者可以输入任意长度的脚本,那么这显然要比限制输入长度更加危险。而对最短长度的检测可以使攻击者无法忽略强制要求输入的域,同时也无法输入与预期不符的数据。对输入长度的检测是输入验证最基本的要求,但是,好的输入验证并不是仅仅检测输入长度,还可以根据被验证程序的上下文进行更深入的检测。如果程序需要验证一个输入域,验证逻辑对该域的合法值限定得越详细,验证逻辑就能越好地工作。第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击17拒绝验证失败的数据,不试图对其进行修复通过设置默认值来保存一个丢失的输入,处理密码域时自动剪裁掉超过最大长度的输入,替换掉在输入框输入的JavaScript字符等行为是很危险的,不要试图修复一个未能通过输入验证的请求,直接拒绝掉,这样才是安全的。输入验证本身就很复杂,如果和自动错误恢复的代码混合在一起的话,将会造成更大的复杂性,自动错误恢复代码很可能改变请求的含义或者截断验证逻辑。如果我们能够引导用户,使他们提交的请求能够通过输入验证,就能比专注于自动错误恢复代码有效得多。第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击18验证来自HTTP请求中的所有数据,恶意数据可以从表单域,URL参数,cookie,http头以及URL自身传入。无论一个HTTP请求看起来多么的“正常”,都应该对它进行完整的检查。攻击者可以在你提供给他们的web页面里输入任何内容,他们可以完全脱离浏览器和浏览器的一切验证,修改Cookie、隐藏区域、name与value的对应关系,他们可以为了“错误”的目的在“错误”的时间按“错误”的顺序提交URL请求。第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击19不要使你的软件的安全性依赖于配置和维护它的人对来自命令行、系统属性,环境变量以及配置文件的输入都需要进行校验来保证它们是一致的和健全的。如果攻击者对系统的属性进行修改,可以通过命令行,环境以及配置文件来威胁软件系统,这里要求软件开发者不要相信来自命令行、环境以及配置文件的输入。第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击20不要允许攻击者能够写任意的数据到日志里正确的和长期的记录对于在已部署的软件系统中寻找缺陷和发现安全弱点是很重要的。我们可以手工或者自动的分析日志来查找重要的事件、一段时间内的趋势,它是检测系统和用户行为的宝贵资源。由于日志的价值,它也成为了攻击者的目标。如果攻击者可以控制写进日志文件的信息,他们就可以在输入中混入伪造的日志条目来伪造系统事件,更严重的是,如果负责进行日志分析的代码存在漏洞,特定的有恶意的输入很可能触发该漏洞,并引发更加严重的危害。第三章、安全建议—输入处理3.1集中输入验证3.2保证所有输入信息被验证过3.3建立可信边界3.4检测输入长度3.5拒绝验证失败数据3.6验证HTTP请求中的所有组成3.7对来自命令行、环境以及配置文件的输入进行校验3.8控制写入日志信息3.9输入验证时使用最强的形式3.10防范元字符攻击21在指定的环境中使用最强的输入验证方式。按照间接选择,白名单以及黑名单的顺序进行验证。有很多方法来校验一段数据。根据安全的观点,输入验证的过程需要非常清晰,但是在一些复杂的环境中,那些“最安全的输入验证”并不一定切实可行。所以应该在特定的场景中选择一个相对“最强”的输入验证的形式,一方面满足需求,另一方面防止欺诈。不要为了平衡输入验证的安全性与可用性而困惑。正常的输入验证过程只需要捕获通用的错误并提供易于理解的输入验证反馈。而以安