软件开发最佳实践:代码安全我们需要打什么疫苗
疫苗的发现可谓是人类发展史上一件具有里程碑意义的事件。控制传染性疾病最主要的手段就是预防,而接种疫苗被认为是最行之有效的措施。疫苗相当于把疾病的治疗一直往前移动到疾病未发生之前,使得疾病的治疗的成本降到最低。或者将来还可以直接基因编辑。然而疫苗有几个小问题:
- 个体接种效果不可知。目前除了乙肝,国内还没有医院或者机构可以检测任何一种疫苗抗体的生成,所以我们上幼儿园依赖于你的疫苗接种记录而不是抽血化验结果。
- 疫苗生产过程管理技术上还未突破,原料、佐剂或者生产工艺的变更都可能造成免疫效果偏差。不管你是改进工艺变更流程都需要报批,即使你可能是使用了新的方法或者添加了我国尚未批准的某些更有效的成分。
在目前这种大环境下,我认为疫苗该打还是继续打,而且推荐打,只是打的是什么,需要斟酌。
关注完我们的下一代后,让我们来看看我们产生的代码的下一代需要打哪些疫苗。
下周我要分享代码审计方面的内容,在挖完OWASP金矿以后,也整理了一些软件开发的最佳实践,如果是全新开发的软件,建议可套用,也欢迎给我反馈。Bug只有在Code review阶段修复成本才最低,越往后越高,安全也一样,只有在软件开发的最开始安全就介入才能降低成本提高收益。如果软件都开发完了再来说你帮我看看安全,测测Bug,滚。
关键的安全的点就一些,以下有些是围绕部署交付的,或者是一些最佳实践或流程可让你更顺利实施安全,如果你不在这些地方投入,你肯定也可以做得安全,只是会更累而已。
- 使用框架开始,底层MVC框架,CSS框架,JavaScript框架。所有尽可能松散耦合,分离。
- 使用一个重的框架比轻量级的框架好,否则你会发现最后还是要处理注入、CSRF、Session、Log、队列、错误控制等等。
- 部署全部使用自动化部署,比如Ansible、Puppet、Chef、Salt等等。
- 使用Vagrant+VirtualBox隔离开发环境。把编程环境搭建过程写到README.md。如果你发现别人没有给你提交PR,可能不是因为他懒,或者他没有能力,而是你自己的环境配置太复杂,别人无法搭建起跟你一模一样的环境来工作并为你更改代码。
- 提供完善的Mock机制。通过环境变量控制解耦第三方依赖。
- 发布使用Docker或者不可变服务器。
- 构建和测试自动化。
- 经常性更新所有软件,包括操作系统、框架、使用到的vendor软件包。更新可以固化在某个大版本内,小版本持续更新。
- 定期大版本更新,阅读changelog,找到breaking change、deprecated,找到某个实现更好的方法。只有经常性更新,你才可以把变化和变化导致的故障分散在较长的一段时间,否则一个CVE漏洞出来紧急修复会让你措手不及。
- vendor包使用Yarn、Bower、npm、pipevn等等实现管理和更新。
- 依赖组件要少,选一个大的而不是选很多小的,选择那些开发活跃的组件。
- 使用Ticket管理feature和bug。
- 阅读所有的文档,检查所有的配置,参照,对每个配置的更改记录下来并且注释好。杜绝组件自带的超级用户、测试代码。生产环境的Debug需要关闭,可以使用自动化部署来保证。
- 使用Git进行源代码版本控制,环境变量,密码等不进入Git,提供模板文件用来拷贝到测试和正式环境。
- Git使用master、dev分支,利用好patch、feature分支。
- 部署代码和程序源代码可以分成2个Git。
- 数据库必须使用Migrate机制,让数据库和程序一样有版本,并且跟程序的版本绑定。
- 整个团队遵守一个统一的代码规范。比如Python的PEP8,PHP的PSR-1。虽然写得很糟糕的代码机器跑起来是没有任何问题,但是你的代码除了给机器看外,还要给未来的你和你的团队其他成员阅读。
- 代码规范变量名和函数名要长。如果你不是写类库,注释尽量少,让业务代码自注释。access_token = wifi_get_access_token_from_clearpass_once_and_full_cached_with_exception_catched(),不用担心,现在的编辑器都可以自动补全的。
- 团队使用一个统一的编辑器,比如Atom,使用editorconfig规范配置。
- 不要个性化,不要定制。遵守标准,不重复发明轮子。比如根目录要有一个README.md,配置使用yaml格式,不更改默认的Web log格式等等。
- 用户填写的内容应当原样保存,不要自作主张为了注入攻击而过滤。
- OWASP Top 10和外带的8个CWE必须熟读。展开来又有18个关注点,当然有些跟上面是重复的。
- 不要信任用户的任何输入。这些数据可能来自表单提交的,URL的ID,上载的,Header里面的,Cookie的。进入函数的第一件事是验证。不要假定函数的调用者帮你处理好了验证。
- 函数内处理好缩进,核心业务代码缩进不要太深。分析是正常进入还是异常进入。比如获取不存在的ID,编辑别人的资源,这些不是从正常浏览可以产生的,直接输出错误。而如果是去访问第三方资源导致网络超时等是可能发生的,应当try/catch给用户友好的提示信息。
- 性能不是一开始考虑的问题,一开始你应当是能实现他,最后再考虑优化,而且优化可以有多个层级,有些不需要在最底层优化。
- 安全一定是贯穿所有的。如果你对自己没有信心,可以聘请其他专家审计。
缩进举例:
# BAD
if request.user.is_staff:
if is_this_article_exists(Article, pk=id):
if this_article_is_him(id, request.user):
this_is_the_most_()
concerned_content_()
update_article_info()
return response
else:
raise PermissionDenied()
else:
raise Exception()
else:
raise PermissionDenied()
# GOOD
if not request.user.is_staff:
raise PermissionDenied()
if not is_this_article_exists(Article, pk=id):
raise Exception()
if not this_article_is_him(id, request.user):
raise PermissionDenied()
this_is_the_most_()
concerned_content_()
update_article_info()
return response