1、 应用系统安全开发技术规范 (版本号 V1.3) 朗新科技股份有限公司 二〇一五年十二月 更改履历 版本号 修改编号 更改时间 更改的 图表和章节号 更改简要描述 更改人 批准人 0.5 2013-11-24 初稿 施伟 施伟 1.0 2015-11-19 修改 宋月欣 陈志明 1.1 2015-11-30 修改 宋月欣 陈志明 1.2 2015-12-3 修改 宋月欣 施伟 1.3 2015-12-3 修改
2、 施伟 注:更改人除形成初稿,以后每次修改在未批准确认前均需采用修订的方式进行修改。 目录 1 背景与目标 1 2 安全编程概念 1 2.1 安全编程 1 2.2 结构化编程 2 2.3
3、 脆弱性 2 2.4 可信计算 2 2.5 安全可信模块 3 2.6 不可信任模块 3 2.7 敏感信息 3 2.8 特权 3 2.9 信息隐藏 3 2.10 中间件 3 2.11 死锁 4 2.12 可信边界 4 2.13 元字符 4 2.14 参数化查询 4 2.15 UNIX JAIL环境 4 2.16 临时文件 4 2.17 信息熵 5 2.18 SSL 5 2.19 TLS 5 2.20 HTTPS 5 2.21 Http会话 5 2.22 Cookie 6 2.23 HttpOnly Cookie 6 3 安全编程原则 6 3.1 统一的
4、安全规范 6 3.2 模块划分 6 3.3 最小化功能 7 3.4 最小化特权 7 3.5 对多任务、多进程加以关注 7 3.6 界面输出最小化 7 3.7 使代码简单、最小化和易于修改 8 3.8 避免高危的服务、协议 8 3.9 数据和代码分离 8 3.10 关键数据传输保护 8 3.11 禁止赋予用户进程特权 8 3.12 使用适当的数据类型 9 3.13 使用经过验证的安全代码 9 3.14 使用应用中间件 9 3.15 设计错误、 异常处理机制 9 3.16 提供备份机制 9 3.17 检查传递变量的合法性 9 3.18 检查所有函数返回代码 9
5、3.19 修改面向用户的操作的反馈缺省描述 9 3.20 文件操作的要求 10 3.21 其他编码原则 10 4 应用安全分析 11 4.1 安全需求 11 4.2 安全威胁 11 4.2.1 Web安全漏洞 11 4.2.2 拒绝服务攻击 12 4.2.3 嗅探攻击 12 4.2.4 中间人攻击 12 4.3 安全约束 13 5 安全编程要求 13 5.1 输入处理 13 5.1.1 建立可信边界 13 5.1.2 验证各种来源的输入 14 5.1.3 保证所有的输入信息是被验证过的 14 5.1.4 对输入内容进行规范化处理后再进行验证 15 5.1.5
6、选择合适的数据验证方式 15 5.1.6 防范元字符攻击 15 5.1.7 拒绝验证失败的数据 15 5.1.8 在服务端进行验证 15 5.1.9 建立统一的输入验证接口 16 5.1.10 控制写入日志的信息 16 5.1.11 从服务器端提取关键参数 16 5.2 输出处理 16 5.2.1 限制返回给客户的信息 16 5.2.2 建立错误信息保护机制 16 5.3 数据库访问 16 5.3.1 合理分配数据库访问权限 16 5.3.2 合理存放数据库连接帐号和密码信息 17 5.3.3 使用参数化请求方式 17 5.3.4 对 SQL 语句中来自于不可信区域
7、的输入参数进行验证 18 5.3.5 对数据库操作的返回数据进行验证 18 5.3.6 分次提取数据 18 5.3.7 通过 row(行)级别的访问控制来使用数据库 18 5.3.8 确保数据库资源被释放 18 5.4 文件操作 19 5.4.1 对上传文件进行限制 19 5.4.2 把文件名以及文件内容作为不可信的输入对待 19 5.4.3 安全的使用文件名 19 5.4.4 使用文件系统访问控制 19 5.4.5 注意文件访问竞争条件 19 5.4.6 安全使用临时文件 20 5.4.7 确保文件系统资源被释放 20 6 安全特征 20 6.1 关注应用的对象重
8、用 20 6.2 用户访问控制信息的机密性 20 6.3 不要在客户端存放敏感数据 20 6.4 避免内存溢出 21 6.5 可配置数据保护 21 6.6 禁止在源代码中写入口令 21 6.7 随机数 21 6.8 使用可信的密码算法 22 6.9 异常管理 22 7 应用安全设计规范 23 7.1 应用安全规划 23 7.2 数据安全等级划分 23 7.3 数据库规划 23 7.3.1 用户权限 23 7.3.2 数据源设计 23 7.3.3 外部系统访问 23 7.4 角色划分 24 7.5 URL规划 24 7.6 程序文件目录规划 24 7.6.1
9、 数据及程序分离 24 7.6.2 静态程序资源 24 7.6.3 程序文件分类 24 7.7 Cookie 24 7.8 文件安全 25 7.8.1 文件存储 25 7.8.2 文件操作 25 7.8.3 文件类型 25 7.9 第三方组件安全 25 7.9.1 组件兼容性 25 7.9.2 组件安全及成熟度 25 7.9.3 组件配置 25 7.10 Web Service 25 7.11 RESTful Web Service 26 7.12 应用安全关注点 27 7.13 应用安全限制应对方案 29 7.13.1 外网隔离 29 7.13.2 外网文件
10、操作 29 7.13.3 正向和反向隔离装置(国网系统) 29 8 应用安全开发规范 30 8.1 Java及Web安全编程规范 30 8.1.1 不信任未知 30 8.1.2 数据层开发 30 8.1.3 会话管理 32 8.1.4 Cookie 33 8.1.5 输入验证 33 8.1.6 输入文件名的验证 34 8.1.7 输出处理 34 8.1.8 敏感信息处理 36 8.1.9 异常信息处理 37 8.1.10 特殊页面跳转 37 8.1.11 文件操作 37 8.1.12 资源释放 38 8.1.13 内存控制 38 8.1.14 外部程序调用漏洞
11、 38 8.1.15 整数溢出 39 8.2 C++安全编程规范 39 8.2.1 不信任未知 39 8.2.2 免缓存区溢出 40 8.2.3 免缓整数溢出 42 8.2.4 域名合法性检查 45 8.2.5 检查返回值 46 8.2.6 产生随机数 47 8.2.7 验证输入文件名 48 8.2.8 类设计注意事项 48 8.2.9 外部程序调用漏洞 50 8.2.10 临时文件处理 50 1 背景与目标 在Internet大众化及Web技术飞速演变的今天,Web安全所面临的挑战日益严峻。黑客攻击技术越来越成熟和大众化,针对Web的攻击和破坏不断增长,W
12、eb安全风险达到了前所未有的高度。 许多程序员不知道如何开发安全的应用程序,开发出来的Web应用存在较多的安全漏洞,这些安全漏洞一旦被黑客利用将导致严重甚至是灾难性的后果。这并非危言耸听,类似的网上事故举不胜举,公司的Web产品也曾多次遭黑客攻击,甚至有黑客利用公司Web产品的漏洞敲诈运营商,造成极其恶劣的影响。 本规范为解决Web应用系统安全问题,对主要的应用安全问题进行分析,并有针对性的从设计及开发规范、开发管理、安全组件框架、安全测试方面提供整体的安全解决方案。 使本组织能以标准的、规范的方式设计和编码。通过建立编码规范,以使每个开发人员养成良好的编码风格和习惯;并以此形成开发小组
13、编码约定,提高程序的可靠性、可读性、可修改性、可维护性和一致性等,增进团队间的交流,并保证软件产品的质量。 2 安全编程概念 2.1 安全编程 安全编程是指开发人员首先需要具备一定的安全知识,然后识别数据在流转(输入、处理和输出)过程中可能面对的威胁,对这些威胁进行分析得出其利用的漏洞,通过合理地编写代码消除这些漏洞,降低软件面临的风险。本规范对开发人员的编码提出统一的安全要求, 主要涉及输入处理、 输出处理、 数据库访问、 文件操作、异常管理等方面,如下图: 输入处理部分能指导开发者避免用户的不良输入;输出处理能指导开发者对输出内容进行过滤; 数据库访问、 文件操作部分则能指
14、导开发者进行数据库查询, 写入文件等操作时进行防护; 而异常管理、敏感数据保护、对象重用等技术则指导开发者改进软件的自身缺陷。 WEB 开发规范部分则指导用户在WEB 系统( B/S 架构应用)的研发方面时如何增加对应用软件的保护。 2.2 结构化编程 结构化编程,一种编程典范。它采用子程序、程式码区块、for循环以及while循环等结构,来取代传统的goto。希望借此来改善计算机程序的明晰性、品质以及开发时间,并且避免写出面条式代码。 2.3 脆弱性 脆弱性指计算机系统安全方面的缺陷,使得系统或其应用数据的保密性、完整性、可用性、访问控制、监测机制等面临威胁。 2.4 可信计算
15、 可信计算的行为会更全面地遵循设计,而执行设计者和软件编写者所禁止的行为的概率很低。 2.5 安全可信模块 审计和访问控制模块是唯一的安全可信模块。 2.6 不可信任模块 除审计和访问控制模块外其它所有模块均为不可信模块。 2.7 敏感信息 系统的敏感信息包括用户身份信息、认证信息、授权信息、交易过程中的私密或隐私信息、其它的敏感信息。 2.8 特权 特权只是允许去做并不是每个人都可以做的事情。 2.9 信息隐藏 信息隐藏指在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是不可访问的。 信息隐藏基本原理框图: 2.1
16、0 中间件 中间件是提供系统软件和应用软件之间连接的软件,以便于软件各部件之间的沟通.中间件技术创建在对应用软件部分常用功能的抽象上,将常用且重要的过程调用、分布式组件、消息队列、事务、安全、连结器、商业流程、网络并发、HTTP服务器、Web Service等功能集于一身或者分别在不同品牌的不同产品中分别完成。 2.11 死锁 死锁是操作系统或软件运行的一种状态:在多任务系统下,当一个或多个进程等待系统资源,而资源又被进程本身或其它进程占用时,就形成了死锁。 2.12 可信边界 可信边界可以被认为是在程序中划定的一条分隔线,一边的数据是不可信的而另一边则是可信的。当数据要从不可信的一
17、侧到可信一侧的时候,需要使用验证逻辑进行判断。 2.13 元字符 元字符就是在编程语言中具有特定含义的字符或者字符串。例如在 SQL 查询中,单引号(‘)是危险的字符;在文件系统路径中两个点号(..)是危险的字符; 在命令 shell 中,分号(;)和双 &(&&)符号同样是危险的字符,而换行符(\n) 对日志文件很关键。 2.14 参数化查询 参数化查询(Parameterized Query 或 Parameterized Statement)是指在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值,这个方法目前已被视为最有效可预防
18、SQL注入攻击 (SQL Injection) 的攻击手法的防御方式。 2.15 UNIX JAIL环境 一个被改变根目录的程序不可以访问和命名在被改变根目录外的文件,那个根目录叫做“chroot监狱(chroot jail,chroot prison)”。 2.16 临时文件 创建临时文件的程序会在完成时将其删除。 2.17 信息熵 信息熵指信息的不确定性,一则高信息度的信息熵是很低的,低信息度的熵则高。 2.18 SSL 安全套接层(Secure Sockets Layer,SSL),一种安全协议,是网景公司(Netscape)在推出Web浏览器首版的同时提出的,目的是为网
19、络通信提供安全及数据完整性。SSL在传输层对网络连接进行加密。 SSL采用公开密钥技术,保证两个应用间通信的保密性和可靠性,使客户与服务器应用之间的通信不被攻击者窃听。它在服务器和客户机两端可同时被支持,目前已成为互联网上保密通讯的工业标准。现行Web浏览器亦普遍将HTTP和SSL相结合,从而实现安全通信。此协议和其继任者是TLS。 2.19 TLS SSL(Secure Sockets Layer)是网景公司(Netscape)设计的主要用于Web的安全传输协议。这种协议在Web上获得了广泛的应用。IETF()将SSL作了标准化,即RFC2246,并将其称为TLS(Transport
20、Layer Security),其最新版本是RFC5246,版本1.2。从技术上讲,TLS1.0与SSL3.0的差异非常微小。 2.20 HTTPS 超文本传输安全协议(缩写:HTTPS,英语:Hypertext Transfer Protocol Secure)是超文本传输协议和SSL/TLS的组合,用以提供加密通讯及对网络服务器身份的鉴定。HTTPS连接经常被用于万维网上的交易支付和企业信息系统中敏感信息的传输。HTTPS不应与在RFC 2660中定义的安全超文本传输协议(S-HTTP)相混。 2.21 Http会话 在计算机科学领域来说,尤其是在网络领域,会话(session)是
21、一种持久网络协议,在用户(或用户代理)端和服务器端之间创建关联,从而起到交换数据包的作用机制,session在网络协议(例如telnet或FTP)中是非常重要的部分。 在不包含会话层(例如UDP)或者是无法长时间驻留会话层(例如HTTP)的传输协议中,会话的维持需要依靠在传输数据中的高级别程序。例如,在浏览器和远程主机之间的HTTP传输中,HTTP cookie就会被用来包含一些相关的信息,例如session ID,参数和权限信息等。 当客户端在多个服务器调取数据时,保持会话状态的一致性是需要注意的,客户端需用同时保持和某一个主机的连接,或者多个服务器端需要共享一个储存会话信息的文件系统或
22、者数据库。否则,当用户在一个新的而不是一开始保存会话信息的主机上提交访问请求的时候,主机会因为无法获知原来主机的会话的访问状态而产生问题。 2.22 Cookie Cookie(复数形态Cookies),中文名称为小型文本文件或小甜饼,指某些网站为了辨别用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。定义于RFC2109。为网景公司的前雇员Lou Montulli在1993年3月所发明。 2.23 HttpOnly Cookie HttpOnly是包含在Http 响应头信息Set-Cookie中的一个额外标志,如果浏览器支持HttpOnly标志的话,在生
23、成Cookie时使用HttpOnly标志可帮助减轻客户端脚本访问受保护的Cookie时带来的风险(客户端脚本不能访问HttpOnly Cookie)。 3 安全编程原则 3.1 统一的安全规范 每个软件项目在设计阶段都应明确在项目实施过程中项目组应遵循的统一规范,具体包括: 1. 命名规则、组件使用规范、异常处理规范、日志处理规范、工具使用要求、 代码集成规范。 2. 针对本规范提出的主要代码脆弱性应进行相应的防范设计,具体内容应在软件概要设计中体现或有单独的文档体现。 3.2 模块划分 1. 软件应该按照安全性划分模块,审计和访问控制模块为安全可信模块,其它模块为不可信任模块。
24、只有安全可信模块才可以执行安全控制功能,其它的模块不能访问安全可信模块的安全信息、功能或者权限。安全可信模块应该与其它模块分离,由经授权的内部专人进行管理。 2. 只有安全可信模块,才能以高安全等级访问系统的敏感信息,对于其他模块限制其访问敏感信息。 3.3 最小化功能 根据“没有明确允许的就默认禁止”的原则, 软件应只包含那些为达到某个目标而确实需要的功能,不应包含只是在将来某个时间需要但需求说明书中没有的功能。 软件在最小化功能建设方面应遵循如下原则: 1. 只运行明确定义的功能。 2. 系统调用只在确实需要的时候。 3. 一次只执行一个任务。 4. 只有在上一个任务完成后才
25、开始下一个任务。 5. 只在确实需要的时候访问数据。 3.4 最小化特权 1. 只为程序中需要特权的部分授与特权。 2. 只授与部分绝对需要的具体特权。 3. 将特权的有效时间或者可以有效的时间限制到绝对最小。 3.5 对多任务、多进程加以关注 软件开发应尽量使用单任务的程序。如果软件需要使用多任务和多进程,应该认真分析研究多任务和多进程会不会发生冲突,同步所有的进程和任务以避免冲突。 同时作为结构化的编程, 每个原子化组件都要保证一个入口和一个出口。如果进程之间需要交互,则这些交互操作应同步。对于每一种可能的交互情况都要考虑相关的安全策略。 3.6 界面输出最小化 软件应保
26、持用户界面只提供必须的功能,没有多余的、不必要的功能,确保用户不能通过用户界面直接访问数据或者直接访问被保护对象。 3.7 使代码简单、最小化和易于修改 开发时应尽量使代码简单、最小化和易于修改。使用结构化的编程语言,尽量避免使用递归和 Goto 声明。使用简单的代码,清除不必要的功能,防止采用信息隐藏方式进行数据保护。 3.8 避免高危的服务、协议 软件应尽量避免使用不加保护的及已被证明存在安全漏洞的服务和通信协议传输文件,如FTP 、SMTP。 3.9 数据和代码分离 软件应该把数据与程序放置在不同的目录中,这里的数据包括远程下载文件等。 3.10 关键数据传输保护 1.
27、软件在传输关键数据时,使用加密算法保证数据在通信过程不被破译,使用数字签名保证数据在传输过程中的一致性和不可否认性。相关的加密算法和签名技术等应符合国家相关法律法规要求。 2. 信息隐藏是不可靠、 效率低的做法,软件应该使用正确的安全保护措施,不要依赖隐藏进行数据保护。 3.11 禁止赋予用户进程特权 用户进程的授权应采用最小授权法,对于软件的普通用户进程,禁止赋予该类进程特权用户权限。特权用户类型包括: 1. 超级用户。 2. 直接操作数据库用户。 3. 安全管理用户。 3.12 使用适当的数据类型 应该小心使用数据类型,尽量使用占用内存较小的数据类型,如可用整型数据的不用
28、实型,特别是在程序接口部分。例如,在一些编程语言中signed 和 unsigned 的数据类型是视为不同的(如C或者C++语言)。 3.13 使用经过验证的安全代码 使用经过验证的安全代码模块和外部源程序,防止潜在的安全风险。 3.14 使用应用中间件 中间件作为一种应用层架构,软件设计应尽可能使用中间件,中间件选型时应选择成熟的、业界主流的中间件产品。 3.15 设计错误、 异常处理机制 软件设计开发时应建立防止系统死锁的机制,异常情况的处理和恢复机制,具体包括错误和异常检测、数据回滚、安全错误通知、错误和异常记录、断点保护等。 3.16 提供备份机制 为保证运行数据的完整
29、性和可用性,软件开发应设计有效的备份策略,根据业务和系统维护需要提供定期或不定期、自动或手动方式的备份机制。 3.17 检查传递变量的合法性 应检查所有传递给系统函数调用(System Calls)或本地调用(Native Calls)的变量的合法性。 3.18 检查所有函数返回代码 应检查所有函数调用返回代码(错误代码),对每个期望的返回值设置相应的处理程序。 3.19 修改面向用户的操作的反馈缺省描述 应对面向用户的操作的反馈缺省描述进行必要的封装,删除有关后台系统或其它敏感信息。 3.20 文件操作的要求 在需要进行文件操作时,应预先设定目前工作路径(全路径名),使用全路
30、径名表示文件的位置。 3.21 其他编码原则 1. 当一个进程对敏感对象(包含秘密信息或包含不可更改信息)使用完后,应立即擦除对象的敏感信息,然后再删除对象。任何不需要使用的资源应及时释放。 2. 确保所有用来指示数组下标的数据(或指针)都指向了一个有效的数组元素。 如果某个函数不能保证下标所指向元素的有效性,或者根本不去检查边界时,不要使用该函数,应该寻找一个安全的函数,或者自己重新编写一个或者封装一个不安全的函数,加上必要的判断条件,使它安全。 3. 当输入不可信任数据时,要在该数据的内容和格式上同时加以检查。对于整数,应该检查数据大小是否超出了能够表示的范围;对于输入的字符串,要
31、确保长度没有溢出,保证每一个字符都是有效的。 4. 对经常使用的类似的 SQL 语句应使用绑定变量的方法,避免数据库执行类似 SQL 语句时重复解析、重复访问数据文件的行为。 5. 程序编码结束后,应对代码进行优化,提高应用处理 SQL 的性能。 6. 禁止使用通配符的方式进行数据的插入操作。 7. 采用数据库作为系统间接口方式时,应建立独立的接口表,并按最小化特权原则进行授权。 4 应用安全分析 4.1 安全需求 系统安全本质上属于信任问题,要保证应用安全,就必须将解决各种操作过程中不可信问题。安全的几个基本要素为机密性、完整性、可用性、可审
32、计性、不可抵赖性等方面的安全要求。 机密性要求保护数据内容不能泄露,加密是实现机密性的要求的常见手段。 完整性要求保护数据内容是完整、没有被篡改的。 可用性要求保护资源是可被按照需要访问。 可审计性对出现的安全问题提供调查的依据和手段。 不可抵赖性建立有效的责任机制,防止用户否认其行为。 4.2 安全威胁 4.2.1 Web安全漏洞 根据结合国网系统安全检测项目常见安全问题及OWASP组织发布的安全漏洞,系统中存在的需要解决的安全风险如下: 序号 安全威胁 产生环节 1 SQL注入 编码 2 失效的身份认证及会话管理 设计、编码 3 跨站脚本(XSS)
33、编码 4 点击劫持(ClickJacking) 编码 4 不安全的直接对象引用 编码 5 安全配置错误 配置实施 6 敏感信息泄露 设计、编码 7 功能级访问控制缺失(失败的URL访问权限限制) 设计 8 跨站请求伪造(CSRF) 编码 9 使用含有已知漏洞的组件 管理、设计 10 未验证的重定向和转发 编码 11 传输层保护不足 设计、编码 12 文件上传漏洞 编码 13 不安全的加密存储 设计、编码 4.2.2 拒绝服务攻击 分布式拒绝服务攻击(英文:Distributed Denial of Service,缩写:DD
34、oS)亦称洪水攻击。顾名思义,即是利用网络上已被攻陷的电脑作为“僵尸”,向某一特定的目标电脑发动密集式的“拒绝服务”式攻击,用以把目标电脑的网络资源及系统资源耗尽,使之无法向真正正常请求的用户提供服务。黑客通过将一个个“丧尸”或者称为“肉鸡”组成僵尸网络,就可以发动大规模DDoS或SYN洪水网络攻击,或者将“丧尸”们组到一起进行带有利益的刷网站流量、Email垃圾邮件群发,瘫痪预定目标受雇攻击竞争对手等商业活动。 4.2.3 嗅探攻击 利用计算机的网络接口截获目的地为其他计算机的数据报文的一种技术。它工作在网络的底层,把网络传输的全部数据记录下来. 嗅探器可以帮助网络管理员查找网络漏洞和检
35、测网络性能。嗅探器可以分析网络的流量,以便找出所关心的网络中潜在的问题。 证明你的网络有嗅探器有两条经验: 1. 网络通讯丢包率非常高:通过一些网管软件,可以看到信息包传送情况,最简单是ping命令。它会告诉你掉了百分之多少的包。如果你的网络结构正常,而又有20%-30%数据包丢失以致数据包无法顺畅的流到目的地。就有可能有人在监听,这是由于嗅探器拦截数据包导致的。 2. 网络带宽出现反常:通过某些带宽控制器,可以实时看到目前网络带宽的分布情况,如果某台机器长时间的占用了较大的带宽,这台机器就有可能在监听。应该也可以察觉出网络通讯速度的变化。 4.2.4 中间人攻击 在密码学和计算机安
36、全领域中,中间人攻击 ( Man-in-the-middle attack,通常缩写为MITM )是指攻击者与通讯的两端分别建立独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通讯双方的通话并插入新的内容。在许多情况下这是很简单的(例如,在一个未加密的Wi-Fi 无线接入点的接受范围内的中间人攻击者,可以将自己作为一个中间人插入这个网络)。 一个中间人攻击能成功的前提条件是攻击者能将自己伪装成每一个参与会话的终端,并且不被其他终端识破。中间人攻击是一个(缺乏) 相互认证的攻击。大多
37、数的加密协议都专门加入了一些特殊的认证方法以阻止中间人攻击。例如,SSL协议可以验证参与通讯的一方或双方使用的证书是否是由权威的受信任的数字证书认证机构颁发,并且能执行双向身份认证。 4.3 安全约束 序号 限制 1 外网不允许部署数据库 2 外网仅提供HTTP、HTTPS访问,不提供等 3 外网访问内网必须通过强隔离装置,仅支持TNS协议(国网系统要求) 4 外网应用不允许直接访问内网关键业务系统数据库 5 其它系统原则上都不允许直接连接营销系统数据库 5 安全编程要求 5.1 输入处理 5.1.1 建立可信边界 需要在程序中定义清晰的可信边界。
38、 在一些代码中用于保存可信数据的数据结构,不能被用来在其它代码中存储不可信数据。使数据穿越可信边界的次数降到最低。 当程序混淆了可信和不可信数据的界限时会导致安全边界发生问题,最容易导致这种错误的情况是把可信和不可信数据混合在一个数据结构里。如下例: 该例中程序接受一个 http 请求并将" usrname "参数放在 HTTP session 里, 但是并未检查用户是否被授权。 usrname =request.getParameter("usrname"); if(session.getAttribute(ATTR_USR)== null){ session.setAttrib
39、ute(ATTR_USR,usrname); } 由于开发者都知道用户是不能直接访问 session 对象的,所以很容易信任来自 session 的所有信息,但是如果在该 session 中混合存储了可信和不可信的数据, 就会违反完全可信边界的原则,带来安全隐患。如果不能很好的建立和维护可信边界,开发者将不可避免的混淆未被验证和已验证的数据,从而导致一些数据在未经验证时就被使用。如果输入的数据在处理前通过一些用户的交互发生了改变, 可信边界就会遇到一定的问题,因为它很可能在所有数据进入之前不能做出完全的输入验证。在这种情况下,维护一个可信的边界就尤为重要,不可信数据应该单独
40、存放在专门存放不可信数据的数据结构内,在经过验证之后才被放在可信区域。 这样在看一段代码时,就很容易识别数据是在可信边界的哪一侧。 5.1.2 验证各种来源的输入 不要将软件的安全性寄托在配置、使用和维护人员的敏锐理解力、深刻洞察力或良好意愿上,不仅需要验证用户输入,而且还要验证所有来自于软件之外的输入,这些输入应当包括下列内容,但并不仅仅局限于此: 1. HTTP 请求消息的全部字段,包括 GET 数据、 POST 数据、 COOKIE 和 Header 数据等。 2. 不可信来源的文件。 3. 第三方接口数据。 4. 从数据库中检索出的数据。 5. 对来自命令行、 环境以及配
41、置文件的输入。 6. 网络服务。 7. 注册表值。 8. 系统性能参数。 9. 临时文件。 5.1.3 保证所有的输入信息是被验证过的 确保在输入验证之前,新输入的数据不能被添加到程序中(输入的数据不能进入程序代码中被执行)。也就是说,程序默认情况下要对所有的输入信息进行验证,不能通过验证的数据将会被拒绝。 5.1.4 对输入内容进行规范化处理后再进行验证 当输入数据包含文件名、路径名、URL 等数据时,应先对输入内容进行规范化处理后再进行验证,如文件路径、URL 地址等数据,需要规范化为标准的格式后再进行验证。 5.1.5 选择合适的数据验证方式 应根据情况综合采用多种
42、输入验证的方法,包括: 1. 检查数据是否符合期望的类型。 2. 检查数据是否符合期望的长度。 3. 检查数值数据是否符合期望的数值范围,比如检测整数输入的最大值与最小值。 4. 检查数据是否包含特殊字符,如: 、 "、 '、 %、 (、)、 &、 +、 \、 \'、 \"等。 5. 应使用正则表达式进行白名单检查尽量避免使用黑名单法。 5.1.6 防范元字符攻击 攻击者通常会使用元字符对应用程序发起攻击。攻击者可以通过对同一个元字符采取多种编码方式或者根据语言特征采取多种实现方式,因此应过滤输入数据中那些具有特定含义的字符,如单引号、双引号、圆括号、分号等。 5.1.7 拒绝
43、验证失败的数据 应拒绝验证失败的输入数据,不试图对其进行修复(如处理密码域时自动剪裁掉超过最大长度的输入,替换掉在输入框输入的 JavaScript 字符等)。输入验证本身就很复杂,如果和自动错误恢复的代码混合在一起的话,将会造成更大的复杂性,自动错误恢复代码很可能改变请求的含义或者截断验证逻辑。如果我们能够引导用户,使他们提交的请求能够通过输入验证,就会比专注于自动错误恢复代码有效得多。在验证失败时,安全的做法是不试图修复一个未能通过输入验证的请求,直接拒绝掉。 5.1.8 在服务端进行验证 仅在客户端进行验证是不安全的,尤其是在 WEB 应用系统中,客户端的验证大多采用脚本(如Jav
44、aScript)来完成,客户端的验证很容易被绕过,安全的做法是在客户端验证的同时,在服务器端也进行验证。 5.1.9 建立统一的输入验证接口 应建立统一的输入验证接口,为整个应用系统提供一致的验证方法,这样可以避免分散验证带来的代码管理混乱,并且可以减少遗漏。 5.1.10 控制写入日志的信息 由于日志数据的价值,它也成为了攻击者的目标。 如果攻击者可以控制写进日志文件的信息, 他们就可以在输入中混入伪造的日志条目来伪造系统事件, 更严重的是,如果负责进行日志分析的代码存在漏洞,特定的有恶意的输入数据很可能触发该漏洞,并引发更加严重的危害。如果日志数据中包含输入数据,应对输入数据进行验
45、证,禁止攻击者能够写任意的数据到日志中。 5.1.11 从服务器端提取关键参数 需要获取参数时,应当从服务器端提取关键参数,禁止从客户端输入,例如产品价格、用户角色、鉴权标志等,如果关键参数允许从客户端输入提供,攻击者可通过伪造或篡改输入数据造成程序关键逻辑错误。 5.2 输出处理 5.2.1 限制返回给客户的信息 编码时应该限制返回给客户与业务处理无关的信息,禁止把重点保护数据返回给不信任的用户,避免信息外泄。 5.2.2 建立错误信息保护机制 开发人员在编码时,禁止将详细错误信息直接反馈到客户端,详细错误信息中包含系统信息、文件和目录的绝对路径信息,应对错误信息进行规整和清理
46、后再返回到客户端,建议只向客户端返回错误码,详细错误信息可以记录在后台服务器。 5.3 数据库访问 5.3.1 合理分配数据库访问权限 应按照“最小化原则” 为应用程序分配数据库访问权限。 避免为应用程序分配过大或不必要的数据库权限,禁止将数据库 DBA 权限分配给应用程序。 5.3.2 合理存放数据库连接帐号和密码信息 禁止在应用程序代码和配置文件明文存放数据库连接帐号和密码信息,建议对数据库连接账户和密码信息加密后再保存在配置文件中。 5.3.3 使用参数化请求方式 使用参数化的 SQL 请求是防止 SQL 注入的有效方法。导致 SQL 注入漏洞的原因就是攻击者可以改变 S
47、QL 请求的内容,攻击者提交的数据变成了 SQL 执行命令的一部分。在构造 SQL 请求时,开发者应当知道哪些应该被翻译为数据而哪些应该被翻译为命令的一部分。如果正确的使用参数化的 SQL 语句,就可以通过不允许数据指向改变的方法来防御几乎所有的 SQL 注入攻击。参数化的 SQL 语句通常是由 SQL 字符构造的,但是来自客户的数据是需要与一些绑定参数组合在一起的。也就是说,开发者使用这些绑定参数来准确的向数据库指出哪些应该被当作数据,哪些应该被当作命令。当程序要执行该语句的时候,它就会告知数据库这些绑定参数的运行值,这样就避免了数据被认为是命令语句而被执行的错误。 但是,如下是 SQL 请
48、求中包含字符串拼接造成 SQL 注入漏洞的例子: 这是一个 java web 应用程序,让用户在数据库中搜索一些信息,用户可以指定要搜寻的对象名称,并用以下代码执行这些查询: ... Stringitem =request.getParamater("item"); String q="SELECT * FROM records WHERE item="+item; PreparedStatementstmt= conn.prepareStatement(q); ResultSetresults=stmt.execute(); ... 虽然本例
49、程序 使用 了 参数化的 接口 , 但是它 犯了 一个很常见的 错误: 把用 户 的 输入作 为 prepareStatement() 的参数传入,如果允许用户控制 PreparedStatement 的内容,参数化 SQL 提供的安全特性就会失去效果,很多 SQL 注入攻击的关键字也会被包含在之前构造的语句中。 合理的使用参数化 SQL 代码如下: ... Stringitem =request.getParamater("item"); String q="SELECT *FROM records WHEREitem=?"; PreparedStatementstmt= con
50、n.prepareStatement(q); stmt.setString(1,item); ResultSetresults=stmt.execute(); ... 5.3.4 对 SQL 语句中来自于不可信区域的输入参数进行验证 在一些复杂的情况会出现要求输入影响 SQL 语句结构的情况,譬如要求在 where 子句里增加一个动态约束,这时应对来自不可信区域的输入参数进行验证。 5.3.5 对数据库操作的返回数据进行验证 不应盲目信任来自数据库的数据,建议对来自数据库的数据进行验证,确保其格式正确且能够安全的使用: 1. 检查 SQL 请求的返回记






