《web安全开发指南》笔记

制订安全计划

安全的焦点在于数据,除了数据,公司的任何部分都可以被取代。

数据的操作:创建Create读取Read更新Update删除Delete

常见Web应用威胁

缓冲区溢出

攻击者试图将足够多的数据发送到缓冲区,以便让应用程序或输出缓冲区溢出,从而导致再缓冲区外的内存数据被损坏。

解决:对应用程序处理的所有数据都执行边界和大小检查,无论是输入还是输出。

代码注入

一个实体从中间人攻击(main-in-the-middle-attack)的形式将代码添加到服务器和客户端之间的数据流中。

解决:确保使用了加密的数据流,HTTPS协议和代码验证(如果可能),提供客户端反馈机制也是一个好主意。

跨站脚本(cross-site scripting,XSS)

攻击者将js脚本或其他可执行代码注入到应用程序的输出流中。攻击者想尽一切办法将你的脚本内容在目标网站中目标用户的浏览器上解析执行。

XSS有三类:

  • 反射型XSS(也叫非持久型XSS):发出请求时,XSS代码出现在URL中,作为输入提交到服务端,服务端解析后响应,在响应内容中出现这段XSS代码,最后浏览器解析执行。这个过程就像一次反射,故称为反射型XSS。
  • 存储型XSS(页脚持久型XSS);与反射型XSS的区别仅在于提交的XSS代码是否会存储在服务端。存储型XSS会将提交的XSS代码存储在服务端(不管是数据库、内存还是文件系统等),是最隐蔽的。
  • DOM XSS:它与以上两者的区别在于DOM XSS的XSS代码不需要服务器解析响应的直接参与,触发XSS靠的是浏览器端的DOM解析。如
    1
    2
    3
    4
    5
    6
    <!-- url: http://example.com/#alert(1) -->
    ...
    <script>
    eval(location.hash.substr(1));
    </script>
    ...

DOM XSS常见的输入点:

1
2
3
4
5
6
7
8
9
document.URL
document.URLUnencoded
document.location
document.referrer
window.location
window.name
xhr请求回来的数据
document.cookie
表单项的值

常见的输出点:
直接输出HTML内容,如:

1
2
3
document.write(...)
document.writeln(...)
document.body.innerHtml = ...

直接修改DOM树,包括事件。如:

1
2
3
4
5
6
document.forms[0].action = ...(以及其他集合,如:一些对象的src/href属性等)
document.attachEvent(...)
document.create(...)
document.execCommand(...)
document.body, ...
window.attachEvent(...)

替换document URL。如:

1
2
3
4
5
6
document.location = ...
document.location.hostname = ...
document.location.replace(...)
document.location.assign(...)
document.URL = ...
window.navigate(...)

打开或修改新窗口。如:

1
2
3
document.open(...)
window.open(...)
window.location.href = ...

直接执行脚本。如:

1
2
3
4
eval(...)
window.execScript(...)
window.setInterval(...)
window.setTimeout(...)

漏洞挖掘:

  • 页面URL或接口URL是否有query(?)标志。
  • 不可控数据输入进行标签替换
1
2
3
4
5
6
7
8
9
function HtmlEncode(str) {
let s = '';
if (!str) return '';
s = str.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\"/g, '&quot;')
return s;
}
  • XSS比较难挖掘,需要具体业务进行可能性分析
XSS危害
  • 挂马
  • 盗取用户cookie
  • Dos(拒绝服务)客户端浏览器
  • 钓鱼攻击,高级钓鱼技巧
  • 编写针对性的XSS病毒,删除目标文章、恶意篡改数据、嫁祸、”借刀杀人“
  • 劫持用户Web行为,甚至进一步渗透内网
  • 爆发web2.0蠕虫
  • 蠕虫式的DDoS攻击
  • 蠕虫式挂马攻击、刷广告、刷流量、破坏网上数据。。。

解决:严格验证,审核机制。

CSRF(Cross Site Request Forgery),跨站请求伪造。

它的请求有两个关键点:跨站点的请求,请求时伪造的。伪造的定义很模糊,一般情况下我们可以认为:如果请求的发出不是用户的意愿,那么这个请求就是伪造的。

按照攻击方式,CSRF可以分为

  • HTML CSRF攻击:HTML元素发出的,最普通
    html:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <link href="">
    <img src="">
    <img lowsrc="">
    <img dynsrc="">
    <meta http-equiv="refresh" content="0; url=">
    <iframe src="">
    <frame src="">
    <script src="">
    <bgsound src="">
    <embed src="">
    <video src="">
    <audio src="">
    <a href="">
    <table background="">

css:

1
2
@import ''
background: url('')

当然也包括动态标签生成

  • JSON HiJacking攻击
    接口返回数据通常为json格式,当返回格式通过eval('(' + JSON_DATA + ')')时,就可能
  • Flash CSRF攻击

漏洞挖掘:

  • 目标表单是否有有效的token随机串
  • 目标表单是否有验证码
  • 目标是否判断力Referer来源
  • 网站根目录下crossdomain.xml的”allow-access-from domain”是否是通配符
  • 目标JSON数据似乎可以自定义callbak函数等。

CSRF危害

  • 篡改目标网站上的用户数据
  • 盗取用户隐私数据
  • 作为其他攻击向量的辅助攻击手法
  • 传播CSRF蠕虫

界面操作劫持

它是一种基于视觉欺骗的Web绘画劫持攻击。他通过在网页的可见输入控件上覆盖一个不可见的框(iframe),使得用户误以为在操作可见控件,而实际上用户的操作行为被其不可见的框所劫持。执行不可见框中的恶意劫持代码,从而完成在用户不知情的情况下窃取敏感信息、篡改数据等攻击。

从技术发展阶段上分析,可以分为以下三种:

  • 点击劫持(Clickjacking):劫持用户的鼠标点击操作。主要的劫持目标是有重要会话交互的页面。
  • 拖放劫持(Drag&Drop jacking):拖放操作不受”同源策略“限制,用户可以把一个域的内容拖放到另一个不同的域。因此突破同源限制的拖放劫持可以演化出更广泛的攻击形式,突破很多种防御。
  • 触屏劫持(Tapjacking):移动端类似点击接触的攻击模式。

点击/触屏劫持原理:透明层+iframe
拖放劫持原理:dataTransfer对象

漏洞挖掘:

  • 目标的HTTP响应头是否设置好了X-Frame-Options字段
  • 目标是否有javascript的Frame Busting机制
  • 更简单的就是用iframe嵌入目标网站试试。若成功,则说明漏洞存在。

文件上传

解决:限制允许上传的文件类型,扫描文件并检查,比如头部签名信息。

硬编码的认证

出于测试目的,开发人员经常将认证信息放在应用程序的初始化文件中。

解决:我们需要去掉这些硬编码的认证信息,以集中式存储的安全信息替代。

发现隐藏或受限制的文件/目录

当应用程序允许输入一些特殊字符(如斜杠或反斜杠)时,黑客就有可能发现隐藏或受限制的文件和目录。

解决:禁止使用特殊字符,把重要的文件放在web根目录之外且操作系统能直接控制的地方。

缺少认证或认证不正确

解决:应该避免因为任何原因而使用访客账户,并为每一个用户分配独立的账户。

缺少授权或授权不正确

解决:只为用户提供执行特定任务时所需要的权限

缺少加密或加密不正确

解决:尽可能用最长的密钥来加大破解难度

操作系统命令注入

攻击者会修改应用程序使用的操作系统命令来执行特定的任务。

解决:确保应用程序再一个沙盒环境中运行。

参数篡改

URL参数,请求头的参数篡改。

解决:确保加密了在浏览器和服务器之间传递的任何参数。传递时使用安全的网络传输协议,如HTTPS。

远程代码包含

很多web应用程序依赖包含外部库,框架和API。大多数情况下,包含语句会含有一个相对路径或使用一个记录着硬编码路径的变量,当黑客能够获取到这些路径信息并更改它们的时候,远程代码包含可能指向黑客想奥包含的代码。

解决:尽量使用硬编码的完整路径。

会话劫持

每次用户登录到你的web服务器,服务器就会为其创建一个唯一的会话。会话劫持者会侵入到会话中并拦截用户与服务器之间传输的数据。含有劫持会话所需信息的三个最常见的地方是:cookie,URL重写与隐藏域。黑客会在这些地方查找会话信息。

解决:会话加密,HTTPS协议。

SQL注入

攻击者会修改由应用程序创建的查询,而这个查询是由用户或其他输入引起的,大部分SQL注入的情况是应用程序需要的是用于查询的数据,但它实际接收到的却是一些SQL片段。其他形式的SQL注入攻击与使用了转义字符或其他意想不到的字符或字符串有关。

解决:避免动态查询。

软件安全保障

软件安全保障(software security assurance,SSA)的观点是,软件需要某种监管来确保它不会导致其使用,控制和保护的数据及资源发生丢失,误差,篡改,不可用或误用。

OSSAP

为了使SSA在Web 应用程序中成为现实,有一个重要的网站需要关注,那就是开放Web应用安全项目(Open Web Application Security Project,OWASP),中国版:http://www.owasp.org.cn/

在应用程序中将SSA作为软件开发生命周期一部分的根本原因,是想尽可能确保软件是可靠和无差错的。

要将实现 SSA 作为应用程序的一部分,第一步需要先定义 SSA 的要求。可以将 SSA 分解为以下 8 个步骤。

  • (1) 评估软件和制订修复计划。
  • (2) 定义数据的各种安全风险并进行分类,优先修复最严重的风险。
  • (3) 执行全面的代码检查。
  • (4) 进行必要的更改。
  • (5) 测试修复并在生产环境中验证它们确实是有效的。
  • (6) 制定防御机制来保护应用程序的访问及其管理的数据。
  • (7) 衡量你所做的这些更改的有效性。
  • (8) 以适当的方式培训管理者、用户和开发人员,以确保良好的应用安全性。

其中对数据和资源分类:这一过程包括识别应用程序涉及的各种数据,包括它自身的代码和配置信息。一旦识别了所有数据,你就要对其进行分类来确定所需的安全级别,从而保护这些数据。

完成数据分类之后,就可以开始用这些分类信息来执行各种任务。例如,你可以考虑通过以下方式减少漏洞:

  • 制定编码规范
  • 执行强制的开发人员培训
  • 在开发团队中聘请安全主管
  • 使用专门定位安全问题的自动化测试程序

进行必要的分析:准确地知道代码可能包含怎样的缺陷是很重要的。要理解数据安全不仅与代码有关,还涉及创建代码所需的工具和使用这些工具的开发人员的技能。

  • 逻辑:分析的目的是要找出过程逻辑中的安全漏洞。例如,应用程序可能会允许用户保持其登录状态,却没有对过期后的登录状态有效性进行检测。这里的问题是,在线用户可能根本就不在线,而是其他人利用了他的凭据登录了系统,并且没人知道这件事,因为所有人都觉得这个用户是像平时一样正常登录该系统。
  • 数据:关键是要考虑如何将数据合
    并,并添加安全防护措施,从而让数据源更难被发现。考虑以下这些问题:
    • 谁能访问数据
    • 数据以什么格式存储
    • 数据何时可被访问
    • 数据存储在什么地方
    • 为什么每个数据项都能作为应用程序的一部分被使用
    • 数据如何分解成各个部分,以及组合数据供应用程序使用的后果
  • 界面:现今软件的一大问题是包含了不必要的功能。一个应用程序应该满足一组特定的目标,或者执行一组特定的任务。总有些人在想,如果软件能有一些额外的特性,可能会更好,虽然这些特性与其要达成的核心目标完全无关。功能膨胀这个词由来已久,你经常在涉及金钱消耗的场景中发现它,比如应用程序性能问题的根源、用户培训费用的提升,以及开发进度的延迟等。但是,受功能膨胀的影响,应用程序界面的问题会因攻击面(attack
    surface)的增加而对安全性产生重大影响。另一个潜在的威胁是提示界面,它提供给潜在的黑客太多信息或功能,从而丢失了应用程序的安全性。虽然提供一种途径让用户找回密码是有必要的,但有些实现方式确实有可能让黑客得到用户的密码从而伪装成该用户。黑客甚至可能会修改用户的密码,使得真正的用户无法登录。
  • 约束:约束就是一种方法,在允许执行某一行为之前,用来确保其满足特定的标准。例如,除非用户拥有相关权限,否则禁止其访问数据元素就是一种约束。但是,约束有其他更重要的形式。最重要的约束是确定用户如何管理数据。大部分用户只需要读取数据,但应用程序通常提供了读 / 写访问,从而打开了一个巨大的安全缺口。对于数据也需要考虑约束。当处理数据时,你必须精确定义用什么来保证数据的唯一性,
    并确保应用程序不会破坏任何与唯一性有关的规则。有了这个想法,你一般需要考虑下面
    几种约束:
    • 确保数据有正确的类型
    • 定义数据能接受的值的范围
    • 明确数据长度的最大值和最小值
    • 列出所有不能接受的数据值

HTML/CSS/JavaScript的关键问题

定义HTML的关键问题

  • 代码注入:HTML5 里有大量黑客可用来注入恶意代码的方法,包括你通常不认为可疑的数据源。
  • 用户跟踪:因为大多数情况下应用程序使用了不同来源的代码,所以你可能会发现库、API 或微服务实际上执行了一些类型的用户跟踪,而黑客能利用其来获取公司信息。你给黑客的每一份信息都会让攻克系统安全变得更容易。
  • 污染输入:除非你提供自己的输入项检查,否则 HTML5会放行所有用户想要提供的输入。你可能只需要一个数字型的值,但用户可能输入一段脚本代码。尝试对输入进行彻底检测以确保真正得到你要求的输入数据,这在客户端近乎是不可能的,所以你还需要确保有强大的服务器端检查机制。

定义CSS的关键问题

  • 设计主导:CSS3 代码导致安全问题的一个主要原因是由设计来主导一切。问题在于CSS 的样式级联部分不允许 CSS3 知道除了其父节点之外的任何信息。因此,黑客能创建出一种声称要做某件事的界面,实际上却做着另外的事情。
  • 上传的 CSS:在某些情况下,应用程序的设计者会允许用户上传一份 CSS 文件来获得某一特定的应用外观或让其在一个特定的平台上运行得更加良好。但是,上传的 CSS 也可能包含一些代码,使得黑客更容易破坏设在各个地方的安全措施,或者在可见范围内隐藏脏东西。例如,黑客会在 CSS 里隐藏 URL,从而将应用重定向到不安全的服务器上。
  • CSS着色器:一种特殊的CSS用法,它允许访问浏览器数据和跨域数据,从而会产生一些极端的问题。重要的是要明白,有时候在屏幕上展现数据的行为会导致你最初未考虑到的潜在安全漏洞。

定义JavaScript的关键问题

  • 跨站脚本(XSS):只要你在沙盒环境之外运行JavaScript,黑客就有可能对你的应用程序做一些讨厌的动作。
  • 跨站请求伪造(CSRF):一段脚本能够利用保存在 cookie里的用户凭据来访问其他站点。在这些网站上,黑客能执行应用程序设计目的之外的各种任务。例如,黑客能以用户的名义,进行账户篡改、窃取数据、欺诈以及许多其他的违法活动。
  • 浏览器及其插件的漏洞:很多黑客会利用已知的浏览器及浏览器插件的漏洞来强迫应用程序执行其不应执行的任务。例如,用户的系统可能突然变成了向其他系统传送病毒代码的傀儡。黑客能够破坏的程度受限于相关的漏洞。一般来说,你要确保自己安装所有的更新,并要了解这些安全漏洞会如何影响应用程序的作业

考虑端点的防御要素

端点是指网络传输的目的地,比如一个服务或一个浏览器。当数据包到达端点时,其包含的数据会被解包出来并提供给应用程序做进一步的处理。端点安全是至关重要的,因为端点是网络的一个主要入口点。除非端点是安全的,否则网络会接收到恶意的数据。此外,糟糕的端点安全性会对网络里的其他节点造成损害。接下来会讨论端点安全的三个阶段:预防检测修复

预防安全漏洞

避免安全陷阱的第一步是一开始就承认陷阱是存在的。

你构建的所有应用程序必须足够可靠以实现以下功能:

  • 对抗普通的攻击
  • 在安全措施失效时发出警报
  • 避免对可能会发生漏洞的地方做出假设
  • 要假设即使是受过训练的用户也会犯导致安全事故的错误

检测安全漏洞

大部分的开发团队需要以下这些领域的专家:

  • 网络
  • 数据库管理
  • 应用设计与开发
  • 移动技术
  • 网络取证
  • 法规遵从

每个应用程序都需要一个团队,这个团队能够定期讨论与应用程序相关的安全需求和安全威胁。

修复受损的软件

安全负责人可能让开发团队帮忙定位攻击者。安全信息与事件管理(security information
and event management,SIEM)软件能帮忙查阅指向问题根源的日志。

修复过程应该包含对应用程序各部件的更新和补丁进行检查的策略。为了达成这一目标,必须维护良好的应用文档。当然,为了确保安全问题不再发生,开发团队需要对所有应用程序需要的更新进行测试。最后,你需要确保数据在整个过程中始终保持安全,并执行应用程序所需的所有数据恢复。

处理云存储

在一个员工要求随时随地能用身边的任何一种设备访问数据的世界里,云存储是一个必然的灾难。
大部分云存储的主要问题在于其天生是公开的。例如,商业版的Dropbox听起来非常好,并且确实提供了更多的安全特性,但该服务仍然是公开的。公司不能在自己的私有云上使用该服务。
对于如何将云存储作为 Web 应用程序策略的一部分,有下面几个方案可供选择。

  • 阻止访问:通过使用防火墙,指定策略或应用程序的特性,确实可能阻止所有对云存储的访问。但是,想要阻止无处不在的想要访问云存储的用户去访问是很难的,并且,用户访问的意愿是相当坚决的。此外,阻止访问会对业务需求产生负面作用。例如,合作伙伴可能会选择云存储来作为交换大文件的方法。阻止策略还会招致用户的愤怒,进而使他们不再使用你的应用程序,或者想办法规避你提供的这个特性。当你的公司必须管理大量敏感数据,有保护数据的法律要求,或者根本不需要用到云存储的灵活性时,阻止访问是最好的选择。
  • 允许不受控的访问:你可以选择无视云存储使用过程中涉及的相关问题。但是,这样的策略会让公司面临数据丢失、数据泄露以及各种各样的其他问题。不幸的是,许多公司目前正是使用这样的方法,因为控制用户的访问已经变得非常困难,且这些公司缺乏采用其他方法的手段。
  • 依赖公司授权的安全场景:如果要求用户用公司账户访问云存储,你至少能监控文件的使用并能在职员离职时恢复数据。但是,云存储的基本问题仍然存在。有相关知识的黑客仍然能侵入账户并窃走数据,或者只是以其他方式来窥探你。在你的公司不需要管理法律要求保护的数据,而你也愿意用一些安全性换取方便的时候,这一方案是可行的。
  • 控制应用程序的访问:
    许多云服务支持以特别的方式与服务进行交互的API。虽然这一方法非常耗时,但它确实能让你控制将用户敏感数据保存在哪里,同时用户仍然能够灵活地使用云存储处理不敏感的数据。在公司需要与大量合作伙伴进行交互,还需要管理大量敏感和核心数据时,你应该考虑这一方案。
  • 依赖第三方的解决方案:你能找到一些第三方解决方案,比如 Accellion(http://www.accellion.com/),它提供了
    云存储的连接器。这种供应商提供的服务可以作为应用程序与在线数据存储之间的中介。用户可以无缝地与数据进行交互,但连接服务会根据你设置的策略来控制访问。这种方法的问题在于,在开发应用程序时你有额外的一层需要考虑。此外,你必须信任提供连接服务的第三方。在需要拥有灵活性而又不消耗一般的开发成本,并且不想创建依赖于 API 访问的解决方案时,你可以使用这种解决方案。

使用外部代码和资源

定义库的使用

当使用库时,安全工作的重点是你的应用程序,因为你从主机服务器上下载用于应用程序的代码。库的代码是在进程内执行的,所以你需要明确获取库代码的源头是可信任的。

定义API的使用

每个 API 都有自己的生命周期且依靠不同的方法去处理管理数据等问题。因此,当比较两个 API 时,即使它们都来自同一家机构,你也不能对其中一个作出任何安全性方面的假设。
API 还依赖信息交换。任何信息交换都要求有额外的安全保障,因为你的部分数据始终会落在自己的主机系统上。你需要明白主机应提供适当的安全措施来保护请求中传递的数据。

定义微服务的使用

与 API 类似,微服务也是在主机系统上运行。你发出请求,微服务则响应某种类型的资源(通常是数据)。但是,微服务与 API 差异巨大。微服务的重点放在小型任务上,一个典型的微服务专注于把单一任务执行好。此外,微服务往往很关注平台的独立性,其特点是,提供的服务要能与任何规模、具有任何能力的任何平台进行交互。API 与微服务关注点的不同会极大地影响安全形势。

当使用微服务时,你需要确保其所在的主机是可信赖的,且微服务执行的单个任务是明确定义的。还有一点很重要,要考虑微服务如何与你提供的任何数据进行交互,且不要假设每一个微服务都以相同的方式与数据进行交互(就算这些微服务存在于同一台计算机上)。
微服务的使用意味着效率以及在更广泛平台上工作的能力,但你还需要意识到额外的安全检查的要求。

访问外部数据

外部数据有各种各样的形式。任何形式的外部数据都是可疑的,因为有人会在单纯的数据访问行为中对数据进行篡改。

下面是最常见的作为外部私有数据源的信息存储来源:

  • 存储在你自己公司主机上的数据
  • 存储在合作伙伴计算机上的数据
  • 由在服务器上运行的应用程序计算出的数据
  • 从传感器或公司提供的其他数据源引入的数据

数据仓库可能会存在问题,并且仓库越公开,就越会产生问题。数据仓库的使用方式确实有所区别,但确保你得到的是真正的而不是伪造的数据,这是最重要的。

允许他人访问

确保提供的应用程序的安全性是很重要的。作为资源的提供者,你突然成为多个外部实体的单一故障点。维持环境安全意味着要完成以下事项:

  • 定期检查所有资源以确保它们是可用的
  • 提供有用的资源文档
  • 确保第三方开发者遵守规则(通过在采购语言里加入安全性要求等)
  • 执行必要的安全测试
  • 持续跟进资源存在的潜在威胁
  • 更新主机资源以避免已知的漏洞

一旦通过了将资源提供给第三方使用的初始阶段,你还必须维护这些资源,以便应用程序能继续依靠其运行。此外,想象你在提供资源时面临着某些威胁是很重要的。下面是一些你必须考虑的事项:

  • 黑客会尝试利用你的资源来入侵你的网站
  • 开发人员会滥用资源,并尝试用它执行其设计目的以外的任务
  • 资源会以某种方式受损

迎合用户需求与期望

从用户的视角看待应用程序

考虑自带设备的问题

理解基于Web的应用程序的安全性

为了让移动设备更安全,你需要考虑执行以下这些步骤

  • 让应用程序的所有利益相关者(包括用户、CIO、CISO、人力资源以及开发团队以外的
    其他人)参与到应用程序特性的决策过程中来
  • 以书面形式制定一个移动安全策略。
  • 确保管理层理解为安全措施提供资金支持的需求。
  • 获取正确的工具来创建安全的应用程序。以下是一些通常会用到的与解决该领域问题相关的工具:
    • a. 用户或系统认证
    • b. 数据加密
    • c. 移动设备管理
    • d. 通用的反病毒保护
    • e. 虚拟私有网络(virtual private network,VPN)支持(需要时)
    • f. 防止数据丢失
    • g. 主机入侵防御
  • 与有强大安全专业技能的公司建立合作关系(如果有必要)。
  • 开始开发工作。只有在为应用程序创建了强大的支持系统之后,你才能开始开发工作。

考虑原生应用的问题

对基于Web的应用程序相关的问题的直接反应就是用原生应用程序替代。
如果你真的想用原生应用程序来确保更好的安全性,就需要为每一个想支持的平台开发一个原生应用程序,这会变成一个巨大的工程。对大多数公司来说,从增强安全性和提升应用程序控制的角度出发,花费时间开发所需的这些应用程序是不值得的。

使用定制化浏览器

验证代码兼容性问题

处理几乎连续的设备更新

设计密码的可选方案

用户不想要输入密码来访问或使用应用程序的某些功能。
遗憾的是,你仍然需要一些方法来确定用户的身份。接下来描述了一些成熟的方案(你如今可以实现的),这有助于你找到最合适的解决方案。

使用口令

口令是一种部分可读的语句。你可以组合字母、数字以及特殊字符,让密码更复杂。例如,你可以创建一个I love flowers这样的口令。这一口令包含了大写和小写字母、数字以及特殊字符。它能对抗字典和其他常用的暴力攻击。它可能会比多数用户采用的密码长。但是,其仍然很好记。用户不需要把口令写下来。

今天的用户确实有理由抱怨他们需要记住的密码的长度。一种可以考虑的作为解决方案的技术是密码保险箱。密码保险箱能以一种安全的方式保存大量用户密码,可让用户继续使用复杂的密码。

使用生物识别的方案

  • 指纹
  • 虹膜
  • 声纹

这三种解决方案都有缺陷,黑客有攻克它们的方法,所以你可能要选择另一种生物识别方案或将其中一种与其他认证方式结合使用。相关供应商有一些正在开发中的生物识别替代方案,下面描述了其中的一些。

  • 心跳:一个更有趣的生物识别替代方案是结合带分析算法的心率监视器。
  • 人脸识别
  • 耳形
  • 输入识别技术:每个人都有不同的输入方式。打字的速度、按住键的时间,甚至是输入字母的间隔,都能识别出作为打字者的你。通过输入一段特定的语句并监控你如何输入,应用程序可创建一种双因素认证,这与只是输入密码并没有什么不同。但是,现在黑客窃取到密码后仍然用不了它。

为什么使用双因素认证:大多数单因素认证方案的问题在于它们有一个很容易被利用的弱点。例如,黑客可通过社会工程学的攻击或是暴力来窃取密码或口令。通过使用双因素认证,可以降低认证过程被攻克的风险。当然,有人会说三因素或四因素认证会更好,但那样会导致没有人能登录进自家账号的情况。有很多类型的双因素认证。例如,你可以给用户一个密码和一个令牌。你还可以将密码与生物特征识别方案结合起来。许多银行目前就使用了双因素认证。

依靠钥匙卡

个人电脑也会使用钥匙卡技术。你能用它来控制对整个个人电脑的访问,或者控制对某个应用程序的访问。当用于访问某个应用程序时,开发人员需要开发读取卡信息的代码,检测其有效性,并认证用户。
使用钥匙卡的主要优点是,它能提供复杂的密码及潜在的其他细节来帮助识别用户。为了防范黑客,密码可以设得尽可能复杂。根据所使用的技术,你甚至可以很容易地改变钥匙卡的信息,所以密码的更改操作不会有太高的成本。钥匙卡通常也比较大,用户不会经常弄丢(虽然你可以预见有些用户至少会把一些钥匙卡忘在什么地方)。因为该技术由来已久,所以比起其他安全解决方案,钥匙卡的成本相对比较低。用户也比较倾向于选择钥匙卡(丢失时除外),因为它们比较快且易于使用。
钥匙卡的主要缺点是用户会忘记将其放在哪里或遗忘在家里。丢失的钥匙卡能为黑客提供对公司造成真正损害的入侵机会。即使只是把钥匙卡遗忘在家里,提供一张临时卡也是种额外的支持开销。当然,还有在用户不再需要时回收临时钥匙卡的问题。

依靠USB key

USB key 实质上是一个包含一个或多个密码或令牌的闪存驱动器。你将 USB key 插入电脑并启动,电脑会使用其中的数据登录系统。同一个 key 中可以包含访问多个应用程序的多个密码。应用程序需要识别 key 中的密码,但该技术可以实现在不输入密码或任何其他信息的情况下登录应用程序。

与钥匙卡相比,USB key 有以下显著的优点:

  • 不需要获取新的 USB key 就能更改密码
  • 能包含多个密码
  • 总体成本低于使用钥匙卡
  • 可以升级各类凭据
    当然,USB key 有许多与钥匙卡类似的缺点。例如,用户不管是丢失了钥匙卡还是 USB key,结果都是相同的,该用户不再能登录应用程序而其他人可以。事实上,由于 USB key比钥匙卡小,因此用户会更有可能丢失 USB key,并且泄露的不仅仅是一个密码。

实现令牌策略

你通常会用智能手机来实现一套令牌策略。
在大部分情况下,在双因素认证系统中,应用程序会将令牌作为第二认证方式。第一认证方式仍然是钥匙卡、生物学特征、密码或口令。从理论上说,如果愿意,你也可以将令牌作为主认证方式。比起其他认证方法,这种方式会带来以下这些你需要解决的问题。

  • 用户需要一直将智能手机带在身边。
  • 丢失手机可能会损害作为登录应用程序方式的令牌系统。
  • 该方案只能用于登录除智能手机外的设备,而如今的用户在可能的情况下都想使用智能
    手机而不是其他电脑设备。
  • 用于用户登录的计算机需要有接收令牌所需的设备。

聚焦用户期望

让应用程序易于使用

让应用程序快速运行

用户的期望是在获取执行某个任务所需的数据时不要有任何等待。并且,数据需要从一开始就是准确的且以用户需要的形式组织。否则,用户就会感到挫败。事实是安全措施会使得应用程序变慢,但你作为开发人员,需要专注于将延迟保持在最低的水平。

创建可靠的环境

客观看待安全性

获取第三方帮助

重新造轮子通常是错误的做法。很有可能其他人已经创建了一个能很好地满足你的需求的安全解决方案,所以你能直接使用它而不需要创建自己的解决方案。

发现第三方安全解决方案

考虑云安全方案

理解数据仓库

处理文件共享问题

  • 有良好文档的 API
  • 安全性
    • 云分级:这些特性中最重要的就是云分级,你可以定义文件如何存储。有一些文件只出现在文件共享服务中,有一些除了云副本外还需要本地副本,还有一些只能出现在本地磁盘上,尽管其能通过文件共享服务访问。
    • 文件类型:有些文件类型可能有特定的存储要求。这些文件可能包含敏感数据,且你可能需要在本地存储它们或者对它们进行加密,尽管基于文件分级的原则它们会以不同的方式出现。
    • 访问频率:你的应用程序可能只会以某一特定频率访问文件。当访问文件的频率不一样时,可能表明出现了安全问题。文件共享服务应该就这一潜在问题向你发出警报。
  • 全局命名空间:
    某些情况下,文件共享服务会提供数据孤岛(data silo)方式的访问,这样财务数据就能完全与人力资源数据隔开。数据孤岛确实能达到这一目标,但有时候数据需要以这样的方式存储:任何有权限的人可在任何地点用任何设备访问数据。要获得这种访问,你
    需要一个全局命名空间。
  • 冗余:为了能安心写代码且不需要应付用户的需求,你需要有一个具备充分冗余的文件共享方案。否则,你不能确保用户需要的数据在任何时刻都能访问。但是,作为数据冗余的一部分,你需要确保数据安全地存储在每个采用单独的虚拟服务器的地方(某些类型的共享文件攻击依赖攻击者入侵目标服务器,而当文件共享服务依赖单独机器上的虚拟服务器时,这种攻击会变得更加困难)。

考虑云存储

  • 归档
  • 设置信息的存储
  • 媒体存储

选择产品类型

使用库

库是存在于单独的文件中的代码,但你需要将它们加载进自己的应用程序中。库会变成你的应用程序的一部分。大部分情况下,你能把库下载到本地服务器,但在其他情况下则不能。将库下载到本地服务器的好处是,其他人看不到你如何使用它来创建应用程序。通过让加载过程更快和更简单,这一做法还能提升应用程序的速度。此外,下载库意味着库代码是稳定的,这使得有可能创建出更可靠的应用程序。

当使用外部库时,你需要考虑原作者的声誉,以及你在应用程序中使用库的方式。

访问API

一些 API 会加入安全的访问,以帮助确保黑客不能轻易利用某些漏洞来控制API,或以作者意想不到的方式使用它。有些 API 需要一个名称与密码的组合来访问服务,这是很好的做法,只要信息是以加密的方式发送的。但是,即使使用了加密,数据也不是真正安全的。作为一个替代方案,API 可使用密钥来提供访问。不幸的是,当密钥暴露时,API 的端点仍然会很乐意地为请求它的人提供信息。总之,API 会在某个时刻出现安全问题,但你可以用
某些方法来减少黑客的活动。在使用 API 时保持警惕并观察黑客的行为也是很重要的。

API 的一个潜在问题是,它们可能会很慢。你的应用程序会变得低效并且有延迟。这是一个很重要的考虑因素,因为用户没有耐心,并且可能在等待的过程中不经意间地破坏你的应用程序。损坏的应用程序通常会表现出安全问题,黑客非常乐于利用这些问题。
当使用 API 的时候,黑客还可能采用中间人攻击的方式访问你的数据。中间人攻击特别难以检测,因为调用显然是正常的,且你不会在稍后收到的数据中发现任何异常。同时,黑客会利用收集到的数据来做某些事情,比如检索客户的信用卡号码或获取关于你的公司的有用信息。当使用 API 时,你必须密切关注数据加密等问题以确保数据安全。

考虑微服务

微服务实际上是构建于已有技术上的一种新技术。微服务是对 Web 服务、API 和库的混用,但是是以小型包的形式。事实上,微服务具有以下这些你需要考虑的特性:

  • 依赖一个小型的、单一目标的、基于服务的应用程序来创建一个具有完整功能的应用程序(每个单一目标的应用程序就是一个微服务)
  • 用最合适的编程语言来完成任务
  • 对于具体的微服务,用最高效的数据管理技术访问应用程序数据
  • 在每个微服务之间开发轻量级的通信
  • 依靠 REST 等协议来通信,所以管道是哑的,但微服务是智能的
  • 采取分散式应用管理,分别监控每个微服务
  • 根据需要选择各种微服务来搭建任意数量的成熟的应用程序(桌面端、移动浏览器、原生移动应用,甚至是 API)

从安全的角度看,微服务实现的安全性会不同于库或 API。每个微服务会基于自身的需求提供独立的安全性,而不是为整体使用一个网站中所有微服务的每个人或事提供单一的解决方案。因此,微服务会提供更好的安全性,但这个安全也是不平衡且更难以处理的。

开发成功的界面

应用程序依赖界面与用户进行交互。当界面有缺陷时,用户对应用的好感会减少,且用户的满意度也会降低。

有些界面决定甚至在编码之前就是错误的。例如,有些校验技术会告诉用户输入不合法但不告诉用户原因。更重要的是,当反馈缺少有用的提示和示例,用户会变得很沮丧。本章还讨论了可用来改善界面的技巧。有些技巧能奏效,有些则不能,但这不是什么问题,重要的是,有些技巧能减少用户的焦虑。

评估UI

创建简洁的界面

一个简洁的界面以用户能理解的方式聚焦于一个问题

使界面灵活

削弱用户输入非法数据的能力,使界面简洁,以及保持事情简单,所有这些都以某种方式限制着用户。它们是有助于保持数据安全,同时让用户与应用程序的交互变得更简单的约束。但是,界面也需要灵活性。用户会在很多设备上使用应用程序,并不是所有的设备都能接受你提供的初始安排。让用户根据需要组织屏幕会给用户以“权力”,同时不会留下任何安全漏洞。只要有可能,一个界面应该让用户能够进行以下操作。

  • 拖曳
  • 调整大小
  • 选择
  • 排序

提供辅助功能

你通常应该提供的一个帮助项是说明文字。将说明文字添加进某一元素的 HTML 标签中。

  • title
  • alt
  • longdesc

用户帮助信息有很多其他形式。大部分网站都缺失的一种帮助是提供一个示例说明:对于文本框和其他将键盘输入数据作为输入的输入框,你想要怎样的输入。

任何输入项还应该提供帮助链接。当用户点击该链接时,应用程序应展示有额外信息的帮助页面。这主要是要尽可能简单地为用户提供正确的信息。你从一开始就阻止的任何错误将会降低用户的挫折感,使数据输入更准确,并且最重要的是,能避免经常导致安全问题的错误。

定义可访问性问题

你可能想知道可访问性与安全有什么关系。当用户对某个界面不理解时,他们就会犯错误。当人们因某一特别的障碍而不能理解某个界面时,他们仍然会犯错误,但是再多的培训也无法解决这一问题。即使用户有积极的动机来正确地使用软件,不达标的可访问性也会阻止他们达到这一目的。错误经常会转换成最坏的安全漏洞。预想不到的输入,即你从没想过会有人提供的输入,经常造成最严重的问题。缺少可访问性的应用程序更容易创建一个糟糕的环境,从而使得破坏安全的错误经常发生。

测试工具有助于验证开发团队在创建每个人都能使用的应用程序方面是否圆满完成了工作。

提供受控制的选择

安全与实施控制有关。应用程序必须控制其与数据交互的方式。如果不破坏数据或造成数据泄露,管理数据就是创建一个安全界面的关键基础。执行控制的最好方式之一就是采用技术手段确保用户的选择仅限于你期望的选项。使用特定的输入控制会限制用户的选择,但也会让用户更有效率,因为用户可以考虑更少的可用选项。以下是一些常见的特定输入控制选择控件。

  • 单选按钮
  • 复选框
  • 列表框
  • 菜单

在创建一个受控制的选择环境时,除了标准的输入类型(包括较老的类型,比如password),你还可以使用特殊的 HTML5 特性。但是,这种情况下的方案是为用户提供一个文本输入框,这意味着这些选项不如你想象的那样安全。以下是 HTML5 专有的控件。

  • color
  • date
  • datetime
  • datetime-local
  • email
  • month
  • number
  • range
  • search
  • tel
  • time
  • url
  • week

选择UI的解决方案级别

实现标准的HTML控件

控制用户选择的方式。其中很多以 HTML 控件形式出现。HTML 控件的优点是它们在大部分地方能自动工作。浏览器必须支持控件要求的 HTML 级别,但这是唯一的要求。

使用CSS控件

创建 CSS 控件的优点是所有东西都是本地生成的,所以它们很好看,但应用程序也能执行得很好。此外,CSS 让你能够创建充满活力的应用,所以使用它们会更加有趣。CSS 的使用还能给用户视觉提示,这意味着你能用它们减少错误,即增强应用程序的安全性。当涉及安全性时,CSS 还有某些限制。为了获得任何级别的校验,你必须使用 HTML5 和
JavaScript 技术。CSS 不能提供任何校验的可能,因为它不是设计用来执行这种任务的。记住,CSS 的设计者表示它就是用于基本的页面格式化的,除此之外没有其他的功能。通
过巧妙的编程,你可以得到 CSS 能提供的一些有趣的效果。

用JavaScript创建控件

当创建一个界面时,你有两个级别的 JavaScript 支持,并且可能在一个应用程序中实现这两种级别的支持。下面描述了客户端和服务器端的控件。

    1. 依靠客户端控件:通常你不会手动创
      建控件,而会使用某个库,如 jQuery 和 jQuery UI。当使用客户端的控件时,应用程序从本地或远程服务器加载库,但只要库已加载,所有控件都会在本地运行。客户端处理的主要好处是你能获得速度优势,并且应用程序在多数情况下更可靠,因为你能稳定访问使应用程序正常运行的各个部分。使用客户端控件的一个重要的安全问题是,你正在将别人的代码融入自己的应用程序中。这意味着你的应用程序和你从来没有看过的代码之间有一个信任关系。
  • 2.依靠服务器端控件:服务器端控件通常使用服务器上的某个 PHP 脚本或其他脚本启动。在许多情况下,你能直接调用脚本,传递其所需要的数据,并在浏览器中看到脚本的输出。这一过程全都在服务器上运行。因为脚本是进程外的,所以更容易保证你的应用程序与第三方代码分离,这使得你的应用程序更安全。但是当使用服务器端控件时,你要处理可靠性和速度方面的问题。这种方案的客户端部分通常要依赖 JavaScript。事实上,通过采用如异步 JavaScript 和XML(Asynchronous JavaScript and XML,AJAX)这样的技术,你可以将来自服务端控件的输出嵌入到页面中并根据需要更新其内容。当使用这种方式时,通过仅将数据发送给服务器并只更新该数据影响到的页面部分,可以提升应用程序的速度。

校验输入

在所有你能做的确保应用程序安全的事项中,最重要的是要假设你接收到的所有数据都是恶意的。当你假设接收到的每个输入都包含病毒或某些将破坏系统的漏洞时,就会开始以新的方式看待输入。是的,这非常偏执,但这个观念对于确保数据安全很重要。用体现恶意输入观点的方式校验数据在今天的应用环境中很重要。即便有这种观点,你也可能发现你还不够偏执(黑客在寻找掩盖恶意数据的新方法方面非常有创造力),并且需要聘请更偏执的人。

只允许特定的输入

  • 类型
  • 范围
  • 正则表达式
  • 特殊字符

查找鬼祟的输入

黑客会持续寻找提供非法输入数据的方法。目的是让应用程序以某些特定的方式崩溃或迫使应用程序以意外的方式作出反应。例如,鬼祟的输入是 SQL 注入攻击的根本原因。黑客利用表单里的输入创建一个让应用程序最终以意外方式运行的条件。这种攻击造成的影响范围从损坏数据到执行不属于数据部分的脚本。

请求新的输入

黑客最喜欢利用的一个漏洞是寻求这样一些情况:应用程序会对第一次传递过来的输入数据进行严格的检查,但在随后的传递中会放松检查。由于许多语言处理循环的方式,这种情况很容易发生。只有仔细的开发者会在第二次及随后的输入重试中避免这种情况。每一次代码的输入都应该依赖相同的代码进行检查和校验输入。无论用户是第一次还是第三次输入数据,检查都应该提供相同的保护。
但是,当你开始质疑用户输入数据的动机时,这个问题有新的含义。例如,你需要考虑为什么用户需要重试 10 次才能在表单里输入正确的名字。用户知道他们的名字,这不是他们可以突然忘记的东西。在应用程序中一直重复尝试输入明显的数据可能意味着某些事情,而不只是遇见了健忘的用户。你需要质疑表单是否足够干净。当你确定表单是干净的且你有恰当的校验时,你需要开始寻找这个问题的其他原因,比如黑客正在尝试不同的方法攻破应用程序。总之,大部分应用程序应该允许一定次数的重试,然后将其关闭并等待
管理员的关注。

使用客户端和服务器端校验

在创建基于 Web 的应用程序时,应用程序的速度是一个首要问题。用户的关注时间在1到5秒内,不足以执行太多的校验。但是,执行校验并获得用户的正确输入是很重要的。为此,采用客户端校验看起来是可行的办法。JavaScript 程序可以快速检测错误的输入,并在数据与服务器之间来回传递之前告诉用户。事实就是,你确实需要客户端校验。

但是客户端校验有它自身的一些问题。数据必须在某些点离开其所在的系统并发送给服务器。在浏览器发送数据及服务器接收数据这段时间,黑客有各种机会拦截到数据并更改它。为了确保数据真的有正确的类型和内容,你需要在服务器端执行二次校验检查。如果无人篡改数据,二次校验将很快通过,且用户将在合理的时间内获得响应。

服务器端的校验检查需要覆盖每一个输入和输出。只在数据从客户端到达时进行检查,然后就假设没人会在服务器上对其进行篡改是不够的。黑客会很积极地攻击服务器,因为对服务器的一次入侵可影响到大量的用户。因此,你需要对服务器上的每个环节的每个输入进行校验,以确保应用程序及其数据的安全。

期待意外

构建可靠的代码

世界上最可靠的应用程序是在执行任务时不会出现任何中断的应用程序。这就要排除对外部处理输入资源的使用,因为这三项都有故障点。这样的应用程序没有用户,且需要在可靠性极高的硬件上运行。但是,即使这些要素都满足,要想创建 100% 可靠的应用程序仍然是不可能的。每个应用程序都有可能出故障,变得不那么可靠。

区分可靠性和安全性

定义可靠性和安全性的角色

可靠性是对应用程序出现故障的频繁程度的度量。这是一个统计学度量。

安全性是对应用程序出现故障时会造成多大影响的度量。不像可靠性,没有统计学方法来衡量安全性。

避免可靠代码中的安全漏洞

软件越可靠,其不安全的风险越高。问题在于可靠性和安全性的不同目标之一。一个可靠的应用程序通常是可访问、可使用和可预期的,这会导致更多的安全问题。以下是可靠性与安全性有不同目标的一些例子。

  • 复杂的密码会让软件更加安全,但这意味着,当用户忘记密码时,软件可能变得不可用,从而使得软件不可靠。
  • 确保没人篡改应用数据的检查会导致当发现篡改时软件变得不可用。
  • 对安全性检查的误判会让应用程序的行为不可预测。
  • 管理安全性设置会增加应用程序界面的复杂度并令其可用性降低。
  • 由于安全限制(如某些政策的规定)拒绝对所需资源的访问会降低应用程序的可访问性并使其变得不可预测。

事实上,有很多安全性与可靠性严重对立的情况。你必须平衡两者,以确保应用数据保持合理的安全,且应用程序能可靠地运行。

在某些情况下,设计者可能不想要这种限制的潜在影响,并移除范围检查以使应用程序更可靠。毕竟,如果软件的目标是防止核反应堆出现危机,但软件却阻止输入能让反应堆免于出现危机的值,那软件的安全性就不再重要了,因为没有人会围绕该问题进行辩论。
处理这种情况的折中解决方案也是存在的,但它会增加软件的复杂度,并因此对可靠性造成更大影响。此外,因为折中解决方案需要增加编码,所以应用程序的速度也会受到影响。但是,在日常操作中包含各种类型的安全性检查并允许管理员重载以便在紧急时刻去除检查,用户就可以输入正确的值来挽救反应堆,但软件平时不会允许这样的输入。
关键是你通常找到的是只解决安全性或只解决可靠性难题的方案。只关注其中一个或另一个通常都不是好的做法,因为黑客(或用户)会让你在某些方面付出代价。

聚焦应用程序的功能

当你聚焦于应用程序的功能时,可靠性和安全性之间的平衡变得更加明显。这不是简单地使任务完成的事情,而是让任务按用户最初设想的方式完成。在当今世界,这意味着要做类似下面这样的事情。

  • 计数键(越少越好)
  • 允许应用程序在任何平台运行
  • 确保应用程序总是可用的
  • 将与任务无关的步骤数减少到 0
  • 使问题的答案明显,或完全避免问题

开发团队协议

朝着创建一个可靠应用程序的目标所做的任何努力都必须考虑整个团队。开发团队需要从一开始就具有设计和构建可靠应用的意识。在这个过程中,团队还需要记住保持平衡。如果一个应用程序因为运行太慢而无人使用,那将它做得既可靠又安全也没有什么意义。

为了给公司制定合适的协议,你需要以某些方式将任务分解。开发应用程序的团队必须从以下三个不同的级别考虑可靠性的问题。

  • 偶然发生的设计或执行错误
  • 更改技术
    • 面向未来的
    • 黑客的改进
  • 恶意

团队成员之间的沟通是很重要的,但确保沟通没有被误解则更加重要。想当然的假设制造了各种问题,而人们特别擅长用假设来填补信息鸿沟。当然,一名团队成员的假设可能不是由同一个团队的其他成员来执行。开发团队所需的沟通应包括以下这些最佳实践

  • 可靠性和安全性培训
  • 可靠性要求
  • 可靠的设计
  • 可靠的编码
  • 安全源代码处理
  • 可靠性测试
  • 可靠性和安全性文档
  • 可靠性和安全性准备
  • 可靠性和安全性响应
  • 完整性检查
  • 安全性与可靠性研究

创建经验教训的反馈回路

  • 质量
  • 故障点
  • 培训

考虑成套解决方案的问题

处理外部库

外部库带来了许多有趣的可靠性问题。

提升使用外部库可靠性的最好方式之一就是将库下载到本地磁盘并使用这一副本,而不是直接使用供应商网站上的副本。

一个折中的方案是将库代码下载到本地,持续跟踪库的更新,并为应用程序安排更新的时间,以便让你所使用的主要库与所需的安全及特性更新保持一致。这就意味着要按你的时刻表执行更新,而不是按供应商的时刻表。虽然这一方案看起来是一个完美的解决方案,但其实并不是。黑客常常依靠零日(zero-day)攻击对最多数量的应用程序造成最大的伤害。因为你的应用程序不会自动更新,所以你还要面对发生灾难性的零日攻击的可能性。

处理外部API

解决这种情况下的可靠性问题的一个方法是为调用计时。当应用程序检测到调用花费的时间比平时长,它就有可能给出一些反馈,比如告诉用户连接比较慢、联系管理员或以某些其他方式对问题作出反应。关键是要确保你能跟踪每次调用花费多长时间并恰当地采取行动。

使用框架

框架表现出的可靠性介于库和 API 之间。

区别框架和库:库是纯粹的代码,可以下载下来作为应用程序的一部分运行。你可以直接调用函数,且这些函数的源码有时候是可以直接获得的,这样你就能更改函数的行为。框架提供了一种与行为交互的方式,这意味着某些任务对于开发人员是不可见的——开发人员请求框架执行任务,而框架决定如何完成它。代码仍然可以从供应商下载且仍然会成为应用程序的一部分,但底层的技术是不一样的。有些人将框架定义为经过包装的库,它提供结构和代码。

调用微服务

在许多方面,一个微服务就是一个细粒度的 API。

但是,微服务在某些方面还是与 API 不同的,因为前者比较小且更加可定制化。为此,以下列出了你在使用微服务时需要考虑的一些可靠性问题。

  • 使用更多不同源的微服务会增加连接失败的可能性并降低应用程序的可靠性。
  • 选择能真正优化实现特定需求所需的服务的微服务,这可以提升应用程序的可靠性,因为这会降低出现重大错误的风险。
  • 依靠一致的接口来进行微服务调用会减少潜在的代码错误,增强应用程序的可靠性。
  • 拥有多个提供相同服务的微服务源会减少单个故障点的数量,提高应用程序的可靠性。
  • 使用小型的服务意味着单个服务的丢失不会让所有服务都不可用,但使用 API 就会,所以这种情况下其可靠性较高。

包含库

考虑库的使用

用库增强CSS

用库与HTML交互

用库扩展JavaScript

区分内部存储库和外部存储库

内部存储库有很多保存在第三方网站的外部存储库所没有的优点。最主要的是,在相同的编码水平下,内部存储库会比外部存储库更快、更可靠且更安全。(例如,黑客可以轻易下载存储在外部网站上的库的副本,但对于内部存储库则不太可能做到这一点。)此外,由于库只包含了公司真正需要的代码,因此很可能内部存储库会使用更少的资源,这是因为其比通用版本要小。当然,以上对内部存储库做了很多可能不对的假设。以下是一些你需要考虑的点。

  • 构建和维护内部存储库成本很高,所以公司可能不会持续更新以保证安全。
  • 很少有公司能组建一个在技术水平上与第三方供应商相当的开发团队,所以库的质量可能会有问题。
  • 第三方库会接受大量测试人员的测试,所以即使是很小的错误也能被发现。内部存储库通常只接受了少量的测试,甚至可能没有排除一些重大缺陷。
    内部存储库可行的关键在于,其目标是一些特定的功能,而第三方库不能提供这些功能,并且开发团队将它们组合起来需要很大的精力。当综合考虑应用程序的设计时,必须权衡使用内部存储库所带来的优点和风险。此外,必须进一步定义第三方库所带来的风险。

定义库带来的安全威胁

跨站脚本(XSS)

开发者最常面对的安全问题是 XSS。以下三种简单的方法可以处理 XSS

  • 永远不要在同一个 HTTP 响应中传递不受信任的数据(如 HTML 或 JavaScript)。事实上,如果能让主 HTML 文档保持静态是最好的。
  • 当你必须将不可信的数据从服务器传递到客户端时,确保你以 JSON 格式对其编码,并且数据有值为 application/json 的 Content-Type。
  • 一旦准备展示数据,请使用Node.textContent()document.createTextNode()Element.setAttribute()(只需要第二个参数)这些调用以确保页面可以正确地展示它。

危险的函数调用

JavaScript支持某一调用并不意味着这一调用是安全的。使用setInnerHtml().innerHtml =可以注入你不想要的脚本代码。使用setInnerText() 则不会。

直接修改 document

请使用那些添加、删除或更新 DOM(Document Object Model,文档对象模型)元素的调用。

动态创建脚本

任何时候你把字符串转变成脚本,你就正在邀请黑客来提供这些字符串。使用eval()、传递字符串参数的setTimeout()、传递字符串参数的setInterval()或者new Function()会让你的代码严重不安全。

能执行但会导致安全缺陷的代码

使用JavaScript 的严格模式可确保只有安全的调用可以工作。

与声明不符的内容

黑客喜欢发送给你与你所想不一样的内容。避免这一问题的最好方式是遵循一个内容安全策略,这意味着要包含合适的内容标签,比如 script-src ‘self’ 和 objectsrc ‘self’。

解决主要方案:

启用严格模式

严格模式对 JavaScript 的操作方式做了许多明显的和微小的改变。重要的是使 JavaScript 应用的调试变得更简单,因为你的 JavaScript 应用程序如今会在遇到问题时报错,而不是悄悄地失败并表现出怪异的行为。这意味着过去常常无故死掉的奇怪的库调用现在会告诉你相关问题的信息。以下是严格模式可帮你解决的主要问题。

  • 消除对 with 的使用
  • 防止错误变量
  • 不允许强制多态使用 this
  • 阻止重名
  • 对不可变值的改变会发出通知

开发CSP

开发人员面对的最大问题之一是,要开发不容易受到各种脚本或 XSS 等内容的注入攻击的应用程序。由于浏览器的工作方式,这一问题并不容易解决。浏览器实际上信任来自某一特定来源的任何内容,所以浏览器会信任与元数据处于同一级别的任何注入的内容。内容安全策略(Content Security Policy,CSP)旨在通过创建白名单这类措施来解决这一问题。

CSP 提供了让浏览器免于识别来自不受支持的网站的脚本及内容的方法。该策略表现为加在页面顶部的各种头部信息。当浏览器看到这些头部信息时,会使用它们来确定哪些脚本和内容是可以安全加载的。

头部包含三个基本要素:策略名数据类型数据源。下面是一个只包含一个数据类型的CSP 头的例子。

1
Content-Security-Policy: script-src 'self'

在这个例子中,CSP 声明浏览器应该只执行嵌入到本页面的脚本。如果你还想提供对jQuery 的支持,就需要为此扩展策略,如下所示。

1
Content-Security-Policy: script-src 'self' 'https://code.jquery.com'

你不需要提供源代码文件的具体位置,只需要主机站点的信息。每一个入口通过空格与下一个分隔开。一个 CSP 可以定义各种内容类型。例如,你可能决定你要支持在本地页面中找到的脚本,但你根本不想支持对象。在这种情况下,你要使用下面的 CSP 头。

1
Content-Security-Policy: script-src 'self'; object-src 'none'

安全地包含库

有些公司简单地将一个库加到它们的工具箱里,因为其提供了所需的功能,而没有仔细考虑应如何将库与其他代码集成在一起,甚至没有考虑使用它是否是一个好的做法。至少在某些情况下,那些没有仔细考虑库的使用的公司最终都会后悔,因为这些库包含了大量的安全漏洞,运行得不可靠或很慢,或者与从一开始就自己开发库相比,其要耗费公司更多的时间。

拥抱沙盒方案:本质上来说,这意味着第三方库在比自己创建的代码较低的权限级别上运行。使用沙盒不能确保应用程序是安全的,但它降低了应用程序出现安全漏洞的可能性。

充分研究库

任何时候你选择将一个库添加进自己的应用程序中,就展示了对库作者的信任。即使有称职的安全专家的帮助,将一个库拿出来并对其每一处的代码进行测试(假设能得到完整的代码)会花费比从头开发库更多的时间。(确保你拿到的不是压缩版的库,不然会很难检查,因为供应商移除了空白字符。)因此,你必须考虑将这个库作为你创建的应用程序解决方案的一部分,带来的风险是否足够小。当研究一个库时,考虑一下以下问题。

  • 行业杂志是否以负面的方式讨论过这个库?(如果这个库有已知的未解决的安全漏洞,你可能需要重新考虑是否将其用于自己的应用程序中。)
  • 是否有其他公司使用了该库且没有问题?
  • 开发这个库的公司在回答关于库的完整性问题时是否是积极的?
  • 创建这个库的公司是否提供了一致的 bug 修复和更新?
  • 人们在这个库的在线支持中问了哪些问题?
  • 这个库存在了多长时间?(存在时间很长的库通常问题更少,并且创建者的支持工作也做得更好。)

精确定义库的使用

没有人会使用应用程序中每个库的每个功能。有些研究网站宣称开发人员只会使用库的一个或两个功能。用文档记录你要如何使用一个库,可将注意力聚焦在相关部分,从而帮你规避使用库所带来的风险。更重要的是,跟踪具体的使用部分能告诉你何时必须执行一次更新,或如何通过移除用不到的功能来缩小库的规模。对于如何使用库来执行特定的应用任务了解得越多,就越能降低使用它时的风险。

保持库的小规模和内容聚焦

只要有可能,就应将第三方库保持在小规模且聚焦于特定的需求。这意味着你要移除你不会使用的代码,因为这些代码会制造以下问题。

  • 拖慢下载速度
  • 增加安全问题
  • 降低可靠性
  • 导致不必要的更新

使用构建器意味着你创建的库只包含你确实需要的元素。当然,其缺点是你必须提供对这个库的维护,这意味着当作者有更新时你得不到自动的更新。如果你选择了这一方案,关键是要定期检查是否有更新库的需要。

执行必需的测试

在将任何库放到生产环境中之前,你必须构建一个测试套件,以确定这个库是否能如你期望的那样工作。你可以使用一个供应商指定的测试套件,但使用自己设计的、能模拟你的应用程序如何使用这个库的测试套件也是很重要的。测试过程应该包含对合法和不合法的输入数据的检查。重要的是要确保对于合法的输入你能得到预期的输出,而对于不合法的输入你能得到某些异常。

区分库和框架

框架以库所不具备的方式为网站提供了一套模板。当使用库时,应用程序只是依靠其他来源的代码。框架的使用意味着另一种层面的参与,应用程序现在使用外部源来获得额外的支持。因为框架更充分地集成到一个应用程序中并且提供了更多的资源,所以比起使用库,你还必须将其放在一个更高的标准上(虽然你应该将两者都放在高标准上)。
尝试指出一个框架是否能安全使用是很困难的。事实上,测试会变得几乎不可能。

为此,第三方的安全专家尝试量化框架和模板库的安全级别。查询相关信息的一个好地方是mustache-security(https://code.google.com/archive/p/mustache-security/)。这个网站为你提供
对框架 7 个级别的检查。

慎用API

区分API和库

考虑流行速度上的差异

库与 API 都能为开发人员节省开发时间。事实上,现在大部分基于 Web 的应用程序会结合使用库与 API 来执行任务。但是看起来 API 比库流行得更快,原因如下。

  • 减少了资源的使用
  • 减少了编码要求
  • 更小的学习曲线

区分用法上的差异

接下来描述了 API 的一些典型应用以及它们的安全意涵。

  • 数据查询
  • 监视控制与数据采集
  • 监视
  • 多媒体
  • 定位

用API扩展JavaScript

定位合适的API

创建简单示例

依靠 API 来创建安全应用程序比你想象的要更加困难,因为即使是最好的 API 也要依靠一定程度的信任。你的风险是要信任主机的数据或主机提供的其他资源。

定义API带来的安全威胁

通过JavaScript安全访问API

验证API的安全性

测试输入和输出

保持数据的局部性和安全性

防御性编码

考虑使用微服务

微服务是一种相对较新的技术,它会将一个大应用程序分解成多个小模块。每个小模块独立运行并只执行单一任务。由于微服务所依靠的技术以及其运行的方式,微服务提供的安全性会比本书目前为止提到的其他技术更高。但是,与其他技术一样,微服务也会给黑客制造问题的机会。重要的是要记住任何技术都有让黑客可利用来做坏事的漏洞。开发人员的目标是要将这些漏洞减至最少,并确保有尽可能多的防护措施来帮助执行监控过程。

定义微服务

详述微服务的特点

许多开发人员习惯于严重依赖面向对象编程(object-oriented programming,OOP)技术来进行整体性设计。开发任何应用程序都始于定义各种对象和考虑各种问题。目前的应用程序设计需要大量的前期时间来启动,且它们被绑定到特定的平台上。微服务则不同。没有
大量的代码,你只需要写很少的代码,并且在进一步的开发过程中才作很多决定,而不是在开始阶段就作决定。微服务具有以下特征。

  • 小:每个微服务只执行一个任务。
  • 语言独立:每个微服务使用最适合它要执行的任务的语言,而不会考虑其他微服务的需求。
  • 数据传输独立:虽然大部分微服务目前都依赖 JSON 来传输数据,但你可以使用任何最适合具体微服务的数据传输方式。
  • 队列消息:微服务的通信通常采用异步消息系统,所以没有哪个微服务会导致整个应用程序的延迟。
  • 笨管道:现今的许多通信方式存在的一个问题是管道智能化。微服务依靠笨管道以及智能服务。许多微服务采用 REST(Representational State Transfer,表述性状态转移)协议进行通信。
  • 分散模式:每个微服务与其他微服务是隔离的,并且与应用程序也是分开的。某个微服务的失败通常不会影响到应用程序的操作。每个微服务接受独立的监控。
  • 与平台无关:任何应用程序都能使用微服务,无论该应用程序及微服务在什么平台上运行。

你可能想知道微服务的大小,也就是只执行单一任务的真正含义。想想一个字符串的处理。当使用某个一体化应用程序时,会有一个单独的对象来处理首字母大写、反转以及将字符串转换成数字等操作。当使用微服务时,你会为每个任务创建一个单独的微服务。

区分微服务与库

认识到微服务并不像库那样以进程内方式运行是很重要的。微服务像 API 一样在某台服务器上运行。这意味着你在使用微服务时不会有使用库时碰到的安全风险。你可以完全将应用程序的代码与微服务分离开。
微服务的调用语法也与库不同,你要创建一个 JSON 请求并将其发送到服务器端。响应也会是 JSON 格式的。JSON 的使用让我们能够以不依赖 XML 的更丰富的方式进行工作。使用 JSON 比使用 XML 简单得多,因为 JSON 对于 JavaScript 是原生的并且有更轻量级的语法。你会在本章后面的部分看到它是如何工作的。而现在,你只需要知道在大部分情况下
微服务不同于库的调用。
从安全的角度来说,微服务会比库更安全,因为它们不是以进程内的方式执行,并且你可以通过使用 JSON 的最佳实践方法来避免大部分错误数据输入形式。当然,黑客可以破坏你为安全所做的努力,而微服务也不会例外。

区分微服务与API

API 常常要求你创建一个对象,然后调用这个对象。请求可以有许多形式,如 REST、HTML 请求头或 XML。响应会涉及对页面对象的直接操作。这个过程是很麻烦的,因为你在使用大量不一致的一体化代码。
与 API 一样,微服务也是在进程外执行的。但是与 API 不同的是,微服务并不是大块的一体化代码。每个微服务都是很小的,并且在自己的进程内运行,这使得可以完全保证一个函数与另一个函数是隔离的。数据交换只使用一种形式,就是 JSON,这似乎是现在最好的方法,因为它比使用 XML 简单。

考虑微服务的策略

使用微服务是有意义的,因为它可以创建在任何地点任何设备上都能运行的应用程序,而不会给开发人员制造太多麻烦。不幸的是,一体化应用程序开发场景会创造一片封地,在这片封地里某一级别的管理者可以支配他们自己的特定资源。因为微服务很小,可方便地服务于各种目的,并且不会在意所需的数据来自哪里,所以它们打破了公司团队之间的围墙,破坏了过去被支配的各种封地。在这种情况下,某种程度的斗争,甚至是破坏的发生是在所难免的。

将某个微服务策略添加到你的编程工具箱中时,遵循一个流程是有帮助的(你不必精确地遵循下面列出的步骤,但它们会帮你克服在使用微服务时遇到的各种阻力)。

  • (1) 组建一支负责微服务开发的团队,让他们与目前维护一体化应用程序的团队分开。
  • (2) 从为一个新的应用功能创建一些粗粒度的微服务开始做起。
  • (3) 一开始开发提供自包含业务功能的微服务,这样你就不用太担心互动的问题。
  • (4) 给现有团队提供足够的时间去发现如何使用微服务并开始将它们用于现有应用程序中。但在你有足够的成功经验使每个人都同意之前,不要完全将现有应用程序迁移到微服务。
  • (5) 随着最初的微服务开发的进行,你会发现有哪些地方需要改变,创建细粒度的微服务会产生更好的效果。
  • (6) 标准化服务模板,这样就可以在最小化团队沟通成本的情况下开发微服务。标准化模板还可以减少安全问题,因为没有人必须去做任何假设。
  • (7) 创建足够细粒度的微服务去开发一个完整的应用程序,但不要聚焦于某个现有应用程序的需求,而要考虑创建一个新的应用程序。
  • (8) 获取必需的工具去执行粒度监控、日志收集、应用度量、自动化发布以及展示系统状态或日志报告等数据的状态仪表盘。
  • (9) 完全基于微服务开发技术构建一个小型应用程序。其目的是创建一个表明微服务是真的可行的完整应用程序。对于刚开始学习微服务的开发团队来说,开发一个小型应用程序会降低失败的概率。
  • (10) 慢慢地交叉训练每个人,使得技能方面的尖锐分歧减少。
  • (11) 打破不同团队之间的孤岛。开始创建微服务,使得来自每个团队的代码、资源和数据可提供给所有其他团队,而不需要考虑这些资源来自哪个团队。
  • (12) 从现有的一体化应用程序开发转移到充满微服务设计的开发。
  • (13) 从一个小型的一体化项目开始,如果可能,将这个一体化项目完整地迁移到某个微服务环境。慢慢地执行这项任务,在每次添加微服务之后都进行评估以确保应用程序真的运行得更快、更可靠并且更安全。
  • (14) 当你用细粒度和更多功能的微服务替换旧的微服务之后,将旧的微服务从系统中移除掉。

用JavaScript调用微服务

理解通信中REST的角色

微服务依赖 REST 这样的通信架构类型,因为它比其他协议[如简单对象访问协议(Simple Object Access Protocol,SOAP)]等更轻量。使用 SOAP 在某些条件下有优点,但在互联网场景中存在一些问题,比如占用大量带宽,以及在客户端和服务器之间需要一个更加正规的通信水平。依赖 REST 进行通信的应用程序被称为 RESTful 应用程序。在微服务中使用 REST 具有以下益处。

  • 解耦消费者和生产者
  • 提供无状态的通信
  • 允许使用缓存
  • 允许使用分层系统
  • 提供统一接口

用JSON传输数据

微服务依赖 JSON 来传输请求和响应。是的,你还会使用 REST 来发送数据,但这个信息最终是以 JSON 格式发送的。下面是你使用 JSON 来传输数据的三个主要理由。

  • 干净的数据
  • 效率
  • 可扩展性

用Node.js和Seneca创建微服务

最好的例子位于网站 http://senecajs.org/ 上。服务器的源码如展示的那样工作。

定义微服务带来的安全威胁

缺少一致性

微服务可能带来的最大问题是缺少一致性,这似乎也困扰着过去创建的每个库和 API。

考虑虚拟机的角色

所有微服务通常都在自己的虚拟机(virtual machine,VM)环境中运行。这意味着一个微服务通常不会破坏另一个微服务。即使一名黑客入侵了一个微服务,他能造成的破坏通常是最小化的。但是,很有可能在同一个虚拟机中运行着多个微服务,为此,黑客可能尝试各种技术来入侵整个 API。

使用JSON进行数据传输

    1. 考虑eval()的危险
    1. 防御跨站请求伪造

一个跨站请求伪造(cross-site request forgery,CSRF 或 XSRF)是攻击者尝试让用户在不经意间或不知情的情况下执行代码的攻击。这个代码在用户所在的权限级别上运行并且有用户的认证凭据,所以它看起来是用户本人在执行代码而不是别人。在大部分情况下,当发生下面的一系列事件时,微服务会遭遇到这种特殊的攻击。

  • (1) 一名用户登录进一个使用微服务的应用程序。
  • (2) 该用户执行各种任务,每个任务都依赖 REST 进行通信。
  • (3) 攻击者发送给该用户一个有特殊格式的 URL,它看起来像是其他的 REST 消息,但会
    干黑客想要用户干的事情。这个 URL 可能作为电子邮件消息的一部分或在某些其他的通信中出现,甚至可能是网页里的一个链接。
  • (4) 用户初始化请求,就像平时一样发送 URL 到微服务。
  • (5) 恶意的请求被执行,而用户甚至可能还没意识到其已经发生了。

定义传输层的安全

微服务和 API 的致命弱点是需要来回发送消息,而不是在单一机器上执行任务。解决这一问题的基石在于传输层安全(Transport Layer Security,TLS)。关键是要确保客户端与服务器之间的传输层在消息发送过程中是安全的。这意味着要用 HTTPS 和 REST 等技术来确保通信尽可能安全。
把每个调用和响应都包在 HTTPS 和 REST 中的一个问题是应用程序会变慢。解决这一问题的最佳方法是使用负载均衡来传输客户端的通信,并保持一个渠道开放给后台处理需求。保持放开后台处理渠道可降低成本并减小使用 HTTPS 和 REST 的影响。

HTTPS 和双向 TLS 的使用可确保客户端和服务器在每次的请求 / 响应中都能识别对方。认证减少了某些人能成功实施中间人攻击来非法获取数据的机会。现在的大部分通信是采用单向 TLS 的,也就是客户端会验证服务器的身份,但服务器简单地认为客户端没有受到损害。鉴于微服务的实质,你真的需要实现双向 TLS 来验证客户端和服务器的身份。

创建可替换的微服务路径

像黑客一样思考

定义Web安全扫描的需求

Web 安全扫描的基本思想是,它们会告诉你你的网站目前是否干净,并且有时候能帮你考虑潜在的安全漏洞。

构建测试系统

考虑测试系统的使用

你需要一个测试系统,这样你就能自由探索黑客会采用的各种攻击手段。除非充分了解如今的大部分应用程序是何等脆弱,否则你真的无法修复自己的应用程序。开发人员会犯各种错误,在当时看起来是无害的,但后面会导致安全问题。小到代码中的一些错误注释都可能导致问题。使用生产系统来测试是一个糟糕的想法,因为本书所描述的方法可能会让你遭受真正的攻击。你需要一个能够破坏并恢复的系统,以确保你真正掌握所学。
这里的要点是对有害条件下的各种应用程序进行测试,但是以安全的方式进行,不会真的损害任何真实数据或任何生产系统。给测试环境注入病毒可以让你发现应用程序会如何应对,而无需真的在生产环境中试验这个病毒。当然,你的测试系统不能连接到你的生产网络(否则病毒可能会跑出来并感染生产系统)。你也可以使用这个环境来测试各种漏洞,并确定黑客会采用什么手段来破坏你的应用程序。
但是,测试系统不止能用来测试应用程序。你还可以用它测试对策的效率或者特定配置的漏洞。测试整体的系统有助于确保应用程序不会轻易被其他部分的漏洞所破坏。测试整个环境是很关键的,因为任何黑客都会这样做。

接受必需的训练

  • 通用原则(General principles)
  • 代码质量(Code quality)
  • 并发(Concurrency
  • 未验证的参数(Unvalidated parameters)
  • 访问控制缺陷(Access control flaws)
  • 认证缺陷(Authentication flaws)
  • 会话管理缺陷(Session management flaws)
  • 跨站脚本攻击(Cross-site scripting,XSS)
  • 缓存溢出(Buffer overflows)
  • 注入缺陷(Injection flaws)
  • 不安全的存储(Insecure storage)
  • 拒绝服务(Denial of service,DOS)
  • 配置(Configuration)
  • Web 服务(Web services)
  • AJAX 安全性(AJAX security)

创建正确的环境

因为你会用测试系统来进行各种测试,所以必须为你的实验室提供物理上的安全性。否则一些好心人会让病毒或恶意代码进入到生产环境中。你也不应该把安全设置开放给那些心有不满的职员,或那些会用它来给你的生活制造麻烦的人。测试环境应该尽可能反映生产环境,但你也需要保持它们的物理隔离,否则就得承担后果。
为了将生产环境和测试环境隔离,你应该考虑为测试环境使用不同的颜色标示。你不应该把生产环境连接到测试环境中,反之亦然。此外,很关键的是要标记测试环境,这样就没有人会在生产环境中使用它们。

使用虚拟机

大部分公司没有足够的硬件来为应用程序用户所依赖的每种操作系统和浏览器配置单独运行一台机器。解决方法就是使用虚拟机,这样你就能通过配置一台计算机来运行多个虚拟机。每个虚拟机会代表用于测试的一种用户配置。
使用虚拟机还有另外一个理由。当虚拟系统被你制造的攻击击垮时,你可以简单地停掉它,删除相关文件,并从保存在硬盘中的基线配置中创造出新的副本。你可以在几分钟内就搭建出新的测试系统,而不需要花上几小时。
虚拟机可以解决搭建系统中的许多问题,但你也需要考虑高端硬件的需求以使其可正常工作。动力不足的系统无法产生可用于评估安全问题的结果。你能在一台物理机器上创建的虚拟系统的数量受限于内存大小、处理周期以及在这一过程中系统必须使用的其他资源。

获取工具

除非你想要写自己的安全工具软件(一个毫无疑问的坏想法),否则就需要从别处获取它们。幸运的是,McAfee(http://www.mcafee.com/us/downloads/free-tools/index.aspx)等网站会提供各种免费工具,你可以用它们来执行以下任务。

  • 检测主机系统里的恶意软件
  • 评估系统是否易于遭受攻击
  • 在遭受一次攻击后执行诊断分析
  • 使用 Foundstone 的软件应用安全服务(Software Application Security Services,SASS)工具让应用程序更安全
  • 确定入侵发生的时间
  • 扫描系统的各种弱点
  • 对系统进行压力测试

配置系统

从一开始就为你想要支持的每个平台创建一个干净的配置是很重要的。请确保副本是你从配置镜像中弄出来的,这样你就能在以后恢复它。配置应该包含操作系统、浏览器,以及任何支持测试环境所需要的测试软件。你也可能需要包含用户通常会安装在系统中的软件。

恢复系统

定义最常见的漏洞源

每天都有新的安全漏洞出现。任何人都不可能跟进所有的漏洞。但是,某些安全漏洞需要特别关注,而其他漏洞可能未来才会碰到。当然,最好是有一套有条理的方法来定位安全漏洞,而 OWASP 提供的清单(https://www.owasp.org/index.php/Web_Application_Security_Testing_Cheat_Sheet)无疑是一个正确的开始。

避免SQL注入攻击

SQL 注入攻击有多种形式。例如,你会处理应用程序中的表单数据来确定该表单是否能够发送命令到后端服务器。但是,最好是首先有一个检查潜在 SQL 注入攻击的通用方法。

假设你有一个像 http://www.mysite.com/index.php?itemid=10 这样的 URL。你会在很多网站上看到类似结构的 URL。检查漏洞的一个方法是简单地在 URL 后面加上一个单引号,使其成为 http://www.mysite.com/index.php?itemid=10'。当你按下回车,网站会发送回一段SQL 错误消息。这个消息会根据系统有所不同,关键是后端服务器会接收你的 URL 作为SQL 请求,它会被拼成类似这样的格式:SELECT * WHERE itemid=’10’’。多出来的单引号会使查询不合法,从而产生错误。

你现在可以开始操作 URL 来看看会发生什么。例如,你想看看查询会产生多少列数据以及这些列都是什么。可以使用 ORDER BY 这样的 SQL 语句来执行这一任务。改变一下 URL使其包含 ORDER BY 语句,就像这样:http://www.mysite.com/index.php?itemid=10' ORDER BY 1。这与输入 SELECT * WHERE itemid=’10’ ORDER BY 1 这样的命令是一样的效果。通过在每次请求中给 ORDER BY 的值加 1,你最终会看到一个错误。假如当你尝试到 ORDER BY 10 时看到了错误,那么可以确定在这个例子中请求会产生 9 列数据。黑客会继续在 URL 中加入 SQL 命令来了解更多数据库的查询结果。例如,使用 SELECT 语句可以帮你确定哪些数据会出现在屏幕上。你也可以请求特殊的信息作为 SELECT 语句的一部分,比如使用 @@version 来获得 SQL 服务器的版本号(会给你一些关于该 SQL 服务器可能存在缺陷的信息)。这里的关键是原始的 URL 可以直接访问 SQL 服务器,使得别人完全不需要做太多工作就可以控制 SQL 服务器。
你可以在网页 http://www.w3schools.com/sql/sql_injection.asp 上看到另一种 SQL 注入攻击。这些攻击根本的原因是开发人员直接使用来自页面或请求中的数据,而没有首先检查其是否有问题并将错误信息移除。

理解跨站脚本攻击

XSS 在攻击方式上与 SQL 注入有很多相似之处,但实际上这两种技术是不同的。XSS 有两种类型:非持久的(攻击依赖用户访问某个专门制作的链接)和持久的(攻击代码保存在间接的存储媒介中,比如数据库)。这两种攻击都依赖 JavaScript 代码被放到你不希望它们出现的地方。
作为一个非持久 XSS 攻击的例子,可以看看链接 http://www.mysite.com/index.php?name=guest。它看起来像是一个完全无害的链接,带有一个名 / 值对。但是,如果你将一段脚本加入到其中,比如 http://www.mysite.com/index.php?name=guest<script>alert('XSS')</script>,用户会看到一个对话框弹出来,带有信息 XSS。这个例子不会造成任何破坏,但这个脚本可以轻易地做任何你在脚本中可做的事情。例如,你可以定制脚本,让它把用户重定向到另一个网站,那个页面会下载一个病毒或其他恶意软件。

一个持久的 XSS 攻击会比较难实现,但会造成更大的破坏。例如,想象用户登录应用程序之后会发生什么。服务器会将一个会话 ID 作为 cookie 发送回用户的机器。之后的每次请求都会使用这个会话 ID,这样应用程序就能维持这个用户的状态信息。假如攻击者将经过特别定制的脚本发送给服务器作为登录过程的一部分,那么它会被保存到数据库中,用的名称是管理员几乎肯定会去查看的名称。当管理员点击用户名时,这个脚本会执行并会将管理员的会话 ID 发送给攻击者。攻击者现在拥有了能操作其他会话的管理员权限,他可以执行任何管理员级别的操作。你可以在网页https://www.acunetix.com/blog/articles/persistent-cross-site-scripting/ 上获得更多关于持久 XSS 的详细信息。
在这两种情况下,防御 XSS 的最佳方法就是对任何你接收到的输入进行净化。净化输入的过程会移除任何脚本、怪异的字符或其他不期望出现在响应中的信息。例如,当你期望一个数字型的输入时,响应不能是包含有字母的字符。

解决拒绝服务攻击问题

DOS 攻击的思想是相对直接的。你找到服务器上的一个开放端口并持续发送没有意义的包给它,从而压垮相关的服务。大部分服务器都有提供开放端口的服务,比如以下这些服务器。

  • DNS 服务器
  • Email 服务器
  • FTP 服务器
  • Telnet 服务器
  • Web 服务器

当然,你开放越多的端口,服务器被压垮的机会就越大。防御的第一条是通过下线不需要的服务来关闭不需要的端口。一个应用服务器可能只需要作为 Web 服务器,那么这就是你唯一应该安装的服务。作为一名应用开发人员,你要建议下线其他服务(至少不要启用它们)。
当创建某个私有应用程序时,采用非标准的端口也是有帮助的,比如请求认证的时候。
黑客通常会寻找没有最大连接数的服务,所以要确保最大连接数是服务器能处理的值,这是另一个正确的步骤。DOS 攻击中最重要的是让服务器一直运作,比如让 Web 服务器执行一次复杂的搜索。认证机制有助于防止黑客在没有适当认证时发起请求。
还有,如果有足够多的系统在发送无尽的没有价值的请求,也可能把服务器拖垮。一些抵挡 DOS 攻击的措施包括寻找该请求可匹配的模式然后简单地拒绝它们,而不是花费系统资源去处理这些请求。你可以在网页 http://resources.infosecinstitute.com/dosattacks-free-dosattacking-tools/ 上找到大量 DOS 攻击工具来测试你的系统。此外,除了自己去查找攻击的匹配模式并解决它们,你还可以尝试采取以下措施。

  • 购买专门设计用来对抗 DOS 攻击的设备
  • 依靠网络服务提供商(ISP)来检测并消除 DOS 攻击
  • 获取云缓存提供商的服务

去除可预测的资源定位

通过了解特定资源的位置,然后使用这些资源来获取访问系统的足够信息,是有可能对系统发起攻击的。有些网站还将这种攻击称为强制浏览。当然,防止这种攻击的最佳方式是将资源放在不可预测的位置。此外,你要确保认证体系是有效的并且资源是安全的。无论如何,让我们看看这种攻击是如何发生的。这种攻击的一个例子是,识别一个指向合法资源的 URL 并使用这个 URL 去访问属于另一个人的资源。假如你在网页 http://www.mysite.com/Sam/10/01/2015 上保存了你某一天的日程安排,而你想要看看 Amy 在同一天的日程安排。如果管理员没有正确配置服务器的认证,将 URL 改为 http://www.mysite.com/Amy/10/01/2015 就有可能可以正常访问到该信息。另一个例子是,某些服务器会把信息放在特定的目录下。例如,你可能认证了对 http://www.mysite.com/myapp/app.html 的访问,但是你可以将 URL 改为 http://www.mysite.com/system/ 来看看其是否存在。如果你从服务器得到一个 200的响应,那么这个目录就存在,你就可以开始请求它来获得有用的信息。当然,这里假设管理员没有给系统目录加上适当的安全措施,并且 system 是一个标准的目录位置。管理员总是可以更改系统目录的名称,还可以确保只有拥有适当凭据的人员才可以访问它。

克服无意的信息泄露

无意的信息泄露会以各种方式发生。但是,这类攻击通常涉及黑客未经授权地访问某个集中式数据库。以前,信息源是网络信息系统(Network Information System,NIS)这样的东西。但是,今天的信息可能有任何来源,这些来源不够安全或有黑客可利用的缺陷。这样的源头有很多,实在不可能用一个简单例子将它们都展示出来。你可以通过以下措施来克服这类问题。

  • 给操作系统、服务、应用环境、库、API 和微服务打上必需的补丁
  • 配置边界路由器和防火墙来阻止从敏感来源发起的信息请求
  • 限制对所有敏感信息源的访问
  • 永远不要硬编码密码并把它们放在别人能轻易找到的地方
  • 对任何敏感信息源使用双因素认证
  • 执行审核来查找潜在的漏洞(即使你感到系统完全安全)
  • 使用评估工具来确定是否存在从指定位置外的任何位置访问敏感信息源的可能

在BYOD环境中进行测试

BYOD 现象在持续增强。重要的是要认识到 BYOD 不会消失。事实上,你可能已经认识到 BYOD 在某些方面正在变成给用户提供设备的最佳方法。公司最终会告诉用户携带任何完成工作所需的设备,并且把所有东西都交给用户。这听起来像是一场灾难,但这是用户现在正在做的事情。

配置远程访问区域

当在 BYOD 环境中工作时,最好假设设备环境是不安全的且无法简单地使其变得安全。为此,BYOD 会话通常有以下 4 个阶段。

  • (1) 客户端和服务器端创建一个安全的管道以使得外部难以窃听。其目的是防止中间人攻击。
  • (2) 用户提供两种形式的认证。一个双重认证过程会使别人不太可能成功地模拟该用户。
  • (3) 客户端发起一个或多个请求,而服务器端使用一个服务中转模块来进行中转。服务中转模块只处理对合法服务的请求。服务中转模块会自动记录每次请求是成功还是失败。
  • (4) 服务隔离模块只提供对公共数据的访问。它不允许对敏感数据进行访问。客户端只能看到公司认为在 BYOD 环境下可接受的数据。

使用远程访问区域也意味着公司要配置移动设备管理(mobile device management,MDM)。这是有助于确保移动设备尽可能保持安全的一些产品和服务。例如,MDM 会检查移动设备的补丁要求,并确保在访问应用程序之前给设备打上补丁(你可以自动为设备打上补丁)。就算有 MDM,当出现以下情况时,你也不能假设设备是安全的。

  • 设备报告了一些与安全配置有关的值,但你的公司实际上并没有实现这些特定的值。一个恶意软件的开发人员不会知道哪些值需要上报,所以会简单地将它们全都上报。
  • 其他机构会覆盖设备的设置。例如,一个智能手机供应商会在你的控制之外自动给设备打补丁,你必须假设其中一些补丁会包含病毒或其他恶意软件。
  • 不可能给移动设备强加设置。设备可能不提供所需的支持,可能没有将应用程序配置为支持该设备,或者有恶意软件干扰其更新。

检查跨应用程序的攻击

跨应用程序攻击是指一个应用程序获取另一个应用程序使用的数据或资源。在许多情况下,攻击发生于两个应用程序访问相同的数据源时(跨应用的资源访问或 XARA)。

验证你的应用程序不会在这种攻击中泄露数据的最佳方式是持续关注你要支持的平台的新闻。你需要一名安全专家来找出潜在的这类问题。遗憾的是,如果禁止从操作系统或浏览器供应商中获取补丁,你就不能真正对这种攻击做太多事情,只能保持警惕,检查数据存储可能遭到的破坏。当然,这种问题只会给创建远程访问区域提供更多的凭据。

处理真正古老的设备和软件

可能解决过时设备问题的唯一方法是在请求的过程中检查浏览器数据。

依靠用户测试

让用户横冲直撞

开发可重现的步骤

当进行用户测试时,你需要考虑以下这些类型的测试。

  • 功能性:测试所有的应用程序功能是很重要的。这意味着要让用户尝试表单、执行文件操作和计算任务,使用应用程序功能来搜索信息,并且尝试应用程序特性提供的媒体功能。
    当用户测试这些功能时,请确保测试也检查应用程序用来执行大多数任务的库、API 和微服务。

  • UI 和可用性:UI 必须保证让用户感兴趣并为任何有特殊需要的人提供支持。作为这一级别的测试的一部分,你需要检查导航、可访问性、多个浏览器下的可访问性、错误消息和警告、你提供的帮助和任何其他文档,以及布局。

  • 安全性:虽然你已经测试过应用程序以确定其是否有常见安全漏洞,你还是需要让用户来测试它们。看看用户是否会用与你相同的方式使应用程序受到破坏。找到可产生新的漏洞条件的用户操作方式。
  • 负载与可伸缩性:
    你不可能完全测试一个应用程序在高负载下的表现。应用程序的性能会随着负载的增加而降低,你需要确保其能够很好地伸缩。但是,最重要的是,你需要验证负载不会导致安全事故发生。无论负载有多少,应用程序都能继续工作,知道这一点很重要。当然,当负载超过预期时,应用程序会运行得慢得多,但这与真正的破坏是不一样的(例如,放某人进来的事故)。

让用户发声

使用外部的安全测试人员

大部分公司会因为以下这些理由而使用外部安全测试。

  • 定位内部审核时遗漏的缺陷
  • 给客户和第三方提供一份对应用程序安全性的独立评审
  • 在公司没有任何安全专家的时候要确保安全测试是完整的
  • 验证事件管理程序的有效性
  • 培训事故处理团队
  • 降低整个公司的安全成本

考虑渗透测试公司

管理项目

覆盖要点

获取报告

创建API安全区域

你在应用程序中创建或使用的任何 API 都有可能带来大量问题。但是,与库不同,你确实可以让 API 的使用变得更加安全,因为 API 在自己的地址空间和自己的进程内执行。将API 放在沙盒或虚拟环境(特别是受保护的环境)中可以实现以下控制。

  • 精确地控制 API 执行的动作、访问的资源和它与应用程序交互的方式。当然,你也可能令 API 得不到所需资源,或者使沙盒或虚拟环境兼容过多东西而令 API 不能完成任务。你必须在风险(安全)和有效工作之间维持平衡。
  • 控制应用程序如何与 API 交互。例如,你可以减小错误或恶意的输入导致严重灾难的可能性。严格控制应用程序的输入,非法输入造成的影响就会减少,甚至完全没有影响。当然,这种保护也让使用 API 或执行某些测试变得困难。

理解API安全区域的概念

API 安全区域会提供一个安全且灵活的方法来测试或使用你创建的 API。在测试阶段使用一个 API 安全区域以确保可以快速地从错误中恢复,通常是一个好的做法。因为其所提供的安全特性,你可能会在把 API 放到生产环境中之后决定继续使用 API 安全区域。
使用沙盒确实能提供额外的安全性。但是,许多公司在实现沙盒环境时并不关心安全性。除了安全性,沙盒还可以提供以下好处。

  • 通过控制资源使用而降低成本
  • 受控制的第三方 API 访问
  • 用更好的控制来使测试和开发环境获得提升
  • 缩短推向市场的时间
  • 为测试模拟错误场景
  • 受监控的 API 性能

公司也会使用虚拟环境来达到其他目的,而不只是使 API 变安全。与沙盒一样,一个虚拟环境会通过控制资源的使用来降低成本,控制对第三方 API 的访问,以及改善测试和开发的环境。从公司的角度来说,虚拟环境有以下优点

  • 缩短 API 崩溃之后的恢复时间
  • 增强速度和网络带宽控制
  • 改进能源的使用
  • 减少硬件的占用空间
  • 更快的物资供应
  • 减少供应商锁定的情况
  • 增加正常运行时间(即使在安全性和可靠性事件很少时)
  • 延长应用程序的生命

如果使用沙盒和使用虚拟环境时 API 都能很好地工作,那么究竟是选择使用沙盒还是使用虚拟环境,最终可能会归结为它们所能提供的额外功能。但是,如果你想维持如今应用程序要求的安全性,很重要的是要首先考虑这两者在安全方面的表现。

定义API安全区域的需求

安全就是风险管理。

无论你的 API 在一个请求中是发送还是接收数据,在沙盒或虚拟环境中使用该 API 都是有意义的,因为这样做会降低风险。完全隔绝坏的东西是做不到的,但你可以将风险以及你所遇到的漏洞带来的影响降到最低。

确保API可以工作

安全有关风险管理。确保你的 API 按预期工作可以降低风险,因为这会减少别人以你未考虑到的方式运行 API 的可能性。黑客常常干着寻找方法使代码异常工作这样的肮脏事情,让代码执行其作者不想其做的事情。使用 API 安全区域可以让你用平时无法使用的测试方法去测试代码,而无需在测试服务器上制造问题。你可以执行以下测试来确保 API 按预期
工作。

  • 验证正确的输入会产生正确的响应
  • 运用范围检查来确保 API 能正确地响应边界外的数据
  • 输入错误类型的数据来确保 API 能根据类型净化数据
  • 专门输入黑客可能使用的非法数据,比如脚本、标签或二进制数据
  • 删减必需的数据
  • 加入额外的数据
  • 制造 null 值的输入
  • 使 API 超负荷
  • 使 API 得不到资源

实现快速开发

把 API 直接放在服务器上意味着你提供了 API 的一份副本给每个人使用。当然,一旦任何一个开发人员决定尝试某些激进的做法而导致 API 或服务器崩溃,其他所有人也不得不停止工作。你应该让开发人员在做试验时没有负担,因为黑客在做这些事时肯定不会有负担。创建一个所有人都必须遵守规则的受约束环境,几乎肯定会把那些黑客用来破坏服务器的安全漏洞隐藏起来。

沙盒通过使服务器变得不太可能崩溃来帮助实现快速开发。如果 API 遇到了问题,你通常可以非常快速地恢复它。沙盒环境还能很容易地模拟现实世界中的各种情况,比如缺少资源或完全没有资源的情况。但是,你仍然要有一个 API 的副本在运行,所以还不能像在开发环境中那样轻松。
使用虚拟环境可让每个开发人员(或者每组开发人员)拥有一份 API 的副本。当然,你要使用更多的服务器资源去实现这一场景,但关键是你最终会有一个不会干扰其他开发人员的编码工作的环境。每个人都与其他人隔离。如果一名开发人员想到一个攻击 API 的好点子并且攻击成功了(使得 API 或虚拟环境崩溃),这些工作不会影响到其他人。这里的要点是要提供一个环境,让开发人员在寻找最好的应用解决方案时可以没有负担地去做任何事情。

证明最佳的集成

许多集成测试存在的一个问题是测试在找正向的结果而不是负面的结果。开发人员希望验证以下事项。

  • API 按说明书所说的那样工作
  • API 的功能匹配该 API 必须完成的任何业务逻辑可用来进行集成测试的方法有很多,它们还会帮你找到潜在的安全问题。下面描述了最常见的技术:沙盒、创建虚拟环境、使用 mocking 以及依靠 API 虚拟化。
1. 沙盒集成测试

使用沙盒环境可以执行逼真的测试。在某些情况下,当沙盒环境在某些方面不能匹配现实环境时,你会得到假阳性的测试结果。此外,使用沙盒环境通常不能体现出 API 真正的速度指标,因为在 API 和应用程序之间有另一层软件。当多名开发人员在不符合生产环境的环境中发起请求时,速度问题会被放大。

2. 虚拟化集成测试

另一种替代方案,虚拟环境,同样提供了逼真的测试。由于虚拟环境的工作方式,它可以更大程度地模拟生产环境,所以你不会得到太多的假阳性结果。实际测试的速度通常不是一个问题,因为每个开发人员都有自己的私有环境。但是,除非你有正确的配置,否则就不能可靠地进行应用程序的速度测试或负载测试,因为虚拟环境会有偏差的结果。当使用虚拟环境时,在集成测试发生一次失败之后的恢复速度肯定比较快。

3. 使用mocking进行集成测试

一些开发人员使用 mocking 对 API 进行集成测试。一个模拟的 API 以脚本的方式接收输入并提供输出。换言之,这个 API 不是真的。模拟 API 的目的是在分离 API 的情况下测试应用程序部分。使用模拟的 API 可以在开发团队开发 API 之前就开始构建和测试应用程序。mocking 的好处是任何测试都能非常快地运行起来。此外,应用程序接收到的响应是精确且
不会变的,不像一个真正的 API,其响应可能会变化。(mock 不会执行任何真实的执行过程,所以不会有变化的响应。)但是,mock 里面的错误会在测试应用程序时制造假阳性的结果。在网上就有模拟 API 的例子。例如,你想要测试基于 REST 的应用程序,就可以使用Mocky(http://www.mocky.io/)来执行这个任务。你可以使用 Mocky 为应用程序创建一个可预期的响应,Mocky 还可以创建各种响应类型。你不仅控制了 HTTP响应,还控制了内容的响应类型和值。

4. 使用API虚拟化进行集成

沙盒环境可以做一些你平时不容易做到的事情,比如虚拟化真实的 API。很有可能你的开发排期与你所依赖的某些第三方服务的排期不一致。当发生这个问题时,公司常常只能等待所依赖的 API。当然,你可以重新开发自己的 API,但大部分公司不会这样做,并且这真的不是一个好的选项,即使你有相关资源。

虚拟化是一个创建 API 表现的过程,可用于测试或其他目的。这个表现作为一种黑盒,可用来替代真正要依靠的真实 API。这个黑盒最终会提供对真正 API 的访问,但也可以提供对作为替代者的 mock 的访问。关键是 API 虚拟化层提供了与 API 交互和集成到应用程序的一致方法,即使是在 API 还没有完成的情况下。图 10-4 展示了 API 虚拟化层的样子。

在负载情况下验证API的表现

负载测试对于安全来说很重要,因为黑客常常使 API 或应用程序过载来导致其以某种方式失败。在 API 或应用程序失败之后,黑客会使用各种途径来利用失效的软件作为入侵网络的入口。因此,知道当负载加重时你的 API 会如何退化是很重要的,因为你想要优雅地失败,并且是以一种不会给黑客打开很大入侵漏洞的方式。重要的是要记住,如果你给了足够大的负载,每个 API 都会在某个点发生失败。尽管你可能在过去看得了一些承诺,但没有任何方法可以让软件伸缩到某个点从而使其可以承受无限的负载(假设有可能可以进行这样的测试)。

任何 API 负载测试的起点都是确定如何测试。你需要获取统计数据来了解如何加载 API 以模拟一个真实的环境。为了确保 API 如宣传的那样工作,你需要确定以下各项。

  • API 每秒接收的平均请求数
  • API 每秒接收到的请求峰值
  • 端点(发起调用 API 的地点)的吞吐量分布
  • 用户或工作组的吞吐量分布

确定如何创建测试 API 的连接也是很重要的。你可能会很随便地就开始测试。但是,很重要的是要采用各种类型的连接来测试 API,以确保其能在所有的环境下表现良好。

  • 制造重复性负载(测试软件顺序使用相同的请求)
  • 模拟的流量模式(一个随机版本的重复性负载或一段 API 访问日志数据的重播)
  • 真实的流量(你有一个测试组用真实的请求访问 API)
    在建立起能在标准条件下运行的 API 之后,你需要改变这些条件来模拟黑客的攻击。例如,黑客会用特定的请求大量攻击 API,希望使其以某种方式失败。黑客还会在一次DDoS 攻击中持续增加越来越多的僵尸请求,从而能很简单地让 API 到达被破坏的边缘。这真是有创意。很重要的是要确定什么会破坏 API 并导致其失败,然后看看 API 会如何失
    败。知道 API 会如何失败将有助于你确定风险级别,这些风险每个黑客都会感兴趣,这样你就能更好地为后果做准备。

使API远离黑客

用API沙盒进行开发

使用现成的解决方案

  • AirGap
  • Sandboxie
  • Spoon.net

使用其他供应商的沙盒

考虑虚拟环境

定义虚拟环境

虚拟环境可以指代任何提供隔绝其他软件方法的软件。你可以将其想为一个容器,因为它就是。本章前几节已经描述了各种虚拟环境。但是,这一节会描述用于开发目的的那种虚拟环境,而不是作为用户来运行应用程序。
采用虚拟化的基本理由是要隔绝所有或部分开发环境,这样你就能任意操作应用程序。发现安全问题实际意味着你要用在标准的操作系统中永远不会使用的方式去体验软件(特别是没有人会连接到你所使用的网络,这样你的错误就不会影响到任何人)。
虚拟环境还能包含比沙盒更多的特性。可以创建带有特定操作系统、开发工具、应用搭建和浏览器的虚拟环境。虚拟环境可以模拟开发过程中的每个部分,这样你就能在一个虚拟环境中安装 Apache 服务器而在另一个环境中安装 IIS 服务器。这两个虚拟环境可以存在于同一台机器上而不会产生冲突。

区分虚拟环境和沙盒

虚拟环境和沙盒有许多相似点。比如保证 API 安全。大部分虚拟环境和沙盒的基本区别在于虚拟环境具有以下特征。

  • 可重复:当你将虚拟环境文件迁移到另一个系统时,那个系统可以像原来的系统一样正常运行虚拟环境。这意味着每个开发人员都可以以相同的方式看到应用程序,使用着相同的开发环境,所以在一个系统中看到的结果可以在另一个系统中重现。环境搭建时的不同会导致难以(有时候是不可能)验证安全问题的出现。
  • 可移动:根据你所使用的虚拟化软件,你可以将虚拟环境移动到任何地方。只要接收到虚拟环境文件的开发人员有正确的软件,就可以精确地重建开发项目所需的虚拟环境。
  • 可恢复:虚拟开发环境依赖一种特殊的文件,它定义了如何创建所需的仿真环境。如果你的试验导致了虚拟环境崩溃,你所需要做的就是关闭虚拟环境的这个副本并启用另一个。恢复只会花费软件加载所需的时间,可能只需要几秒钟。
  • 可重建:
    有时候你需要在你的系统上运行同一个项目的多个副本。你可以创建你所需要的任意多的虚拟环境副本。虚拟环境的特殊属性意味着你创建的每一个副本一开始都有完全相同的资源、加载的软件、环境等。因此,你系统中的每一个副本都是其他副本的复制品。

实现虚拟化

依靠应用程序虚拟化

应用程序虚拟化是一种打包封装的方案。你要创建一个专门满足这个应用程序的虚拟环境。

检查库和API的漏洞

测试能够并且应该在多个级别上进行。单元测试,即对单个代码块的测试要首先进行。开发人员要在编写完代码后不久就开始执行这类测试。接着要把应用程序各部分组合在一起进行集成测试。最后,应用程序作为一个整体与所有已完成的软件进行测试。所有这些级别的测试(以及更多测试)都要求进行安全测试,即检查意料之外的输入和确定代码是否以可接受的方式执行。在此期间,开发人员还要创建测试框架,即能自动进行测试过程的测试程序,这样测试就能更加可靠和一致。

跳出常规思维的测试:代码依赖过程。是的,代码可能是事件驱动的,或者它可能不包含状态的概念,但本质上,代码是依赖一系列步骤来完成任务的。这一系列步骤就是一个过程。在这里,过程是否完成无关紧要。

创建测试计划

每个测试场景都需要一个测试计划。虽然你想要有跳出常规思维的测试,但确实需要一些结构化的方法来执行测试。否则,测试会变得不一致和不完整。为了达到有用的目的,测试需要有条理,并且有足够的灵活性能够在你需要的时候提供更多测试的能力。

考虑目的和目标

除非开发团队定义了测试的目的(goal)和目标(objective),否则测试不会成功。简单来说,目的就是确定应用程序是否能满足公司要求的技术和业务需求。而目标则是确定应用程序在某个业务环境中能够成功地执行一些任务。例如,某个目标可能是要将新用户添加到数据库而不会产生错误、重复的用户或泄露核心信息。下面将讨论为了得到一个可用的结构,测试必须满足的目的和目标。

制订测试的替代方案:测试不是检查应用程序的唯一方法。测试的要点是验证应用程序以确定的方式运行。其他技术也能达到同样的目的。测试最常见的替代方案是代码检查、统计分析、模型检查和论证。每一种方案都可作为你提升应用程序安全性和确保应用程序可靠运行的策略的一部分。
代码检查是人工进行的检查。一个团队会审查一遍代码并验证其设计和实现满足规范的要求。采用代码检查有助于定位自动化方法可能遗漏的潜在编码问题。事实上,应用程序很有可能带着严重的缺陷通过编译并执行,而代码检查可以发现这些缺陷。你可以在网页 https://www.owasp.org/index.php/Code_Review_Introduction 上查看更多关于代码检查的信息。
统计分析的过程与代码检查一样,但它使用了自动化工具而不是人工进行检查。统计分析的优点是一致性和速度。自动化工具会以完全相同的方式检查应用程序的每一个方面,并且不会由于疲劳而产生错误。此外,自动化工具比人工作得更快。但是,自动化工具也可能遗漏那些人工几乎可以立刻发现的错误。

模型测试会验证应用程序的属性是否满足规范的要求。大部分情况下,这意味着要验证应用程序是否能提供算法等要素的解决方案。此类测试是自动化的,但需求大量的人工输入才能执行。你可以在网页 https://www7.in.tum.de/um/25/target.html 上查看更多关于模型测试的内容。

  • 1.确定目的
    • 验证和校验
    • 优先级覆盖
    • 平衡
    • 可跟踪的
    • 确定性
    1. 测试性能
  • 3.测试可用性
    1. 测试平台类型
    1. 实现测试原则
      • 使软件失败
      • 尽早测试
      • 使测试依赖上下文
      • 创建有效的测试用例
      • 定期检查测试用例
      • 使用各种测试人员
      • 执行静态和动态测试
      • 寻找缺陷群
      • 执行测试评估
      • 避免没有错误的神话
      • 结束测试过程
    1. 理解测试的局限性
      • 测试人员不是通灵大师
      • 测试不是一个决策工具
      • 用户会找到一个不能工作的环境
      • 问题的根源对于测试是不可见的

测试内部库

测试内部API

测试外部库

测试外部API

扩展测试到微服务

单独测试库和API

为库创建测试框架

为API创建测试脚本

API 支持发起调用。测试 API 甚至可以不用应用程序。你所需要的就是一段发起调用并检查响应的脚本。很多第三方产品可以执行这个任务,或者你可以创建一个简单的应用程序来手动执行测试。使用脚本可以确保你每次都得到相同的测试结果,所以使用脚本通常是最好的选择。你执行的任何测试最起码应该检查以下条件。

  • 范围
  • 类型
  • 大小
  • 字符

将测试策略扩展到微服务

开发响应策略

所有测试的焦点在于库、API 或微服务对于给定输入提供的响应。除非代码能做出适当的反应,否则应用程序就不能按原来想象的那样运行。更重要的是,黑客会寻找这种行为上的偏差来加以利用。当定义响应的时候,你必须考虑两种响应类型:直接的和模拟的。

    1. 依靠直接结果
    1. 依靠模拟结果

执行集成测试

一旦你测试了应用程序的各个元素,就可以开始将库、API 和微服务集成到应用程序中。最简单、最完整、最不复杂的集成测试方法是采用分阶段的方法,库、API 和微服务以有序的方式小规模地添加进应用程序。分阶段的集成测试可以帮你很快地定位和解决问题。
将应用程序从模拟的数据切换到真实数据时,每次只迁移一份可能一开始看起来是在浪费时间,但这个过程可以很快地定位错误,最终会节省大量的时间。集成测试的目的是创建一个能按预期工作的完整应用程序并且不包含任何安全漏洞。

开发人员会依赖很多集成测试模型。其中一些模型被设计用于使应用程序尽可能快地运行起来,但只有当没有错误发生时才能完成任务。任何开发软件的人都知道出错在所难免,所以集成测试模型只会导致问题且不能让你完整地检查安全问题。为此,下面列出了三个使用了分阶段方法的测试模型,它们可以比较好地定位安全问题。

  • 自下而上:在这种情况下,开发团队首先添加并测试较低级的功能。这个方法的优点是,对于执行如监控这种任务的应用程序来说,你可以验证你有一个稳定的基础。原始数据在测试阶段的早期就可用,这使得整个测试过程更加真实。
  • 自上而下
    使用自上而下的方法会首先测试所有高级功能,接着测试下一级的功能,直到最底层的功能。这个方法的优点是你可以从一开始就验证 UI 的功能正常且应用程序满足了用户的需求。此类测试适用于表现型的应用程序,它们的用户交互性有很高的优先级。
  • 三明治
    这是自下而上与自上而下的组合。它适用于那些需要使用一些数据源来执行大部分任务,但用户交互性又是首要考量的应用程序。例如,你可能在 CRM 应用中使用这种方法以确保在实现其他特征之前,UI 可以正确地展示来自数据库的数据。

测试与语言相关的问题

设计针对HTML问题的测试

当使用 HTML 时,你需要考虑该语言提供了基础的 UI,同时也为与用户相关的安全问题的发生提供了途径。为此,你需要确保用来展示应用程序所管理的数据的 HTML 经过了测试,以确保它能在各种浏览器上工作。为此,需要考虑以下与语言相关的问题。

  • HTML 组织得很好,且不使用大部分浏览器不支持的标签或属性。
  • 文档进行了正确的编码。
  • 文档里的任何代码要执行必需的错误处理并检查输入是否正确。
  • 当提供特定的输入时,文档的输出应该看起来是预期的样子。
  • UI 元素的选择要降低令人迷惑并导致错误输入的可能性。

设计针对CSS问题的测试

CSS 的初衷是创造一种格式化内容的方法,它并不涉及表格和其他技巧的使用。这些技巧的问题是没有标准的方法,并且这些技巧会让有特殊需求的人无法使用页面。但是,CSS已经不仅能用于格式化了。人们发现了很多方法来用 CSS 制作特效。此外,CSS 现在几乎提供了一定级别的编码功能。因此,完整地测试 CSS 变得很重要,就像应用程序的其他部分那样。CSS 有可能会隐藏安全问题。为此,在应用程序的安全测试中,你需要执行专门针对 CSS 的测试——它应该符合以下标准。

  • CSS 组织得很好。
  • 在代码中没有任何非标准元素。
  • 特效不会导致可访问性问题。
  • 颜色、字体和其他可见元素的选择要考虑到有特殊需要的人士。
  • 处理用户的特殊需要时,可以使用替代的 CSS 格式。
  • 给定一个事件或具体的用户输入,CSS 要提供一致且可重复的输出效果。

设计针对JavaScript问题的测试

JavaScript 会为应用程序提供大部分功能代码。为此,你要用与测试其他编程语言相同的方法来测试 JavaScript 代码。你需要验证对于一个给定的输入,你是否能得到一个特定的输出。你需要考虑将以下这些问题作为你的测试套件的一部分。

  • 确保代码遵循标准。
  • 用全面的输入类型来测试代码以确保它能处理错误的输入而不会崩溃。
  • 执行异步测试以确保应用程序可以处理在不确定的间隔时间后抵达的响应。
  • 创建测试组,这样你就能使用示例的断言代码(或者测试库提供的 assert() 函数)来验证大量断言。测试组可以放大使用断言进行测试的效果。
  • 验证代码是响应灵敏的。
  • 检查应用程序的行为以确保一系列的步骤可以产生期望的结果。
  • 模拟失败的条件(比如资源缺失)来确保应用程序可以很好地进行降级处理。
  • 执行任何所需的测试以确保代码不会易于遭受最近发生的黑客攻击,这些攻击可能还没有在客户端系统中得到修复。使用针对安全的分析器,比如 VeraCode(http://www.veracode.com/),能够帮你定位并修复一些 bug,这些 bug 可能会让黑客基于最近发现的安全漏洞来入侵系统

考虑攻击的本质:很重要的是要明白很多攻击看起来好像什么也没有实现,因为你并不知道攻击者想要干什么。例如,对传输层安全(Transport Layer Security,TLS)协议的攻击,要依靠Rivest Cipher 4(RC4)加密算法,这可能看起来更像是某人在尝试使系统瘫痪。如果你是一个尝试弄清楚应用程序正在发生什么的开发人员,就必须知道正在发生的攻击类型,这就是你要确保团队里有一名安全专家的原因。
在这种情况下,攻击依赖于创建带有诸如 cookie 等重复性信息的请求。发送者会在每个请求中包含 cookie,但是每个请求都是加密的,这样信息看起来就完全不一样了。只有在获取足够样例的情况下才可能破解加密并开始从受破坏的浏览器获取数据。至少有两个团队已经攻破了 RC4,足以用 TLS 从浏览器获取数据。第一种技术大约需要 2000 个小时,而第二种只需要 75 小时。很明显,除非获取的信息有很重要的价值,否则该技术可能并不值得一般的黑客去使用。尽管如此,有这样的技术存在就意味着你需要想想攻击的企图是什么。

使用第三方测试

第三方测试涉及使用外部实体来给你的应用程序执行各种测试,包括安全测试。第三方可以提供许多服务,其中有一些是非常全面的。当然,在开始进行任何测试之前,你需要知道你是可以信任相关第三方的。一旦测试开始,需要确保第三方能接收到来自你公司的适当的指令、有正确的监控级别并提供令人满意的输出。第三方可能会执行类似你会执行的测试,而且你要明白第三方的水平高于你自己的公司,否则从一开始就没必要使用第三方测试。
你可能想要依靠,至少是部分依靠第三方测试的理由有很多。其中最常见的理由是时间。然而,许多公司缺乏能力和其他资源来正确地执行一次完整的测试工作。有时候,公司会聘请第三方来保证内部测试人员的诚实,并确保内部测试人员不会遗漏任何东西。使用第三方供应商通常要遵循以下四个步骤。

  • (1) 找到你想使用的第三方测试服务。
  • (2) 制订测试计划(将第三方作为一项资源使用),精确定义第三方要如何测试软件。
  • (3) 在与第三方签约之后实施测试计划。
  • (4) 基于你从第三方收到的报告和其他输出来验证应用程序。

找到第三方测试服务

定义聘请第三方的理由

  • 质量
  • 成本
  • 培训
  • 时间
  • 专业技能

考虑测试服务的范围

聘用第三方测试商的核心优势是能够确保从所提供的服务中获得充分的价值。但是,世界上最好的测试服务也无法知道每一种可能的测试的所有信息。你需要精确定义想要完成哪种类型的测试,然后依此选择测试公司。第一步,你需要定义环境,包括以下几项。

  • 平台
  • 操作系统
  • 编程语言
  • 问题领域
  • 公司类型
  • 用户类型

不是每一个测试公司都能执行每一种测试。一旦确定了环境因素,你就需要考虑所需的测试类型。为了获得最佳的结果,很重要的是第三方测试商要提供执行你需要的所有测试的设施。以下是一些最普遍的测试类型。

  • 安全性测试
  • 功能测试
  • 自动化测试
  • 性能测试
  • 集成测试

确保第三方是合法的

面试第三方

对测试的搭建进行测试

创建测试计划

指明第三方在测试中的目的

创建书面的测试计划

枚举测试输出和报告的要求

考虑测试需求

实施测试计划

确定公司参与测试的程度

开始测试过程

执行必需的测试监控

处理意外的测试问题

使用结果报告

与第三方讨论报告输出

向公司展示报告

根据测试建议采取行动

明确定义升级周期

应用程序会不断地暴露在网络威胁之下,它所依赖的每一份代码也是如此。为了减少已知威胁带来的风险,需要定期对应用程序进行升级。以下这些升级措施会从几个方面改善应用程序,它们全都对安全性有影响。

  • 提升可用性以减少用户出错的情况
  • 修复编码错误
  • 提升速度以防止用户做出意料之外的事情
  • 修复功能性代码以减少潜在的漏洞
  • 进行必要的修复以升级第三方库、API 和微服务

制订详细的升级周期计划

寻找升级

由于用户投诉和系统中断而由支持人员收集的故障单通常会告诉你需要对你应用程序代码做什么样的升级。如果故障单没能告诉你所需的所有信息,它们至少为你提供了足够的信息去进行必要的搜索,以准确发现你所需要的升级。但是,第三方应用软件有不同的情况。除非开发者或利益相关方在调试内部代码时定位了问题,否则升级是不会发生的。查找第三方软件的相关升级并确保你有它们的完整清单是很重要的。否则你无法维持应用程序的安全状态,造成安全漏洞的未知风险也会增加。许多公司最终会因为没有意识到要进行必需的升级而遭受打击。
当使用公开库、API 和微服务的时候,检查供应商的网站可以了解到你需要集成到应用程序中的升级。许多情况下,供应商会提供 beta 版本的升级,这样你就能比较早地使用它们。花时间检查升级会造成什么影响是值得的,这样你就能知道这个升级对你的应用程序的重要性。与所有影响应用程序所使用功能的升级相比,对应用程序使用的功能没有任何影响的升级会显得没那么重要。但是你仍然应该在某个时间点进行升级,即使确定对你的应用程序没有什么影响,因为升级常常有依赖性的交互,即使是供应商自身也可能不知道(或者是没有完全测试)。

确定升级的要求

  • 确定资源的要求
  • 获取任何所需的平台变化
  • 考虑创建、测试以及发布升级所需的时间
  • 创建一份进行升级所需的人员和技能的清单
  • 定义升级对数据源潜在的影响

定义升级的临界点

升级会从许多方面影响应用程序,而不升级的风险会根据应用程序、平台和需求的不同而不同。升级的临界点会直接与不升级带来的风险成正比。当一个升级变得很关键时,它在你升级清单中的优先级就会提高。计划升级周期的一部分就涉及给升级优先级赋值,这样公司就会首先进行最高优先级的升级。否则,你无法确定升级过程是否真的会帮助公司减少风险。当处理优先级时,请考虑以下这些标准

  • 安全风险
  • 潜在的破坏
  • 潜在的错误
  • 自然事件

检查升级的问题

任何时候你对现有产品进行升级,都可能会有兼容性或其他问题发生,而直到将升级迁移到生产环境之后你才会发现它们。到那个时候,要对问题进行处理已经太晚了,只能做出响应并期望最好的结果。一个更好的方案是在测试阶段查找潜在的升级问题,这意味着要有大量的测试团队或依靠第三方测试商。但是,甚至在你进入测试阶段之前(在升级以任何方式被实施之前),也可以运用各种技巧来发现升级是否会制造问题。
以下列出了一些较为常见的技巧。

  • 查看供应商网站
  • 查看产品或供应商的论坛
  • 阅读行业杂志
  • 直接对新的第三方代码进行测试
  • 开发功能子集的测试场景
  • 获取第三方的专业建议

创建测试场景

  • 故障单内容
  • 行动项
  • 第三方代码变更
  • 环境和配置变更

实施变更

此时,你已经考虑了一次升级的所有元素。所以,这项工作的关键是要确保升级可以:

  • 按期般工作
  • 保持稳定
  • 提供适当的安全性
  • 提升可靠性
  • 创建很棒的 UI
  • 堵住任何安全漏洞

制订升级测试计划

有时候,你需要为升级编写代码。你不应该一直等待这个过程结束才开始测试。要尽可能快地开始测试,特别是当与第三方测试商合作时。你越早发现潜在的问题,修复它们所需的成本和时间花销就越少。考虑使用 mocking,这样你就能在功能可用时进行升级测试,而不用因为依赖而等待整个升级。为此,接下来开始为升级制订并实施一个测试计划。

执行所需的预测试

一旦升级过程开始,你就需要立即开始测试过程。许多人不明白贸然是什么意思,但大部分公司还没解决问题就把钱花光了。为了确保测试能够恰当地进行,你需要立刻开始测试。在升级代码的过程中,你必须执行以下这些级别的测试。

  • 单元
  • 依赖
  • 配置
  • 安全性
  • 数据连通性

执行所需的集成测试

将升级移到生产环境

一旦测试完成并且确定升级可以正常工作,你就需要将其迁移到生产环境。如果你的公司很大,尝试首先对部分用户进行升级,这样如果这些用户发现了错误,你可以在不影响整个公司的情况下修复它。重要的是要记住应用程序必须在升级时保证可靠和安全,还要满足用户要求的速度和界面的体验。否则,升级会因为用户拒绝使用而失败。他们会想要使用感觉更舒服的版本,即使那个版本可能会导致数据泄露或其他安全问题。
从安全的角度来说,聘用第三方测试商对升级进行入侵测试是有帮助的。是的,大部分人会说你应该在测试服务器上完成所有必需的测试,就绝大多数情况而言他们是正确的。但是,入侵测试不仅仅检查应用程序的错误,它还提供了对以下这些项目的完整检查。

  • 网络连接
  • 入侵检测软件
  • 入侵防御软件
  • DMZ 可靠性
  • 所有硬件需要的固件更新
  • 服务器设置
  • 所有软件所需的更新
  • 应用程序环境
  • 第三方库、API 和微服务
  • 应用程序
  • 用户进程
  • 数据库连通性

考虑更新选项

更新意味着将新的信息、特性或界面元素带给应用程序。提高应用程序准确性也属于更新。一次更新可能不会显著影响应用程序的代码,但仍然能够以多种方式影响安全性。例如,更新数据库使其包含一个字段,用于显示谁最后编辑了该记录,这就可以跟踪已经发生在系统中的错误或病毒感染的源头,从而提高安全性。更改提示以使从用户那里得到的信息更加清晰,是一种不需要任何重新编码的安全修复,特别是当这些提示存在于外部文件中时。更新的本质是使应用程序在某些方面变得更好,从而可能不再需要代码修复了。
重要的是要知道何时区分升级和更新,所以这里首先会比较这两个过程以及它们如何影响应用程序。
更新不总是能修复安全问题,就如升级有时候是多余的。为了有效利用公司资源,你需要知道何时进行一次更新以及何时确实需要进行一次升级。

更新可以分为几类。例如,你可以选择更新编程语言套件。新的套件可以提供更好的调试、经过优化的代码输出以及更快的可执行文件。这些特性没有一个会直接影响应用程序代码,但它们确实能影响应用程序的工作。只要重新编译应用程序模块就能让所有人看到它的效果。

区分升级和更新

升级和更新都会影响应用程序的安全性。但是,升级与更新在范围和目标上有很大的不同,所以区分两者的不同是很重要的。比起更新来说,大部分升级会创建重要的新功能并在较深层次上影响代码基础。事实上,许多更新完全不会触及代码。更新会执行诸如以下这些任务。

  • 更改应用程序管理的数据库。例如,一个提供目录信息的数据库可能会有一个对描述的更新,以帮助用户作出更好的选择。
  • 修改现有特性使其变得更友好或不易出错。例如,菜单输入现在可能包含快捷键,这样用户就不必去猜要使用哪些组合键。
  • 重新配置 UI 特性,使它们更好地满足用户的需求。更新可能会包含对更多语言的支持,这样用户就能用他们熟悉的语言来使用界面,而不是他们不太熟悉的第二或第三语言。
  • 包含更多特定的 UI 选项,比如将文本输入框换成单选按钮,这样用户就能作出特定的选择。每一个从应用程序中移除的元素都自然地会提升应用程序的准确度和安全性。

确定何时更新

处理库的更新

处理API和微服务的更新

接受自动更新

以下是供应商喜欢自动更新的主要理由。

  • 减少由于使用过时版本产生的安全性和可靠性问题
  • 减少支持成本
  • 较少的人工需求
  • 提升投资回报率(ROI)

更新语言套件

创建语言支持清单

获得可靠的语言专家

以下步骤会带你深入了解一些你能用于创建更好的应用程序翻译的技术,并因此确保用户真的能遵从你的引导。

  • (1) 将所有在应用程序中硬编码的字符串放置在某种类型的数据库中,通过在应用程序中的位置来标识每个字符串。
  • (2) 将每个硬编码字符串用占位符控制替换,当页面加载时你可以用正确语言的字符串填充。
  • (3) 为每一种语言创建一个单独的数据库文件并确保数据库名称能反映语言内容。
  • (4) 用翻译人员提供的翻译后的字符串填充每个语言数据库。为每一个字符串准确地使用相同的标识符,这样该标识符总是能匹配屏幕提示。
  • (5) 给应用程序添加代码以执行任务,用来自数据库的正确提示符填充占位符。
  • (6) 开始用原来的语言测试应用程序以确保提示符能正常工作。

验证与语言相关的提示符能否在应用程序中起效

确保数据以正确的格式呈现

定义语言支持测试的特殊要求

执行紧急更新

尽可能避免紧急情况

每个人在应用程序支持过程中的某个节点上都会遇到紧急情况。这是很正常的。但是提前做好计划,你可以避免大部分的紧急情况。以下是在避免紧急情况发生时需要考虑的一些问题。

  • 在发布之前对每个更新和升级执行完整的测试。
  • 只有当系统负载和能力可以支持的时候才发布更新和升级。
  • 避免在只有低级别员工在场或关键人物缺席的情况下发布更新和升级。
  • 如果可能,确保应用程序能够访问来自多种来源的所有必需的资源。
  • 制订预留生产力的计划并确保你有足够的额外能力来满足紧急的需求。
  • 假设黑客会在最糟糕的时间发起攻击并(尽可能地)提前为这个攻击做好计划。
  • 定时测试系统的安全性和可靠性。

组建快速响应团队

执行简化的测试

制订持久的更新计划

制订更新测试计划

更新测试不会遵循跟升级测试完全一样的模式,因为你在更新期间对应用程序的改变要少一些,并且可以将大部分精力聚焦于被改变的特性。此外,你可能会在一次紧急情况中进行更新,并且需要尽快实施这些更新以防止出现重大的安全漏洞。为此,你必须执行以下这些级别的测试以确保更新不会弊大于利。

  • 单元测试
  • 集成测试
  • 安全测试
    当更新用于处理紧急情况时,这三个级别的测试通常可以防止更新弊大于利。但是,你必须尽快执行以下这些级别的测试。
  • 可用性测试
  • 完整性测试
  • 访问性测试
  • 性能测试

一旦事情尘埃落定且你有足够的时间进行测试,就需要确保这次更新是完全起作用的。确保功能性的最佳方式是执行以下这些级别的测试。

  • 回归测试
  • 国际化和本地化测试
  • 一致性测试

考虑报告的需要

报告有助于记录应用程序的状态。虽然似乎代码文件里的注释或在走廊里的简单讨论就足矣,但一个接收关于应用程序的信息的正式过程真的是确保每个人都能理解应用程序状态的唯一方式。

报告的一个重要形式是积极地寻求用户反馈(或者指定具体的用户代表来确保每类用户都有表达自己愿望的机会)。你永远无法满足应用程序的每一个用户。但是,你可以合理地寻求满足大部分用户需求的方法。关键是要确保你从用户那里获得的报告确实能够代表主要的观点,而不是少数人的不满发泄。让用户快乐、高效以及受控通常是很符合应用程序开发目标的。

使用报告以做出改变

避免无用的报告

包含有用信息的报告能帮你执行特定的任务。它们提供了行动的基础。为此,能帮你增强应用程序安全性的报告通常包含以下这些主题。

  • bug
  • 速度
  • 可靠性
  • 使用
  • 数据访问

报告的类型决定你该如何根据其包含的信息采取行动。当你不会根据一份报告采取任何行动的时候,这个报告就是有问题的,需要修改,或者是你确实不需要它。报告中的信息通常会因为以下这些原因触发某些行为。

  • 模式的改变
  • 状态的改变
  • 异常事件
  • 意料之外的值

安排时间为升级和更新做出报告

使用自动生成的报告

使用定制的报告

创建一致的报告

使用报告执行特定的应用任务

创建内部报告

确定使用哪些数据源

指定报告的使用

依靠外部生成的报告

从第三方获取完整的报告

从原始数据创建报告

保持内部数据安全

提供用户反馈

获取用户反馈

确定用户反馈的可用性

跟踪当前的安全威胁

知识运用得当,将成为相当强大的工具。为了跟进可能影响应用程序的安全问题,需要跟踪当前的安全威胁。

发现安全威胁信息的来源

阅读与安全相关的专业文章

查看安全网站

获取顾问的意见

避免信息泛滥

下面总结了一些技巧,可用来避免信息泛滥并且完全避开安全威胁。

  • (1) 仔细选择安全信息源。确保选择的安全信息源是与公司需求相关的,能够以简单的方式展现你所需要的信息,并且能够恰当提供你需要的细节,而不会有过度的情况。
  • (2) 能够定位潜在的安全修复,不需要依靠应用程序的变更。在某些情况下,最好的安全信息源可以提供快速修复问题的方法,比如安装一个浏览器或其他什么的更新。
  • (3) 创建一个基于任务的方法来处理威胁。如果无法用一句话来概括一个安全威胁,那你就是没有理解它,也不知道自己是否需要做什么。用一句话总结威胁的习惯会让人受益终身。
  • (4) 丢弃喜欢用炒作手法处理安全威胁的信息源。你所需要的没别的,只有事实。当信息源开始传播炒作时,它就不在可靠了。
  • (5) 培养批判性思维的能力。有些聪明人会基于不采取行动时可能出现的最糟糕情况来规划他们的行动。考虑最糟糕场景中的威胁有助于识别出哪些威胁存在最高的风险,所以值得这么做。
  • (6) 避免昙花一现的威胁。有些威胁只在标题里出现了一次,然后你就再也没见过它了。即使对这个威胁的预测是很可怕的,但当它们无法持续太长时间,没有第二次出现在标题上时,你必须怀疑它到底会有多可怕。

为基于威胁的升级制订计划

预判不需要采取任何行动的情况评估某个具体的安全威胁会带来的潜在风险涉及以下事项。

  • 确定你是否使用了被描述为引起安全威胁的技术。
  • 考虑一下引起威胁所需的一些访问是否真的发生在你的公司身上。
  • 明确在哪种环境下你的公司会遭受攻击以及攻击何时可能发生。
  • 检查现有的防卫手段以确定它们是否足以阻挡攻击。
  • 确保当攻击发生以及你错估了黑客发起攻击的能力时有应对的策略。

决定升级还是更新

安全威胁可以改变升级或更新的意图和紧迫性。在某些情况下,需要处理以下几个方面的问题。

  • (1) 以更新的形式处理安全威胁聚焦的问题。
  • (2) 升级或更新正在支持的软件(比如防火墙或操作系统)以确保安全威胁不能从另外的方向发起攻击。
  • (3) 培训用户,避免他们执行任务的方式会提供给黑客有价值的信息或访问途径(黑客可利用其发起攻击)。
  • (4) 修改目标代码周边的所有代码,确保应用程序在发布更新之后能按升级设计的那样正常工作。
  • (5) 执行所有依赖项的升级,确保整个应用程序稳定。
  • (6) 用黑客会使用的方法进行广泛测试以验证升级后的应用程序可以继续阻挡安全威胁。不是处理每一个安全威胁都要执行这一大串步骤,但要在决定如何处理时考虑每一个步骤。你可能会发现只要一次更新就足以阻挡威胁,或者一次操作系统的升级就是你真正需要的。关键是要考虑安全威胁与你的应用程序及其支持的数据有关的各个可能的方面。

定义升级计划

当用升级来响应安全威胁时,为了在短时间内发布升级,你需要考虑创建升级和逐步测试的方法。敏捷开发技术(http://agilemethodology.org/)能帮你完成这个任务。

为基于威胁的更新制订计划

验证更新是否可解决威胁

更新
可以包括以下事项。

  • 调整已有安全软件
  • 更改宿主平台
  • 修改宿主浏览器
  • 通过用户培训或其他方法促成流程的变更
  • 通过非代码或轻量代码变动的方法来对底层应用进行更新
  • 修改依赖的软件(比如库、API 和微服务)

遗憾的是,不是每次更新都能修复问题。如果目标是要阻挡一个安全威胁,那么最佳的处理方式是首先找到正确的行动方向,因为浪费时间尝试其他方法只会延迟有效方法的采用。黑客不会一直在等待。有时候处理速度要慢下来以进行更多细节的修复,并且承认你需要更多的时间,而不是能够轻易修复好。问题的核心在于要找到黑客使用的攻击类型。例如,中间人攻击可能只需要你正确地加密
传入和传出的数据并加倍小心。此外,它还要求额外的认证和授权方法。这类攻击无法用更新轻易地修复,因为你要查找数据准备发送处理的方式,而这三个任务都是底层代码必须执行的。

确定威胁是否紧急

定义更新计划

要求来自第三方的更新

获取必需的培训

制订内部的安全培训计划

定义所需的培训

设置合理的目标

使用内部培训师

其优点如下。

  • 低成本
  • 熟悉程度高
  • 实用
  • 方便
  • 知根知底

缺点

  • 缺乏尊敬
  • 缺乏时间
  • 缺乏技能
  • 缺乏经验

监控结果

获取第三方对开发人员的培训

最常见的培训方式。

  • 内部安全培训
  • 网络学校
  • 培训中心
  • 学院和大学

指定培训的要求

为公司聘请第三方培训师

利用网络学校

依靠培训中心

利用本地的学院和大学

确保用户有安全意识

制订专门的安全培训

结合书面指南进行培训

创建并使用替代的安全提醒

进行培训有效性检查