JavaScript 安全性

Web 到 Windows 8: 安全性

Tim Kulp

 

多年前,我认为学习如何打高尔夫是个不错的想法。 我在注册学习本地一些驾驶培训课程前,从未加入过高尔夫俱乐部。 在我的第一堂课中,教师问我是否学习过同类课程或打过高尔夫。 我告诉他没有时,他说:“很好! 我们将不必担心一些阻碍您进步的旧习惯了。”

Web 开发人员从浏览器过渡到 Windows 应用商店应用程序时会沿袭一些固有习惯。 尽管 Web 开发人员可以利用现有的 JavaScript 知识,但由于新增了一些功能,因此需要转变思维方式。 安全性就是这样一个有根本差异的功能。 很多 Web 开发人员习惯将应用程序的安全性转交给服务器实现,他们给出诸如以下的理由:“何必麻烦呢? 我们可以很轻松地绕过 JavaScript。”在 Web 客户端,安全功能仅被视为提高可用性的方法,无法增强 Web 应用程序的总体安全性。

使用 Windows 8,JavaScript 通过提供保护数据、验证输入和分隔潜在恶意内容所需的工具,在确保您应用程序的总体安全性方面发挥重要作用。 在本文中,我将向您介绍如何改变在 Web 开发过程中养成的一些习惯,以便使用 HTML5、JavaScript 和 Windows 运行时的安全功能生成更安全的 Windows 应用商店应用程序。

输入验证

Web 开发人员说:JavaScript 验证只用于改进使用性能,而不会增强应用程序的安全性。

Windows 8 开发人员说:使用 HTML5 和 JavaScript 进行验证是您防止恶意内容进入应用程序的第一道防线。

对于传统 Web 应用程序来说,JavaScript 通常只是服务器的网关。 所有对数据的重要操作(如输入验证和存储)都在服务器上进行。 恶意攻击者可以在自己的浏览器上禁用 JavaScript 或直接提交精心设计的 HTTP 请求来绕过所有客户端保护机制。 在 Windows 应用商店应用程序中,开发人员在对数据进行操作前不能依赖服务器来清理用户输入,因为没有服务器。 要进行输入验证只能依靠 JavaScript 和 HTML5。

在软件安全性方面,输入验证是确保数据完整性的重要一环。 没有它,攻击者就可以将每个输入字段作为可能攻击 Windows 应用商店应用程序的攻击矢量。 在《编写安全的代码》第二版 (Microsoft Press, 2003) 中,作者 Michael Howard 和 Steve Lipner 提到: “所有输入在得到证明之前都是不可信的,”这已成为有关管理输入的名言。

您不应信任任何数据,直到证明它符合“已知有用”数据的规范。 构建应用程序时,开发人员知道来自特定字段的数据应像什么(即允许列表),或至少知道它不应该包含什么(即拒绝列表)。 进行输入验证时,尽可能始终使用允许列表以将输入限制为已知的有用数据。 通过只允许已知良好的数据,可降低缺少表述错误数据的新方式或未知方式的可能性。

限制、拒绝和净化

开发人员如何通过将输入限制为已知的有用数据来降低带给用户的风险? 他们使用图 1 中所示的三个输入验证阶段来降低恶意内容进入其应用程序的风险。

Input Validation (Image Based on Figure 4.4 from Chapter 4, “Design Guidelines for Secure Web Applications,” of “Improving Web Application Security: Threats and Countermeasures” at bit.ly/emYI5A)
图 1 输入验证(图像基于第 4 章“提高 Web 应用程序安全性: 威胁与对策”中的“安全 Web 应用程序的设计指南”的图 4.4,网址为: bit.ly/emYI5A

通过将数据限制为“已知有用”的数据开始进行输入验证。熟悉 HTML5 的 Web 开发人员可以使用对其新输入类型和属性的现有知识来限制进入 Windows 应用商店应用程序的数据。 Web 和 Windows 8 的主要区别在于 Windows 应用商店应用程序没有在后台检查输入的服务器。 限制数据必须在 HTML5 或 JavaScript 中进行。

使用 HTML5,可以轻松将每个字段限制为已知的有用数据。 为了说明本文中的示例,我使用了虚构的 Contoso Health 应用程序,它存储用户的个人健康信息。 此应用程序的“个人资料”页捕获用户的名称、电子邮件地址、体重和身高,并提供一个包含一般信息的注释字段。 作为开发人员,我通常知道每个字段的有用数据是什么样的数据:

  • 名称: 包含几个特殊字符的字母字符,最多不超过 45 个字符。 名称条件基于应用程序的目标市场:美国 市场。
  • 电子邮件地址: 输入必须采用有效的电子邮件地址格式。
  • 体重和身高: 带相关标签的数字,显示以英尺、英寸和磅为单位的数据。
  • 注释: 使用标准 Contoso HTML 编辑器的 HTML 内容。

对于“名称”输入元素,我需要限制什么字符对于字段有效,以及该值的长度是多少。 我可以使用 input 标记的两个新属性来做到这点: pattern 和 title。

pattern 是指输入的数据必须符合的正则表达式。 MSHTML(Windows 8 中用于 HTML5 应用程序的呈现引擎)验证输入字段的数据是否与正则表达式匹配。 如果用户输入不符合正则表达式模式的数据,提交表单将失败,并指导用户更正无效的字段。 例如,“名称”字段可以包含字母字符和空格,且只能包含 3-45 个字符。 以下 pattern 值支持:

<input type="text" id="txtName" name="txtName"
  pattern="^[A-Za-z ]{3,45}$" title="" />

title 用于告知户系统要求什么内容。 在本例中,诸如“名称必须包含 3-45 个字符且只能使用字母字符或空格”的语句将说明期望的模式。 没有什么比输入无效且不知道怎样的输入有效更令用户沮丧了。 请友善地向您的用户提供帮助,让他们知道允许输入哪些内容。 title 属性的作用就在于此,它是说明字段所需内容的消息。

包含可接受字符和长度的数据字段模式可能很难确定。 您可以在很多实用的在线资源中找到示例正则表达式,但是请始终咨询您所在组织的安全团队,了解是否有您必须符合的标准。 如果您所在组织没有安全团队或安全团队未设置标准,诸如 RegExLib.com 的资源可提供您可用于数据验证的正则表达式的实用库。

某些字段是特定数据类型,如数字、日期和电子邮件地址。 HTML5 将再次发挥作用,它提供很多新输入类型,如电子邮件、电话、日期、数字等。 使用这些数据输入类型,MSHTML 可检查用户输入的内容是否是有效的数据,而无需编写任何正则表达式或 JavaScript 代码。 输入元素的 type 属性处理新数据类型。 (您可以在 bit.ly/OH1xFf 上查找更多类型和它们的用法。) 例如,为了捕获“个人资料”页的电子邮件地址,我将 type 属性设置为 email,如以下示例中所示:

<input type="email" id="txtEmail" name="txtEmail" />

此字段仅当值符合有效电子邮件地址的格式时才接受它。 如果 MSHTML 未将输入识别为有效电子邮件地址,当用户尝试提交表单时将在该字段上显示验证错误。 使用 HTML5 的新输入类型将数据限制为您希望的样式,不必进行复杂的 JavaScript 验证。

一些新输入类型还允许使用新的 min 和 max 属性来进行范围限制。 例如,由于业务规则,我们应用程序中的人员身高必须介于 3 到 8 英尺。 可以对“身高”字段使用以下范围限制:

<input type="number" id="txtHeight" name="txtHeight" min="3" max="8" />

这些示例提供了使用 HTML5 input 标记限制数据的四种方法。 通过验证长度(使用模式)、格式(再次使用模式)、数据类型(使用新的输入类型)以及范围(使用最小值/最大值),您可以将数据限制为已知的有用数据。 并非所有属性和类型都在提交前提示您更正它们。 请确保使用 checkValidity 方法验证您表单的内容 (bit.ly/SgNgnA),就像在 ASP.NET 中验证 Page.IsValid 一样。 您可能想知道仅使用 JavaScript 是否可以像这样限制数据。 是的,您可以,但是使用 HTML5 属性可以减少开发人员需要管理的代码量,因为它将所有繁重工作交给了 MSHTML 引擎。

拒绝已知的坏(即拒绝列表)输入。 拒绝的一个好示例是创建无法连接到您的 Web 应用程序的 IP 地址的拒绝列表。 当您为要阻止的对象定义相对固定的范围时,拒绝列表很有用。 例如,考虑向组(如您的开发小组)发送电子邮件,然后将个别人从开发小组电子邮件列表中删除。 在此示例中,您知道要从开发小组列表中拒绝哪些电子邮件地址。 为了确保软件安全,您想将工作重点放在限制(允许列表)而非拒绝(拒绝列表)上。 请始终记住随着攻击者找到更新颖的方法来绕过软件防御机制,已知的错误数据在不断变化。 在上例中,设想有新的开发人员加入开发小组,需要审查他们是否应包含在电子邮件中。 从长远来看,与在拒绝列表中包含数千项相比,限制方式更便于管理,提供了更便于维护的列表。

有时,数据同时包含已知的有用数据和已知的错误数据。 例如,HTML 内容就包含这两种数据。 一些标记被批准显示而另一些标记则不允许显示。 剔除或禁用已知的错误数据和允许已批准的数据的过程称为“净化输入”。 Contoso Health 应用程序中的“注释”字段就是这方面的一个示例。 用户可以通过 HTML 编辑器输入 HTML 标记,但是当输入内容在应用程序中显示时只呈现某些 HTML 标记。 “净化输入”通过剔除不安全的内容和不呈现未明确批准的内容,去掉了可能的恶意数据,确保输入安全的内容。 如果您使用 innerText 而非 innerHTML 设置 HTML 元素的值(这会将 HTML 内容呈现为文本而非将它解释为 HTML),Windows 应用商店应用程序可执行净化。 (请注意如果应用程序将脚本标记的 innerText 设置为 JavaScript,则生成可执行的脚本。) JavaScript 还提供另一个有用的净化工具: toStaticHTML。

以下是“个人资料”页的 btnSave_Click 处理程序的示例代码:

function btnSave_Click(args) {
  var taintedNotes = document.getElementById("txtNotes").value;
  var sanitizedNotes = window.toStaticHTML(taintedNotes);
  document.getElementById("output").innerHTML = sanitizedNotes;
}

如果用户将该字符串输入

<strong>testing!</strong><script>alert("123! "
);</script>

txtNotes,window.toStaticHTML 方法将去掉脚本标记,只留下已批准的严格标记。 使用 toStaticHTML 剔除不在批准的安全列表(使用允许列表的另一个示例)上的所有标记以及所有未知属性。 只有已知的有用数据保留在 toStaticHTML 方法的输出中。 您可以在 bit.ly/KNnjpF 上找到已批准的标记、特性、CSS 规则和属性的完整列表。

输入验证降低了恶意代码进入系统的风险。 使用 HTML5 和 toStaticHTML,应用程序可以将输入限制为已知的有用数据并删除或禁用可能的恶意内容而无需服务器介入。

既然 Contoso Health 将获取有效数据,我们如何处理敏感数据(如医疗或财务信息)呢?

敏感数据存储

Web 开发人员说:切勿将敏感数据存储在客户端,因为没有可用的安全存储设备。

Windows 8 开发人员说: 可以通过 Windows 运行时加密敏感数据并安全地存储它。

在上一节中,Contoso Health 应用程序检索了常规个人资料信息。 随着开发的继续,业务赞助商请求获取医疗历史记录表单。 此表单捕获用户一生发生的医疗记录,如最近去看病的情况。 Web 开发的传统规则认为不应在客户端上存储诸如用户的医疗历史记录等敏感信息,因为可能泄漏数据。 在 Windows 应用商店应用程序开发中,可以使用 Windows 运行时的安全功能在本地存储敏感数据。

为了保护用户的医疗历史记录,Contoso Health 使用了 WinRT 数据保护 API。 加密不应是数据保护策略的全部(请考虑“深度防御”: 设置多层次安全体系而非单种防御方式,如仅使用加密)。 请不要忘记涉及敏感数据的其他最佳做法,如仅在需要时才访问数据和不要将敏感数据放置在缓存中。 有一个列出敏感数据的很多注意事项的有用资源,它就是 MSDN 库文章“提高 Web 应用程序安全性: 威胁与对策”(bit.ly/NuUe6w)。 尽管此文档侧重介绍 Web 开发最佳做法,它提供了很多可用于任何开发类型的基础知识。

Contoso Health 应用程序的“医疗历史记录”页包含名为 btnAddItem 的按钮。 用户单击 btnAddItem 时,应用程序对输入“医疗历史记录”表单的数据进行加密。 为了加密“医疗历史记录”信息,应用程序使用了内置的 WinRT 数据保护 API。 这个简单的加密系统使开发人员可以快速加密数据而不会带来很大的管理开销。 以 btnAddItem 单击事件的一个空事件处理程序开始。 然后 Contoso Health 收集表单信息并将它存储在 JSON 对象中。 在事件处理程序内,我添加了快速生成 JSON 对象的代码:

var healthItem = {
  "prop1": window.toStaticHTML(document.getElementById("txt1").value),
  "prop2": window.toStaticHTML(document.getElementById("txt2").value)
};

healthItem 对象表示用户输入表单的“医疗历史记录”信息。 加密 healthItem 从实例化 DataProtectionProvider 开始:

var dataProtectionProvider =
  Windows.Security.Cryptography.DataProtection.DataProtectionProvider(
  "LOCAL=user");

DataProtectionProvider 构造函数(用于加密)采用一个确定数据保护与什么关联的字符串参数。 在本例中,我要将内容加密到本地用户。 我没有将它设置为本地用户而是设置为计算机、一组 Web 凭据、一个 Active Directory 安全原则或几个其他选项。 您可以在开发人员中心主题“保护描述符”(bit.ly/QONGdG) 中找到保护描述选项的列表。 使用哪些保护描述符取决于您应用程序的要求。 此时,数据保护提供程序已可以加密数据,但是数据需要做一点更改。 因为加密算法使用缓冲区而非 JSON,因此下一步是将 healthItem 转换为缓冲区:

var buffer =
  Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary(
    JSON.stringify(healthItem),
    Windows.Security.Cryptography.BinaryStringEncoding.utf8);

CryptographicBuffer 包含很多对象和方法来处理加密和解密中使用的缓冲区。 其中最重要的方法是 convertStringToBinary,它采用一个字符串(在本例中为 JSON 对象的字符串形式)并将其转换为编码的缓冲区。 使用的编码通过 Windows.Security.Cryptography.Binary­StringEncoding 对象来设置。 在本示例中,我使用了 UTF8 作为数据的编码。 convertStringToBinary 方法基于字符串数据和指定的编码返回缓冲区。 缓冲区可用于加密且数据保护提供程序实例化后,我准备调用 protectAsync 方法来加密缓冲区:

dataProtectionProvider.protectAsync(buffer).then(
  function (encryptedBuffer) {
     SaveBufferToFile(encryptedBuffer);
});

encryptedBuffer 参数是 protectAsync 方法的输出且包含缓冲区的加密形式。 换句话说,这是可用于存储的加密数据。 在此处将 encryptedBuffer 传递给 SaveBufferToFile 方法,该方法将加密数据写入应用程序本地文件夹中的某个文件。

healthItem 的加密概括成以下三行代码:

  1. 实例化数据保护提供程序。
  2. 将数据转换为缓冲区。
  3. 调用 protectAsync 以加密数据。

解密数据也很简单。 唯一的变化是使用 DataProtectionProvider 的空构造函数以及使用 unprotectAsync 方法来替代 protectAsync 方法。 GetBufferFromFile 方法从在 SaveBufferToFile 方法中创建的文件加载 encryptedBuffer 变量:

function btnLoadItem_Click(args) {
  var dataProtectionProvider =
    Windows.Security.Cryptography.DataProtection.DataProtectionProvider();
  var encryptedBuffer = GetBufferFromFile();
  dataProtectionProvider.unprotectAsync(encryptedBuffer).then(
    function (decryptedBuffer) {
      // TODO: Work with decrypted data
    });
}

开发人员是否可以将加密用于非 WinRT JavaScript? 可以! 它是否与提供良好数据保护的三行代码一样简单? 不是! 浏览器中的加密最佳做法存在很多挑战,如如何保管加密密钥以及管理确保加密质量所需算法的文件大小。 WinRT 数据保护 API 以及 Windows.Security.Cryptography 命名空间中提供的其他加密工具可以轻松保护您的数据。 使用 Windows 运行时的安全功能,开发人员可以放心地在 Windows 应用商店应用程序中存储敏感数据,同时使加密密钥易于管理。

本地与 Web 上下文

Web 开发人员说: Web 应用程序执行与调用脚本的应用程序同样来源中的外部脚本引用。

Windows 8 开发人员说: Windows 应用商店应用程序将本地应用程序包与外部脚本引用分隔开。

Web 2.0 已培训开发人员,告知他们内容可来自您的网站、其他人的网站(通过糅合)或用户交互。 在 Web 上,内容是虚拟的,任何人都可以获取,开发人员使用脚本引用和来自第三方的 API 数据。 内容传送网络 (CDN) 和诸如 Bing 地图等在线服务转移了管理代码库或大型数据存储库的开销,使 Web 应用程序可以轻松嵌入功能。 降低开销是好事,但是这会带来一些风险。

我们假定保健软件行业中 Contoso 的一个合作伙伴是 Litware Inc,以它为例进行说明。 Litware 将发布新的 Exercise API 并为 Contoso Health 开发人员提供了使用日常操练数据源的密钥。 如果 Contoso Health 是 Web 应用程序,开发小组可以使用类似以下的脚本引用实现 Exercise API:

<script src="https://api.litware.com/devkey/exercise.js"></script>

Contoso 的开发人员相信 Litware 将提供正确内容并知道它有好的安全惯例。 不幸的是,Litware 的服务器已被一个不满的开发人员侵入,他将 exercise.js 更改为包含一个启动脚本,该脚本显示一条弹出消息:“Contoso Health 需要进行维护;请下载以下维护应用程序”。用户认为此消息是合法的,因而受骗上当下载了恶意软件。 Contoso 的开发人员感到困惑:Litware 具有严格的验证,这种安全事件是如何发生的呢?

在 Web 上,以上述方式引用的脚本使用同一站点上脚本的同一来源执行。 这意味着 exercise.js(作为 JavaScript 运行)对 DOM 树以及任何脚本对象具有不受审查的访问权限。 如前文所述,这可能导致严重的安全问题。 为了消除这种风险,Windows 8 将应用程序资源分为两种上下文,如图 2 中所示。

Local vs. Web Context Features (Mashed from “Features and Restrictions by Context” [bit.ly/NZUyWt] and “Secure Development with HTML5” [bit.ly/JOoMOS])
图 2 本地与 Web 上下文功能(来自“按上下文划分的功能和限制”[ bit.ly/NZUyWt] 和“使用 HTML5 进行安全开发”[ bit.ly/JOoMOS])

本地上下文可以访问 Windows 运行时以及应用程序包中包含的所有资源(如 HTML、脚本、CSS 和存储在应用程序状态目录中的应用程序数据),但是不能访问远程 HTML、JavaScript 或 CSS(如前文 exercise.js 示例中所述)。 Windows 8 中的顶级应用程序始终在本地上下文中运行。 在图 2 中,ms-appx:// 用于解析本地上下文中的内容。 此方案用于引用在本地上下文中运行的应用程序包中的内容。 通常在第三个斜杠 (ms-appx:///) 后面引用包的全名。 对于 Web 开发人员,此方法与使用 file:// 协议类似,其中第三个斜杠引用本地文件系统(假定 file://最终用户的计算机/ 替代 file://远程计算机/)。

Web 上下文允许开发人员通过 iframe 将远程内容引入 Windows 应用商店应用程序。 与 Web 浏览器中的 iframe 一样,限制在 iframe 中执行的内容访问它外部的资源,如 Windows 运行时和 Windows JavaScript 库的一些功能。 (您可以在 bit.ly/PoQVOj 上找到完整列表。) Web 上下文的用途是允许开发人员引用第三方 API(如 Bing 地图)或从 CDN 将库拉入自己的应用程序。

使用 http:// 或 https:// 作为 iframe 的源自动将 iframe 的内容转换为 Web 上下文。 当您使用 ms-appx 或 ms-appx-web 时,iframe 还可以是应用程序包中的资源。 iframe 中的源引用 ms-appx:// 方案时,iframe 的内容在本地上下文中运行。 这允许开发人员将应用程序包资源嵌入 iframe,同时仍可以访问本地上下文的功能(如 Windows 运行时、Windows JavaScript API 等)。 另一个可用方案是 ms-appx-web://,它允许在 Web 上下文中运行本地应用程序包内容。 当您需要在标记内嵌入远程内容时此方案很有用,如基于 Contoso Health 应用程序中的用户位置添加本地医院的 Bing 搜索结果(来自 Bing 搜索 API)。 顺便提醒一下,无论何时在 HTML5 内提及 iframe,请记住您可以通过限制在 iframe 内内容的脚本执行将沙盒属性作为您的应用程序的额外保护措施。 您可以在 bit.ly/Ppbo1a 上找到有关沙盒属性的更多信息。

图 3 显示本地和 Web 上下文中使用的各种方案以及它们的使用示例。

图 3 带上下文示例的方案

方案 内容位置 上下文 示例 何时使用
ms-appx:// 应用程序包 本地

<iframe

 src="ms-appx:///1.html"

></iframe>

将内容加载到需要访问 Windows 运行时或完整 Windows JavaScript API 的 iframe。
ms-appx-web:// 应用程序包 Web

<iframe

 src="ms-appx-web:///2.html"

></iframe>

使用作为 Windows 应用商店应用程序接口一部分的远程源中的内容,如显示映射小组件或搜索结果。
http:// 远程 Web

<iframe

 src="http://host/3.html"

></iframe>

引用远程内容,如另一服务器上的网页或脚本文件。

iframe 属于哪个上下文基于如何引用其中的内容。 换句话说,方案决定了上下文。 您可以在 bit.ly/SS711o 上找到有关 Windows 8 中使用的方案的更多信息。

还记得本节开头的 Litware 侵入情形吗? 上下文的 Windows 8 分隔将帮助将跨站点脚本攻击限制为 Web 上下文,在其中它无权访问 Windows 运行时或 Contoso Health 的应用程序数据。 在 Web 上下文中,修改本地上下文没有用。 上下文之间可以通信,但是您可以控制进行何种类型的通信。

上下文之间的通信

顶级文档如何与在 Web 上下文中运行的 iframe 通信? 使用 HTML5 的 postMessage 功能,Windows 应用商店应用程序可以在上下文之间传递数据。 这允许开发人员设置两个源如何通信的结构并只允许已知的好提供程序(再次使用允许列表)访问本地上下文。 将 iframe 与设置为 http://、https:// 或 ms-appx-web:// 的 src 属性结合使用,以引用需要在 Web 上下文中运行的页。

对于 Contoso Health 应用程序,系统从 Litware Exercise API 请求健康提示。 Contoso Health 的开发小组已生成 litwareHelper.html 页,该页用于通过 jQuery $ajax 对象与 Exercise API 通信。 由于远程资源 (exercise.js),litwareHelper.html 需要在 Web 上下文中执行,这意味着它需要在 iframe 内运行。 设置 iframe 与在任何其他 Web 应用程序中基本相同,除了引用该页的方式不同。 因为 litwareHelper.html 页是本地应用程序包的一部分但是需要在 Web 上下文中运行,您使用 ms-appx-web 来加载它:

<iframe id="litwareHelperFrame” src="ms-appx-web:///litwareHelper.html"></iframe>

开发小组将以下函数添加到本地上下文页,该页将数据请求发送到 Web 上下文页:

function btnGetFitTips_Click() {
  var msg = {
    term: document.getElementById("txtExerciseSearchTerm").value,
    itemCount: 25  }
  var msgData = JSON.stringify(msg);
  var domain = "ms-appx-web://" + document.location.host;
  try {
    var iframe = document.getElementById("litwareHelperFrame");
    iframe.contentWindow.postMessage(msgData, domain);
  }
  catch (ex) {
    document.getElementById("output").innerText = "Error has occurred!";
  }
}

receiveMsg 方法处理来自本地上下文的消息。 receiveMsg 的参数是提供给 postMessage 事件的数据(在本例中为 msgData 变量)以及消息目标、消息来源和几个其他信息片段,如图 4 中所示。

图 4 使用 receiveMsg 进行处理

function receiveMsg(e) {
  if (e.origin === "ms-appx://" + document.location.host) {
    var output = null;
    var parameters = JSON.parse(e.data);
    var url = "https://api.litware-exercise.com/data/" 
      + parameters.term +
      "/count/" + parameters.itemCount;
    var options = {
      dataType: "jsonp",
      jsonpCallback: "jsonpCallback",
      success: function (results) {
        output = JSON.stringify(results.items);
        window.parent.postMessage(output, "ms-appx://" 
        + document.location.host);
      },
      error: function (ex) {
        output = ex;
      }
    };
    $.ajax(url, options);
  }
}

receiveMsg 首先检查 postMessage 的来源。 这是至关重要的安全检查,以确保消息来自所认为的源。 请记住 e.origin 检查发送 postMessage 的域和方案,这是您检查 ms-appx(本地上下文地址)的原因。 从 Litware API 收集 JSON 数据后,应用程序使用 postMessage 命令将结果重新传回 window.parent。 注意,在 receiveMsg 中将域设置为 ms-appx。 这是消息传递的“目的地”地址并显示正在将数据返回给本地上下文。 来自 iframe 的数据需要由本地上下文中的资源使用。 开发小组添加 processResult 函数以将来自 Web 上下文的数据处理后返回给本地上下文。

function processResult(e) {
  if (e.origin === "ms-appx-web://" + document.location.host) {
    document.getElementById("output").innerText = e.data;
  }
}

再次强调一下,请始终检查消息事件的来源,以确保只处理来自已批准位置(即在允许列表中注册的位置)的数据。 请注意该来源是 Web 上下文方案: processResult 方法中的 ms-appx-web。 在方案间切换可能是一个不足之处,开发人员在调试期间可以忽略它并了解消息发送到何处。

最后,为了接收来自 Web 上下文的数据并将它返回给本地上下文页,您添加了一个针对该消息事件的事件处理程序。 在 app.onactivated 方法中,将事件侦听器添加到窗口对象:

window.addEventListener('message', processResult, false);

默认情况下分隔本地和 Web 上下文将降低意外执行 Windows 应用商店应用程序之外的源中代码的风险。 使用 postMessage,开发人员可以提供构成应用程序的外部脚本和本地脚本之间的通信通道。

Web 到 Windows 8: 针对旧习惯的新工具

Web 开发人员现在可以访问可用于构建安全的 Windows 应用商店应用程序的熟悉工具和新工具。 使用现有技术(如 HTML5 输入验证)确保输入应用程序的数据的完整性。 诸如数据保护 API(Windows 运行时的新功能)的新工具通过易于实现的严格加密保护用户的机密数据。 使用 postMessage 允许应用程序利用 Web 上的数千个 JavaScript 库和旧代码,并保护用户不受无意的代码注入的侵害。 所有这些元素相互配合,共同确保通常在 JavaScript 受忽视的重要方面: 安全性。

Windows 8 为 Web 开发人员带来反思某些旧习惯的机会。 JavaScript 对服务器不再是外观,不再只是增强可用性的手段。 JavaScript、Windows 运行时和 MSHTML 提供在 Windows 应用商店应用程序中构建安全功能所需的工具,不一定需要服务器。 作为 Web 开发人员,我们拥有很多技能,但是需要密切关注一些旧习惯并将它们转换为学习 Windows 8 新技术的机会。

Tim Kulp 是 Baltimore, Md 的 FrontierMEDEX 开发小组的负责人。有关他的情况,请访问 seccode.blogspot.com 上他的博客或 Twitter (Twitter.com/seccode),在上面他谈论代码、安全性以及 Baltimore 的美食、风景。

衷心感谢以下技术专家对本文的审阅: Scott Graham