在软件开发初期处理安全需求是防止安全问题经济的方式。大多数安全需求都属于非功能性需求(Non-Functional Requirements ,NFRs)。很多从业者发现,在敏捷项目中处理安全和其他NFR非常具有挑战性。原因有二:
  匹配NFR和特性驱动的用户故事需要付出很大努力;
  安全控制常因缺少可见度而被忽视。敏捷过程容易让团队不自觉地侧重于那些可以直观改善客户体验 的新功能开发或缺陷修复。
  在本文中,我们会探讨以上两个问题。
  在用户故事中处理NFR
  敏捷专家们提出过一些方法,用以定义用户故事驱动的开发过程中的NFR。Mike Cohn在他的文章中讨论了评论者们提出的一些有趣的建议。Scott Ambler该主题写了一系列文章,旨在证明并非所有的NFR都可以独立存在于用户故事中。而Dean Leffingwell可以说在这方面做了深入的研究,他在自己的著作《敏捷软件需求》中花了整整一章探讨该主题。这些专家中大部分人都认为,NFR大致可以分成两类:
  非功能性用户故事:以用户故事的形式展现的可测试功能块。这些用户故事中的参与者(Actor)可能是内部IT人员。例如:“作为安全分析师,我希望系统能抑制不成功的身份认证尝试,这样应用程序在面对暴力破解时不至于太过脆弱”。
  约束:这是个跨领域的问题,常常涉及多个用户故事。我们可以将它看作是对所有相关开发工作收取“课税”。例如:开发Web应用时,要求所有开发者对Http表单中的字段进行数据校验即为一种约束。
  处理第一类NFR很简单:创建一系列的NFR用户故事,将它们加入某种工作序列,例如Product Backlog。
  NFR约束的挑战:
  然而处理第二类NFR则困难很多。敏捷专家们对此提出过一些解决办法,包括:
  在特定的用户故事中添加恰当的约束作为验收标准
  在某个中央知识库——例如,贴在墙上或者发表在wiki上的一份文件——中维护包含所有约束的列表。
  但无论哪个方法都无法解决的问题是,如何针对特定的用户故事选择适用的约束。而且,使用SD Elements进行安全需求匹配的经验表明,要将这些技术限定在一定范围之内很困难。下面这个列表是标准Web应用需要面对的安全约束中的一部分,我们以此为例:
  在SQL语句中以变量绑定的方式阻止SQL注入
  对来自客户端的只读数据进行完整性验证,以防止参数篡改
  避开存在于HTML、HTML属性、CSS以及JavaScript中的不可信数据,从而防止跨站点脚本攻击)(XSS)
  避免客户端JavaScript中基于DOM的XSS
  使用安全的算法以避免整型溢出
  不接受外部重定向以防止自由重定向攻击
  授权给受保护的页面,以防止权限提升攻击)
  使用防止跨站请求伪造(CSRF)的令牌
  验证输入
  尽量使用正则表达式,因为它对于拒绝服务攻击有较强的抵抗力。
  为高价值的事务实现事务身份认证
  不要硬编码密码
  该列表列举的可能只是程序员在实现用户故事时需要考虑的安全约束中的一小部分。若将需要遵循的法规标准——诸如支付卡行业数据安全标准(PCI DSS)之类——考虑在内,约束还要增加不少。如果你开始添加其他NFR约束,例如可访问性,约束列表会快速增长,转眼间会超过程序员的承受范围。一旦列表变得臃肿,我们的经验是,程序员往往会将它全部忽略,仅根据自己的记忆来应用NFR约束。而在很多不断专业化的领域——例如应用安全——NFR的数量不断增长,这给程序员的记忆力造成了沉重的认知负担。因此,如何使用静态分析技术侦测代码中违背NFR约束之处成了关注重点。研究表明,静态分析可以侦测出的可预防缺陷多不超过总数的40%,也是说仍有大量的约束漏网,包括特定领域的安全控制,更别说在缺乏定制的情况下辨别静态分析产生的“假阳性”结果也不是那么简单。
  应对NFR约束的挑战
  为了解决NFR约束过多的问题,我们需要做到以下四点:
  按优先级排序:与用户故事和缺陷一样,NFR约束也应拥有不同的优先级,例如:相较于避开HTML中的不可信数据以防止持续的跨站脚本攻击,对HTTP响应头中的不可信数据进行编码或校验以防止HTTP响应拆分攻击不那么重要。因此我们假定程序员通常没有足够的时间处理所有约束,然后我们提供一种机制为如何定义约束的相对优先级提供建议。之所以说“建议”,是因为优先级有可能随着具体情况的变化而变化,程序员需要自己作出判断——对于他们的特定代码,哪种相对优先级是适用的。你可以选用简单的优先级设置,如“高、中、低”,也可以使用数字范围,如1-10。
  过滤:使用简单的标准常常可以为特定的用户故事移除大量的NFR约束。举例来说,假如你正在实现的用户故事与表现层无关,那么可以安全地忽略大量表现层相关的约束。使用标签系统或者简单的Excel过滤器,可以为特定的用户故事削减约束数量。以下是一些Web应用相关的安全性约束过滤器的范例:
  用户故事是否涉及用户输入;
  用户故事是否涉及保密数据,如密码、信用卡信息以及非公开的财务数据;
  用户故事是否返回一个全新或修正过的用户输出,例如一个网页;
  用户故事是否与数据库进行交互;
  用户故事是否会暴露或使用来自RESTful 或SOAP API调用的数据;
  用户故事是否会访问受保护的数据(例如,不是所有用户都能查看或修改的数据)。
  情境(Context):程序员在写代码或定义任务单(tickets)/ 用户故事的时候更容易记起并应用约束。理想情况下,如果把约束列表内嵌入IDE或任务单系统,那么程序员可以在编码时获取相关的约束。既然现在很多IDE都支持内嵌的Web浏览器,那么使用轻量级的网站或者SharePoint站点可以实现这个目标。
  框架:框架可以大幅减少约束产生的“税收”。例如,像 Django这样的框架自带了CSRF防护,这意味着程序员可以安全地忽略该约束,除非他们主动绕过这个内建的功能。根据我们的经验,多数大型的应用软件都会采用某种形式的定制框架,这些框架可以无缝处理高优先级的约束。虽然这样的框架定制可能需要较高的先期投入,但如果你将约束看成一种基于用户故事的“税收”,那么减少约束数量相当于性的降低了“税率”。