gitbook/软件工程之美/docs/95818.md
2022-09-03 22:05:03 +08:00

173 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 34 | 账号密码泄露成灾,应该怎样预防?
你好我是宝玉。我们日常总能看到各种与黑客和网络安全相关的新闻而这其中大部分安全问题都和软件程序有关系。比如说像CSDN数据库泄露事件、携程泄露用户银行卡信息事件、有些电商网站用户可以篡改支付购买金额等等。
在软件项目开发时,安全是一个很容易被忽略的问题,但又可能会造成严重损失。所以我们在软件开发时有必要对安全问题引起重视,防患未然,构建安全软件。
今天,我将带你了解一下软件开发中的安全问题,学习如何构建安全的软件,以及出现了安全问题之后该怎么办。
## 安全问题本质是技术风险
如果你还记得《[15 | 风险管理不能盲目乐观凡事都应该有B计划](http://time.geekbang.org/column/article/88259)》这篇文章中的内容,我在其中提到,风险是指不确定的事件,一旦发生,将会造成消极的影响。
**安全问题,本质上也是一种技术风险,没发生问题的时候一切都好,一旦发生就会有严重的影响。**在对安全问题的应对上,你也可以借鉴对风险管理的方法来改进软件的安全问题,也就是风险识别、风险量化、应对计划和风险监控。
在做风险管理时,首先要做的就是识别风险和对风险量化,对于安全问题,你也可以先思考一下:软件项目中安全问题的主要来源是什么?搞清楚安全问题的来源,以及造成的后果,你就可以对软件中导致安全问题的情况有一个基本的识别和量化。
软件中的安全问题来源主要可以分为以下三大类。
* 第一类:恶意输入
很多我们熟知的软件安全问题都属于此类型,就是黑客通过恶意输入,然后绕过软件限制对系统进行攻击和破坏。
像SQL注入就是黑客把SQL命令输入到软件的输入框或网页的URL查询参数欺骗服务器执行恶意的SQL命令。这样可以绕过密码验证登录管理员账号或者删除数据库数据甚至控制服务器。
还有像XSS攻击将恶意代码通过外部参数或者用户输入的方式植入网页中获取用户的Cookie等敏感信息、盗用管理员权限甚至非法转账。
这类问题都是由于恶意输入导致的,应对恶意输入的问题,最简单有效的方式就是对用户输入的数据,做严格的校验,格式化。
* 第二类:假冒身份
很多程序对于用户身份的校验比较弱,可能会导致黑客假冒用户身份做出超越权限的事情。
比如说,我见过有的网站,把后台入口隐藏起来,而没有做权限控制,导致黑客猜到地址后就可以进入后台操作。
还有的游戏后台不做验证,直接接收传入的数据,导致可以伪造游戏用户发送数据,破坏游戏平衡。
如果对用户的身份不做严格的验证,很可能就会导致假冒身份的安全问题。应对策略就是要对用户的身份做验证,尤其是涉及敏感权限的操作,甚至要做两重验证。
* 第三类:数据泄露
很多软件都储存了用户的敏感信息,比如用户帐号密码信用卡记录,或者服务器的敏感信息,比如数据库连接字符串、登录帐号密码,而这些数据是有被泄露风险的。
一些软件会把服务器上的敏感信息打包在程序中,而程序可能会被反编译导致敏感数据泄露。携程泄露用户银行卡信息事件中,就是因为把用户信用卡信息记录在日志中,日志泄露导致用户信用卡信息也被泄露,造成盗刷等严重问题。
还有CSDN对用户密码明文存储到数据库中数据库泄露后用户密码也跟着一起泄露了而大多数用户习惯于在不同的网站也用相同的密码导致在其他网站的密码也一起泄露了。
对于软件来说, **我们不能假设数据存储是安全的,而是要考虑到数据是有泄露的可能,提前做好预防措施,对敏感数据进行加密。**
在了解了这些常见的安全问题来源和可能带来的后果之后,我们在软件开发时,就可以对薄弱环节、重点问题进行提前预防,在开发时就考虑到可能的安全漏洞,做出科学的应对方案。
## 如何预防软件中的安全问题?
预防软件中的安全问题,也可以参考对风险管理的策略。在风险管理中,对风险识别和量化后,接下来就是要制定应对计划了。
很多开发人员觉得安全问题,只要在软件开发完成之后,测试阶段做一个安全测试就可以了,但这样做等于把安全问题留到了最后环节,是很难达到对安全问题进行高质量管控的。
一方面,对于安全测试来说,很难覆盖到所有可能存在的场景,可能会出现疏漏,导致安全漏洞被利用。另一方面,如果测试阶段发现安全问题,可能需要修改大量代码,甚至于要重新设计,这时候成本就太高了。
所以应对安全问题,最好的方式就是在整个生命周期中都做到重视安全问题,各个阶段都考虑到安全方面的问题,才能真正做到防患于未然,构建出安全的软件。
那么在软件开发的各个阶段,都需要如何考虑好安全方面的问题呢?
**需求阶段**
需求是软件项目的源头,**在确定需求,做产品设计的时候,不仅要考虑到功能上的需求,还要同时考虑到安全方面的要求。**这样当你在架构设计的时候,就不会轻易遗漏安全方面的架构设计。
尤其是对于一些外包项目,如果在需求中没有提出安全需求,大概率外包公司是不会帮你考虑这些需求的。
需求阶段,涉及用户输入的内容,需要考虑到可能的恶意输入,做出针对性预防措施;对于涉及用户权限的,要求有身份的验证,一些对安全要求极高的,可以在需求上就要求做双重验证;对于有敏感数据的,可以在需求上就要求对数据进行加密。
举个简单例子,比如说用户登录功能,如果让你提出安全方面的需求,你会考虑到哪些需求?这里我简单列几个功能参考:
* 登录网页使用Https或者在传输密码时加密
* 增加图形校验码,避免恶意攻击;
* 密码失败次数过多,应该锁定用户一段时间;
* 记录用户登录IP。
当你在需求阶段就提出了安全性的需求,设计、实现和测试时自然不会遗漏掉安全方面的内容,从源头上就让大家有了安全方面的意识。
**设计阶段**
在做设计架构时,最重要的事就是要把安全加入到设计目标,有了安全方面的设计目标,自然能找到很多安全相关的解决方案。
为了保障在设计时就考虑好安全方面的问题,在做架构设计方案的评审时,也需要增加安全方面的评审,确保有安全方面的考虑,确保技术方案切实可行。
现在架构设计领域,也有了一些业界公认的好的安全相关的设计原则,比如说攻击面最小化、权限最小化、纵深防御等。
* 攻击面最小化
攻击面就是指程序被用户直接访问到的部分比如API、网站等这些暴露给用户的地方也是最可能被黑客攻击的地方。
暴露的面越多则风险越高,攻击面最小化的设计原则,就是说尽量减少暴露黑客可能发现并试图利用的攻击面数量。
举例来说,你的数据库应该关闭外网访问,避免黑客直接攻击数据库导致数据泄露。还有像对于一些复杂的多网站业务系统,实行单点认证,就可以让所有业务都在一个地方登录,你可以在这一个地方做到足够安全,这样所有网站的登录都是相对安全的。
* 权限最小化
权限最小化的设计原则就是对于系统的用户、文件访问、进程运行等,都只给予其能拥有的最小权限,这样可以保证一个应用程序或者网站被攻击、破解,能将损害降到最低。
举例来说以前在部署Asp.Net程序的时候运行Asp.Net的程序是单独的一个用户这个用户所拥有的权限是只能运行程序所在目录不能超出其目录范围这样即使用户上传了恶意木马文件那么也只能控制这一个目录避免了进一步的损失。
* 纵深防御
纵深防御的设计原则,指的是从不同的维度去实施安全保护措施,从而缓解被攻击的风险。纵深防御并不是同一个安全方案要做两遍或多遍,而是要从不同的层面、不同的角度对系统做出整体的解决方案。
这里我给你举一个电商网站的例子(摘自:《[代码未写,漏洞已出—架构和设计的安全](http://shimo.im/docs/oNJvDIXQw3EgQGyn)》)。
> 国内中小电商一半以上在早年都犯过这个错误现在基本都修复了。电商的交易和支付系统之间流程是这样一个人过来说老板我要买一台冰箱多少钱两千。OK你把钱付给支付系统。因为支付请求也是在用户侧的浏览器里提交的。这个过程对用户是不可见的但攻击者实际上可以修改这个数据。攻击者可以修改浏览器提交的数据本来交易系统让他提交2000元攻击者改为提交1元然后支付系统就返回OK说我收到钱了。这个OK到交易系统那里交易系统一看支付成功了那就安排发货1元钱就把冰箱买到了。
你看,单独从支付系统和交易系统来看,设计上都没有问题,都对数据输入做了校验,但问题是没有站在一个系统的整体角度去考虑,没有考虑到不仅要校验交易有没有成功,还要校验交易的金额是不是匹配。
当然解决方案其实也很简单:
> 不要只反馈是否OK同时也把支付的金额和OK一起返回过去。是支付2000元OK还是1元OK。这就解决了问题现在的电商都改成这个设计了。
通过这样一些好的安全设计原则,就可以在设计阶段把很多安全问题预防好。
**开发阶段**
只是设计阶段做好安全相关的设计还不好,很多安全问题其实都是编码阶段时,没有好的编码习惯、没有良好的安全意识导致的。
对于开发阶段的安全问题预防,需要从以下几个方面入手。
* 编码规范中加入安全相关内容
对于用户输入的数据,需要有校验,防止恶意输入;对于涉及权限的操作,要检查用户权限;对于敏感数据要进行加密处理;对于用户的操作,要有日志记录;不能在日志中记录敏感信息等等。
* 要有代码审查
代码审查其实在我们专栏提过很多次,这也是预防安全问题一个行之有效的手段,通过代码审查,及时发现代码中的安全问题。
* 增加安全相关的自动化测试
现在有些安全工具可以帮助对代码做安全检查甚至可以和CI集成如果增加相应的自动化安全测试代码也可以第一时间对代码中的安全问题进行反馈。
## 测试阶段
在测试阶段,除了功能测试以外,增加对安全性方面的测试。除了增加相应的测试用例,也可以借助一些安全测试工具(可参考上一篇“[软件测试工具](http://time.geekbang.org/column/article/95533)”这篇文章)来进行测试。
## 上线维护
上线部署时不部署源代码只对编译后程序部署删除Debug文件。
对服务器进行安全设置,比如说严格限制端口,只保留必须的端口;只对少数服务器开发外放服务;开启操作日志;对访问目录设置最小的权限。
通过对整个软件生命周期都做好安全方面的考虑,并落实到位,才能真正保证好软件的安全。
## 如果真的出现安全问题怎么办?
是不是我们在整个软件生命周期都做好安全方面的考虑,也落实到位,就完全不会有安全问题了?
安全问题就像程序的Bug一样没有谁能保证绝对的安全就像风险管理的最后一步风险监控那样我们必须做好Plan B万一出现安全问题马上应对将损失降到最低。
首先,要设立应急的流程。当出现安全问题了,根据流程,知道该找谁,应该怎么去第一时间恢复生产,避免进一步损失。
其次,要分析程序的漏洞在哪里。通过分析日志,找出漏洞在哪里,才能针对性去修补漏洞。
最后,要总结原因。从错误中吸取教训,看问题是在哪个环节导致的,必要的话,就改进开发流程,避免类似的安全问题再次发生。
## 总结
今天我带你一起学习了如何构建安全的软件,软件的安全问题本质也是一种技术风险,可以借用风险管理的知识来帮助构建高安全性的软件。
软件安全问题主要来源是:恶意输入、假冒身份和数据泄露,要注意对输入数据的校验、对用户身份的校验和对敏感数据的加密。
构建高安全性软件,最好的方式就是在整个生命周期中都做到重视安全问题,各个阶段都考虑到安全方面的问题,才能真正做到防患于未然,构建出安全的软件。
安全问题就像程序的Bug一样不能绝对避免同时也要做好应对措施在出现安全问题后将损失降到最低并且避免以后发生类似问题。
## 课后思考
通过对这篇文章的学习,你觉得你所在项目中,在安全方面有哪些可以改进的地方?你有哪些提升安全性的经验可以和大家一起分享的?欢迎在留言区与我分享讨论。
感谢阅读,如果你觉得这篇文章对你有一些启发,也欢迎把它分享给你的朋友。