一、安全性
只要谈到安全性,可能大家更多的是想到黑客的攻防这些,比如木马、病毒、漏洞等等。本文的安全性当然也涉及到这些,但重点是偏向于代码引起的安全性。也就是说,在环境层级和代码层级的安全性上,本文倾向于代码层级,特别是设计和控制。而对如操作系统本身的漏洞或者被攻击的这种安全性,不会涉及。
程序的安全性要想增强,从开始设计到最终编码,包括第三库的应用、接口的设计和数据库的使用等等都需要全盘的考虑。那么安全性的边界在哪儿呢?也就是说,不能因为一个安全性将成本等其它因素都放置脑后,这也是前面的讲设计时说的平衡。
安全的边界性一定是和整个程序的应用场景具体相关,比如一个隔离的单机的应用程序(比如一个车间内的某个非联网设备内部的控制程序),对来自于网络相关的安全性则可以近似的忽略。而这种情况下,其安全性更强调的是程序运行不出现异常即非设计功能的出现。比如一个机械臂程序,它设计只能在30度范围抓取物品,而操作员发现在某些情况下它可以360度抓取物品,这就是安全性没做好。
程序的设计难免会有问题,即使是经过验证上线的程序亦是如此。但开发者必须保证,在出现异常时,不能引起事故。仍然是那个机械臂的例子,如果程序检测到不在30度范围内的动作就必须停止工作或者报警。这就是一种补救措施,也就是安全性的体现。
安全性体现主要包括以下几个方面:
1、预先的论证和设计
这意味着在实际需求提出时,就要将各种可能出现的问题进行分析、汇总。比如先确定安全的目标范围,有条件的可以在此基础上进行建模分析。同时,很多行业内部本身就有成熟的安全性、合规性要求。举一个大家常见的,“起重臂下严禁站人”,都多多少少看过吧。
另外,在一些行业可能已经形成了通用的安全标准,在设计前一定要进行标准的学习,要符合标准的实际要求。另外,详尽的开发文档和测试需求也必须重视。
2、程序设计安全控制
程序设计时,安全控制要掌握好几个原则:
a) 设计控制边界,包括权限边界、应用场景、运行条件等等
b) 防御性设计,严格控制输入和输出,特别强调对用户的操作管理,必须要有日志控制等
c) 设计安全可靠的接口,防止出现异常调用。比如在调用数据库操作时,对Sql语句进行分析,防止出现意外
d) 使用安全可靠的第三方软件,包括框架和软件
e) 设计功能边界,尽量防止出现一专多能(即出现一个强大的多功能模块)
f) 数据安全,控制访问的数量,最好是专一访问,然后再根据实际需求分发
g) 冗余控制,即出现错误后,尽量可以自行恢复或者不形成破坏性结果
h) 不被客户的非关键需求过度让步
3、代码编写
a) 内存的安全管理,包括越界、空指针、悬垂指针等等。另外,内存的大小控制和内存碎片也相当重要,特别是对于一些7×24运行的程序更是如此
b) 多线程的处理,特别是在并行编程和线程池这种复杂场景下,要确保数据和控制的安全性。比如前面提到的条件变量的误触发管理和丢失的控制等;另外在某些情况下,效率也必须考虑
c) 输入输出的控制,即所有的数据输入和输出都需要有验证过程,包括但不限于权限、合规性等
d) 接口的编写要尽可能遵行最小功能原则,尽可能保持二进制兼容等要求
e) 整体的编码要符合常见的设计原则和设计范式,特殊情况必须有说明,比如冗余
f) 安全处理所能控制的所有的错误和异常,尽量做到代码的异常不引起程序的崩溃和不可控行为
g) 拥有完整的单元测试
其实安全性不光体现在设计开发阶段,包括运行、维护等都有,但这就是另外一个话题了。
二、健壮性
健壮性又可以近似和稳定性划成一个问题。在早些年,它也经常被称做鲁棒性。健壮性意味着程序可以在最大可能的情况下稳定运行而不会出现异常或者即使出现异常也不会影响核心功能的使用,从而导致灾难性的结果。健壮性往往是客户最容易接触到,所以经常为客户反馈出来。
要实现程序的健壮性同样需要在设计和开发时共同考虑具体实施:
1、设计阶段
a) 有明确的健壮性需求
b) 分层分模块设计,防止异常和错误的链式传导
c) 可靠的容错设计
d) 异常情况下的最小功能处理,即程序可降级保障程序的核心安全运行
2、编码控制
a) 处理可能预见的问题和异常,尽最大可能让程序可以自行恢复正常运行
b) 安全的资源控制,如内存、CPU以及其它一些文件IO等资源
c) 全链路数据的验证和安全管理,包括但不限于输入、输出和计算等
d) 日志和监控,保障在出现问题时可及时发现并解决
e) 完整的单元测试
三、二者的关系
从上面的分析可以看出,安全性和健壮性其实很多是相通的。安全性确定健壮性,健壮性影响定安全性。只是从设计来看,安全性和健壮性强调的点细节有所不同,但二者一定是互相影响互相促进的。它们两个的本质都是为了提高软件的可靠性和可用性。只不过发力的目标和着眼点不同。
做为一个设计者最重要的是取舍,也就是前面提到的平衡,在安全性和健壮性中,根据实际的需求达到一个最佳的平衡点,当然,最佳是一个相对的概念,大家不要过分的追求完美。毕竟一个项目的成败,大多时候并不是靠技术人员决定的。
四、实例
在上面的健壮性和安全性时,可能大家有点迷糊,不过看下面的例子就明白了。比如有一个程序,要实现从公司的网络服务器上下载升级包,那么安全控制就是首先确保下载的地址的正确性验证、文件大小、HASH、时间戳等。而健壮性控制就是保证下载的文件中如果有特殊字符不会引起异常,在下载过程中网络中断后有完整的处理机制(如断点续传等)而不会造成文件的破坏甚至程序的整体的异常。
那么如果不进行安全验证,可能后续的健壮性处理虽然通过,但达不到要求。最简单的是下载了一个老的升级包,虽然可以正常运行,但实际达不到目的。
五、总结
程序的开发,往往不是一两个因素影响,而是一个多重的因素整体互相影响确定的。而一个优秀的设计者就是要在这些因素中,找到关键的因素,也就是常说的主要矛盾,并处理在某些情况下,次要矛盾转化为主要矛盾时如何处理(比如程序异常时,保存了部分的文件怎么办)。
所以,有丰富的理论和实践形成的指导思想,才是解决问题的关键所在。好医生是在病患未起时就将其消灭。也就是所谓的预防大于治疗。而目前看来,在软件开发中,仍然是以“治疗”为主。希望开发者们能尽量把重心前移,减少亡羊补牢的情况!