4.4.4  安全编码规范

4.4.4 安全编码规范

1.安全编码概述

根据CVE(Common Vulnerabilities and Exposures,简称CVE,即通用漏洞与披露)的统计,截至2014年10月,全球已暴露漏洞75000多项。安全漏洞可以在软件开发生命周期的任何阶段被引入,包括安全需求不明确、设计存在逻辑错误、编码规范不合理等,可能带来技术漏洞、软件部署困难、容易在维护更新中引入缺陷等问题。

一般来说,开发安全的软件要比在软件完成以后再纠正安全问题的成本低很多。对于编码阶段可能被引入的漏洞,软件开发人员应严格检查程序中的错误,尽量在部署之前就减少或清除其中的漏洞。遵循安全编码规范编写代码,并进行源代码安全审核是降低软件安全漏洞的一种有效方式。

2.安全编码规范的制定

编程语言有机器语言、脚本语言、汇编语言、高级语言之分。商业银行信息系统研发主要使用高级语言,常见的有C、C++、Java、C#、SQL、COBOL等。对于每一种语言,其编写的软件中都有可能存在安全漏洞。一种简便的方法是对所有的语言制定一套通用的安全编码规范,例如《OWASP安全编码规范快速参考指南》。

OWASP(Open Web Application Security Project,开放式网络应用程序安全项目)是一个非营利组织。它不附属于任何企业或机构,提供有关计算机和互联网应用程序的公正、实际、有成本效益的信息,其目的是协助个人、企业和机构来发现和使用可信赖软件。《OWASP安全编码规范快速参考指南》是一个与技术无关的通用软件安全编码规范,提供了一种综合的清单模式,内容容易阅读且易理解。该指南的重点不是漏洞和攻击,而在于对安全编码的要求。它包括了软件安全原则的介绍和关键术语列表,可作为安全编码工作的启动工具和建议参考,以帮助开发人员快速了解安全编码规范[42]

对于商业银行来说,另一种可行的方式是针对常用的编码语言分别单独制定安全编码规范,例如“C安全编码规范”“Net安全编码规范”“Java安全编码规范”等。

安全编码规范需要经过组织评审后发布实施,在实施初期需要结合开展安全编码的培训工作,以提高软件开发人员的安全编码意识和能力。结合源代码安全审核工作,可以更有效地确保安全编码规范得到有效执行,显著降低软件安全漏洞的数量。为确保安全编码规范的有效性,同样需要关注最新软件安全漏洞的暴露情况和业界发展趋势,及时更新维护安全编码规范[43]

3.安全编码规范的主要内容

安全编码规范给出了防止出现源代码安全漏洞的最佳编码实践。这些最佳编码实践并不是针对每个漏洞的专门的防范措施,而是经过总结提炼后,以规则的形式要求开发人员在编程过程中遵守的标准。

安全编码规范对于每一个最佳实践均应提供说明,并给出错误和正确的编程实例进行对比。例如,“Java安全编码规范”对于防范SQL注入攻击的安全编码规范如下[44]、[45]

编码规范:防范SQL注入攻击。

产生原因:拼接SQL查询语句时,对于输入参数的验证机制不完善。

修复说明:尽量使用参数化查询和存储过程查询,不要使用原生查询。

错误代码:

//拼接SQL查询语句

String sqlString=978-7-111-51949-2-Part02-25.jpgSELECT∗FROM users WHERE fullname=978-7-111-51949-2-Part02-26.jpg978-7-111-51949-2-Part02-27.jpg+

form.getFullName()+978-7-111-51949-2-Part02-28.jpg978-7-111-51949-2-Part02-29.jpgAND password=978-7-111-51949-2-Part02-30.jpg978-7-111-51949-2-Part02-31.jpg+form.getPassword()+978-7-111-51949-2-Part02-32.jpg978-7-111-51949-2-Part02-33.jpg978-7-111-51949-2-Part02-34.jpg

正常输入:username=tony,password=123456

SELECT∗FROM users WHERE username=978-7-111-51949-2-Part02-35.jpgtony978-7-111-51949-2-Part02-36.jpgAND password=978-7-111-51949-2-Part02-37.jpg123456978-7-111-51949-2-Part02-38.jpg

攻击输入:username=tony,password=978-7-111-51949-2-Part02-39.jpgOR978-7-111-51949-2-Part02-40.jpg1978-7-111-51949-2-Part02-41.jpg=978-7-111-51949-2-Part02-42.jpg1

SELECT∗FROM users WHERE username=978-7-111-51949-2-Part02-43.jpgtony978-7-111-51949-2-Part02-44.jpgAND password=978-7-111-51949-2-Part02-45.jpg978-7-111-51949-2-Part02-46.jpgOR978-7-111-51949-2-Part02-47.jpg1978-7-111-51949-2-Part02-48.jpg=978-7-111-51949-2-Part02-49.jpg1978-7-111-51949-2-Part02-50.jpg

正确方式一:参数化查询

Stringcustname=request.getParameter(978-7-111-51949-2-Part02-51.jpgcustomerName978-7-111-51949-2-Part02-52.jpg);

//此处应该还有其他输入参数验证逻辑,被忽略

String query=978-7-111-51949-2-Part02-53.jpgSELECT account_balance FROM user_data

WHERE user_name=?978-7-111-51949-2-Part02-54.jpg

//执行参数化查询

PreparedStatementpstmt=connection.prepareStatement(query);

pstmt.setString(1,custname);

ResultSetresults=pstmt.executeQuery();

正确方式二:存储过程查询

String custname=request.getParameter(978-7-111-51949-2-Part02-55.jpgcustomerName978-7-111-51949-2-Part02-56.jpg);

//此处应该还有其他输入参数验证逻辑,被忽略

try{

//存储过程查询准备

CallableStatementcs=connection.prepareCall(978-7-111-51949-2-Part02-57.jpg{call

sp_getAccountBalance(?)}978-7-111-51949-2-Part02-58.jpg);

//设置查询参数

cs.setString(1,custname);

//执行查询

ResultSetresults=cs.executeQuery();

//查询结果处理逻辑

}catch(SQLExceptionse){

//?logginganderrorhandling

}

安全编码规范的另一重要内容就是针对每一种语言给出禁用函数列表,以及可能导致安全漏洞的加密算法。同时,不能仅仅简单地禁用危险函数,还必须提供指导性的替代方式。比较完善的安全编码规范还会附带安全编码Checklist(检查表)。这些Check-list同时也是源代码安全审核工具的检查规则。经验表明,只要提供良好的指导并配备检查验证工具,项目研发团队会逐渐主动遵守安全编码规范[43]。