gitbook/代码精进之路/docs/90026.md
2022-09-03 22:05:03 +08:00

9.2 KiB
Raw Permalink Blame History

43 | 编写安全代码的最佳实践清单

像以前一样,当大家看到“最佳实践清单”这个标题的时候,就意味着这一个模块又到了总结的时候了。

这一模块我们从代码安全的角度出发,探讨了如何编写安全的代码。首先我们再来重温一下,为什么需要安全的代码呢?

为什么需要安全的代码?

1.代码质量是信息安全的基础

大部分的信息安全事故,是由软件代码的安全缺陷引起的。没有安全质量保证的代码,建立不起有效、可信的信息系统。信息系统的安全,主要依赖的不是信息安全技术专家,而是我们每一个编写代码的工程师。

2.安全漏洞的破坏性难以预料

直到真实的安全问题发生之前,我们都难以预料软件的安全漏洞到底有多大的破坏性。一个小小的安全漏洞,如果被攻击者抓住了时机,就可以瞬间摧毁多年的苦心经营和良好声誉,把公司推到舆论的风口浪尖,甚至使公司面临毁灭性的风险和挑战。

3.安全编码的规则可以学得到

由于安全攻击技术的快速发展,安全编码涉及到的细节纷繁复杂,安全问题的解决甚至需要大规模、大范围的协作。编写安全的代码不是一件轻而易举的事情。但是,安全编码的规则和经验,却是可以学习和积累的。使用必要的安全管理工具,开展代码评审和交流,也可以加速我们的学习和积累,减少编写代码的安全漏洞。

要想掌握安全编码的技术,熟练修复软件漏洞的实践,我们需要跨过意识、知晓、看到三道关卡。面对最新的攻击技术和安全问题,通过每一道关卡都障碍重重。我们要主动地跟踪安全问题的最新进展,学习最新的安全防护技术。

及时更新自己的知识,掌握难以学习到的知识和技能,也是构建和保持我们竞争力的一个重要办法。

编写安全代码的基本原则

1.清楚调用接口的行为

使用不恰当的接口,是代码安全风险的主要来源之一。我们一定要了解、掌握每一个调用接口的行为规范,然后在接口规范许可的范围内使用它们。不要去猜测接口的行为方式,没有明文规定的行为,都是不可靠、不可信的行为。

2.跨界的数据不可信任

跨界的数据面临两大问题:一个问题是数据发送是否可信?另一个问题是数据传递过程是否可靠?这两个有任何一个问题不能解决,跨界的数据都可能被攻击者利用。因此使用跨界的数据之前,要进行校验。

3.最小授权的原则

信息和资源,尤其是敏感数据,需经授权,方可使用。所授予的权力,能够让应用程序完成对应的任务就行,不要授予多余的权力。

4.减小安全攻击面

减小、简化公开接口,缩小可以被攻击者利用的攻击界面。比如,设计更简单直观的公开接口,使用加密的数据传输通道,只对授权用户开放服务等等,这些措施,都可以减少安全攻击面。

5.深度防御的原则

使用纵深防御体系防范安全威胁。要提供深度的防御能力,不能仅仅依靠边界的安全。编写代码,要采用谨慎保守的原则,要解决疑似可能出现的安全问题,要校验来源不确定的数据,要记录不规范的行为,要提供安全的应急预案。

安全代码的检查清单

安全管理

  • 有没有安全更新的策略和落实计划?

  • 有没有安全漏洞的保密共识和规范?

  • 有没有安全缺陷的评估和管理办法?

  • 软件是不是使用最新的安全修复版?

  • 有没有定义、归类和保护敏感信息?

  • 有没有部署多层次的安全防御体系?

  • 安全防御能不能运转良好、及时反应?

  • 不同的安全防御机制能不能独立运转?

  • 系统管理、运营人员的授权是否恰当?

  • 有没有风险管理的预案和长短期措施?

代码评审

  • 数值运算会不会溢出?

  • 有没有检查数值的合理范围?

  • 类、接口的设计,能不能不使用可变量?

  • 一个类支持的是深拷贝还是浅拷贝?

  • 一个接口的实现,有没有拷贝可变的传入参数?

  • 一个接口的实现,可变的返回值有没有竞态危害?

  • 接口的使用有没有严格遵守接口规范?

  • 哪些信息是敏感信息?

  • 谁有权限获取相应的敏感信息?

  • 有没有定义敏感信息的授权方案?

  • 授予的权限还能不能更少?

  • 特权代码能不能更短小、更简单?

  • 异常信息里有没有敏感信息?

  • 应用日志里有没有敏感信息?

  • 对象序列化有没有排除敏感信息?

  • 高度敏感信息的存储有没有特殊处理?

  • 敏感信息的使用有没有及时清零?

  • 一个类有没有真实的可扩展需求能不能使用final修饰符

  • 一个变量能不能对象构造时就完成赋值能不能使用final修饰符

  • 一个方法子类有没有重写的必要性能不能使用final修饰符

  • 一个集合形式的变量,是不是可以使用不可修改的集合?

  • 一个方法的返回值,能不能使用不可修改的变量?

  • 类、方法、变量能不能使用private修饰符

  • 类库有没有使用模块化技术?

  • 模块设计能不能分割内部实现和外部接口?

  • 有没有定义清楚内部数据、外部数据的边界?

  • 外部数据,有没有尽早地完成校验?

  • 有没有标示清楚外部数据的校验点?

  • 能不能跟踪未校验外部数据的传送路径?

  • 有没有遗漏的未校验外部数据?

  • 公开接口的输入,有没有考虑数据的有效性?

  • 公开接口的可变化输出,接口内部行为有没有影响?

  • 有没有完成无法识别来源的数据的校验?

  • 能不能不使用序列化技术?

  • 序列化的使用场景,有没有足够的安全保障?

  • 软件还存在什么样风险?

  • 有没有记录潜在的风险问题?

  • 有没有消除潜在风险的长期预案?

  • 有没有消除潜在风险的短期措施?

  • 潜在的风险问题如果出现,能不能快速地诊断、定位、修复?

小结

学会编写安全的代码,是一个优秀的、专业的软件工程师的核心竞争力之一。与规范、经济的代码相比,安全的代码有很多不同的特点。

代码不规范和效率不高,业务也可以运转,然后慢慢优化,逐渐演进。但代码一旦出现安全问题,遭受攻击,损失立即就会反映出来,而且破坏性极大。

代码不规范,看的人立刻就会觉得很难受。代码的效率不高,业务运转不通畅,同样会有及时的反馈。就代码的安全层面来说,一般情况下直到攻击发生之前,我们可能都不知道代码是否存在安全问题。等到攻击真实发生的时候,损失已经成为事实了。

代码的规范原则,是一个相对容易掌握的内容。高效的代码,也有很多成熟的经验可以学习。可是,代码的安全,却是一个攻易守难的问题。哪怕我们今天知道了所有的攻击和防护方法(这当然不可能),如果明天出现了一种新的攻击手段,而且全世界只有一个人知道,我们的系统都存在潜在的安全威胁。

编写安全的代码,需要掌握复杂的知识,而且需要大规模的合作。我们之前提到过三道槛,具体展开来是这样的:

我们要想掌握安全编码的技术,熟练修复软件漏洞的实践,需要先过三道关。
第一道关是意识Conscious。也就是说要意识到安全问题的重要性以及意识到有哪些潜在的安全威胁。
第二道关是知晓Awareness。要知道软件有没有安全问题安全问题有多严重。
第三道关是看到Visible。要了解是什么样的问题导致了安全漏洞该怎么修复安全漏洞。

在意识、知晓、看到这三道关面前,我们要打开自己的视野,保持强烈的好奇心,从全世界范围内学习成熟的经验、先进的技术以及最新的进展。

其中最重要的资源是NIST提供的安全漏洞数据库。这个数据库的使用方式有两种:第一种是了解自己的系统有没有最新的安全漏洞;第二种是学习最新的安全威胁的攻击方法和防范技术。

一起来动手

我们今天的练手题就学着使用NIST的安全漏洞数据库。请你从这个数据库里,选择一个或者几个安全漏洞,试着看一下你的系统有没有类似的安全威胁?这个安全漏洞的攻击方式是什么样的?这个安全漏洞的问题出现在哪里?该怎么防范?

欢迎你在留言区留言、讨论,分享你的阅读体验。

如果你觉得这篇文章有所帮助,欢迎点击“请朋友读”,把它分享给你的朋友或者同事。