通过项目真丝部件访问配置数据

分享于 

23分钟阅读

Web开发

  繁體

在10天内开发 Windows 8应用程序。

这些文件提供了一种方法,可以将敏感数据隔离在一个可以从应用程序中的单个位置。 在 ASP.NET 中,DPAPI等工具允许你加密配置元素,以避免暴露敏感的连接字符串和设置。 在本文中,我将展示如何构建一个小部件( 使用Project小部件结构) 来从配置文件中拉取数据。 我还将解释一些安全技巧,帮助你限制对客户端和服务器上配置文件中的数据的访问。

首先,你应该回顾项目丝绸文档,因为项目丝绸是我们的基础。 我们将构建一个工具小部件,这意味着小部件只用于处理数据,而不是提供接口。 你可以通过两种方式构建这个小部件:

  • 在服务器上动态生成脚本。 这种方法使用 ASP.NET 处理程序来生成处理配置值所必需的JavaScript。 这些值将嵌入到脚本中,所以你不需要一个服务器来检索它们。 你可以将JavaScript作为一组具有值的变量构建,或者作为整个应用程序中引用的对象。
  • 构建通过Ajax调用传递配置的服务。 这种方法可以使用项目Silk中的数据管理器make返回一个配置对象。 可以在服务器( 并返回JSON对象) 上创建配置对象,或者在客户机上创建一个JSON对象,并用数据填充它。

有了这些选项和不同的实现,你就有了许多选择。 你选择哪种技术取决于你的编码标准和开发需求。 在服务器上动态构建JavaScript允许你构建一个包含所需数据的单个组件。 通过使用 StringBuilder ( 或者类似的对象),可以构建一组包含配置文件中所有值的简单变量。 这种方法的缺点是你没有 Visual Studio ( 或者你的内部) 能够提供的好处。 不具备智能感知和IDE调试的工具,但是集成到浏览器( 和 IE 10开发者工具一样) 中的各种工具可能是你所需要的。

使用与Web服务通信的JavaScript组件是另一种解决客户端系统无法从配置文件中检索数据的方法。 这是我将在本文中描述的模型。 这种方法的负面缺点是设计复杂,但是你构建JavaScript作为 JavaScript,并且可以利用IDE的强大功能。 这种方法还为下一个开发人员生成easier-to-read代码,这对于开发团队环境中的软件开发人员来说。

从安全开始

对于任何项目,首先要考虑到你的应用程序资产的风险。 在这种情况下,资产是配置设置。 配置数据可以是良性的或者敏感的信息,具体取决于它的用途和用途。 你需要确定:

  • 客户端上需要哪些配置设置?
  • 你为什么需要他们?
  • 暴露这里数据的风险是什么?

记住,客户端计算机对于你的应用程序来说是一个非常危险的环境。 一旦数据在客户端,就可以操纵。滥用和分析,以挖掘来自它的任何恶意信息。 这将引发另一个安全口号: 永远不要在客户端存储敏感信息。 这个警告通常是引用如i 和HiddenFields这样的对象,但它同样适用于我将演示的配置小部件。

下一个安全挑战是将设置视为敏感的设置保持在配置小部件之外。 你可以通过将配置数据的请求约束到已知安全数据来做到这一点。 例如,如果你有一个名为" DocPath"的设置,将文件上传的文件夹的完整路径存储到应用程序。 Web应用程序具有写入特权的文件夹的完整路径在错误的手中有很大的有害。 而在不提供地址的情况下,像" DefaultAddress"这样的设置只提供用于事务的默认字符串地址,这不会是一个公开的问题。

最后,你需要确保请求和响应都通过约束和消毒过程来传递。 必须限制调用组件( 如jQuery小部件或者JavaScript组件)的请求,使它的成为可以识别的。经过批准的配置设置。 在服务器上,需要验证请求是否批准,同时拒绝任何无效的输入。 从配置文件检索数据后,可以对输出进行消毒以确保它不包含任何恶意内容。

设计

我们的配置管理 小部件将通过客户端代码和服务器端Web服务提供对配置文件的访问。 图 1 显示的数据流描述了小部件或者JavaScript对象如何访问配置文件。

图 1小部件或者JavaScript对象如何访问配置文件的数据流

组件或者JavaScript对象调用配置设置时,首先通过约束过程传递请求,以限制客户端从服务请求中可以请求的内容。 接下来,检查配置值是否已经填充,如果是,则将它的返回给调用者。 如果未填充,则使用datamanager方法的SendRequest 方法连接到配置Web服务。 服务将消毒我们的请求,以限制进入系统的恶意内容数量,然后检索相应的配置值。 然后验证这个值以确保配置文件没有被破坏,然后传递给代理对象并返回到客户端。 执行完客户端后,我们将值粘贴到缓存对象中以便以后使用。 finally,我们将数据返回给调用者。 简单地说,这个过程通过约束配置小部件中的内容来检查数据的各种检查。

开发

让我们从Web服务( 服务器组件) 开始。 对于本文,我将在 ASP.NET 中构建它,但是这些概念可以应用于任何服务器端开发语言。 首先创建基本的ASP.NET Web窗体项目。 你需要访问服务中的类,因此添加App_Code文件夹。 对于 non-ASP.NET 开发人员,App_Code文件夹存储将编译并可以用于Web应用程序的代码对象。 对于这个项目,我们需要一个脚本文件夹,服务文件夹和一个 Default.aspx 页面。 如果你构建一个 ASP.NET 项目,你的解决方案应该看起来像 注意,应用程序中使用的所有文件都位于我们将从。

图 2.解决方案资源管理器中的Web服务的视图

配置服务

我们的配置管理的第一部分是配置服务。 这里服务是服务器端 Web ( 或者休息,WCF,等等 ) 服务,使用两种方法提供配置信息。 第一个方法将整个配置作为单个对象返回,第二个方法返回一个配置名称值对。 为简单起见,创建一个类,该类将保存配置对象的名称值对并使用适当的属性来启用JSON序列化。 在 App_Code 文件夹中创建一个新类,并将它的称为 ConstrainedConfiguration.cs。 将图 3 中显示的代码添加到文件中。

using System.Runtime.Serialization;namespace sj.models
{
 [DataContract]
 publicclass ConstrainedConfiguration
 {
 #regionPublic Properties [DataMember]
 publicstring Name { get; set; }
 [DataMember]
 publicstring Value { get; set; }
 #endregion#regionConstructorpublic ConstrainedConfiguration()
 {
 }
 #endregion }
}
图 3。ConstrainedConfiguration类

现在让我们设置配置文件以获得一些可以使用的设置。 将下列设置和值添加到配置文件中( Web.Config 用于 ASP.NET):

<appSettings> 
 <addkey="Setting1"value="This is setting 1"/> 
 <addkey="Setting2"value="This is setting 2"/> 
 <addkey="Setting3"value="This is setting 3"/> 
 <addkey="Setting4"value="This is setting 4"/> 
 <addkey="Setting5"value="This is setting 5"/> 
 <addkey="Setting6"value="This is setting 6"/> 
 <addkey="clientSideApprovedSettings"value="Setting1,Setting2,Setting3"/> 
 </appSettings>

clientSideApprovedSettings 设置是客户端通过配置服务可以提供给客户端的设置键的列表。

准备好类并设置配置文件后,可以创建将 ConstrainedConfiguration 返回到客户端的服务。 在Services文件夹,创建 ConfigurationService.asmx,它还将在App_Code文件夹中创建 ConfigurationService.cs。 这是 ConfigurationService Web服务的代码以及我们将在它的中编写所有代码的文件。

我们的Web服务需要两种方法,一个返回整个经过批准的配置,另一个返回。 第一种方法是 GetConfiguration,如图 4所示的。 它将 ConstrainedConfiguration 对象的Collection 返回给客户端。 注意,该方法用 ScriptMethod 属性修饰,该属性将响应格式设置为 JSON。 这样序列化并返回对象为JSON而不是 XML。

[WebMethod] 
[ScriptMethod(ResponseFormat=ResponseFormat.Json)] public sj.models.ConstrainedConfiguration[] GetConfiguration() { 
 var settings = System.Configuration.ConfigurationManager.AppSettings; 
 string[] approvedSettings = settings[_approvedSettingsName].Split(','); 
 if (approvedSettings.Length >0) 
 { 
 sj.models.ConstrainedConfiguration[] ccs = 
 new sj.models.ConstrainedConfiguration[approvedSettings.Length]; 
 for(int i = 0; i < approvedSettings.Length; i++) { 
 sj.models.ConstrainedConfiguration cc = 
 new sj.models.ConstrainedConfiguration(); 
 cc.Name = Microsoft.Security.Application.Encoder.JavaScriptEncode(approvedSettings[i]); 
 cc.Value = Microsoft.Security.Application.Encoder.JavaScriptEncode(settings[approvedSettings[i]]); 
 ccs[i] = cc; 
 } 
 return ccs; 
 } 
 else 
 thrownew Exception("Approved settings must be supplied."); 
 } 
}
图 4 GetConfiguration方法

首先,我们加载设置来访问它们,而不需要每次都指定整个名称空间。 接下来,我们加载 clientSideApprovedSettings 并用逗号delimitating分割值。 为了确保我们不在 clientSideApprovedSettings 中假定值,我们检查字段名的array 是否在它的中值中。 否则,我们将抛出一个异常,该异常被client-端代码捕获,调用该服务。 如果有值,我们用经过批准的字段的长度作为新的长度来实例化 ConstrainedConfigurations的值。 我们遍历每个经过批准的设置并创建一个 ConstrainedConfiguration 对象,存储来自应用程序设置的NAME 和值。 请注意,我使用 AntiXSS库中的JavaScriptEncoder来编码设置。 这样做将对输出进行编码,以限制进入应用程序的恶意代码。 你可能会问为什么我没有使用HTML编码。 答案是,数据不会在HTML上下文中使用。 我们将把这个数据作为一个代码对象存储在JavaScript中。 编码时,始终注意你将如何使用数据。 不要对未使用的上下文进行编码。

第二种方法,如图1 所示,以为代价,非常相似,但它限制了检索到的单个。

[WebMethod] 
[ScriptMethod(ResponseFormat = ResponseFormat.Json)] public sj.models.ConstrainedConfiguration GetConfigurationSetting(int settingId) 
{ 
 var settings = 
 System.Configuration.ConfigurationManager.AppSettings; 
 string[] approvedFields = 
 settings[_approvedSettingsName].Split(','); 
 sj.models.ConstrainedConfiguration cc = 
 new sj.models.ConstrainedConfiguration(); 
 if (approvedFields.Contains(settings.Keys[settingId].ToLower())) 
{ 
 cc.Name = Microsoft.Security.Application.Encoder.JavaScriptEncode(settings.Keys[settingId]); 
 cc.Value = Microsoft.Security.Application.Encoder.JavaScriptEncode(settings[settingId]); 
} 
 return cc; 
}
图 5 GetConfigurationSetting

Configuration Manager

我们的服务器端组件已经就绪。 现在我们需要构建客户端组件。 在ConfigurationManager对象和 DataStore 对象的概念模型中,我使用了Project和对象的概念模型。 ConfigurationManager 对象负责加载配置信息,ConfigurationStore 是保存信息的数据存储。 让我们从 ConfigurationStore 开始,因为它几乎镜像了来自项目丝绸的数据存储。

首先声明将保存我们的配置信息的对象:

_config: {},

这里对象将存储通过 ConfigurationManager 检索的配置设置。 接下来,需要两种方法从 _config 对象获取数据:

get: function (token) { 
 returnthis._config[token]; 
 }, 
 getConfiguration: function () { 
 returnthis._config; 
 },

get方法返回一个特定的配置设置,而 GetConfiguration 方法返回配置 Collection。 获取方法一起设置方法:

set: function (token, value) { 
 // Store the data this._config[token] = value; 
 }, 
 clear: function (token) { 
 this._config[token] = undefined; 
 }, 
 clearAll: function () { 
 this._config = {}; 
 },

最后,需要添加hasData方法来检查_config对象中的对象数并返回一个布尔值,指示长度是否大于0.

hasData: function () { 
 if ({}!= this._config) 
 returntrue; 
 else 
 returnfalse; 
 }

ConfigurationManager 对象填充存储在 ConfigurationStore 中的配置对象。 它有两种方法:loadConfigurationloadConfigurationSetttingloadConfiguration 方法( 请参见图 6 ) 在 ConfigurationStore 中填充整个配置。 这里方法提供了一个回调参数,可以在运行在单个页面上的应用程序中使用,如HTML5应用程序。

loadConfiguration: function (callback) { 
 ///<summary>Returns the populated configuration object</summary> ///<param type="object">callback to execute after the configuration has returned to the client</param> var that = this; 
 if (!sj.configurationStore.hasData()) { 
 sj.dataManager.sendRequest({ 
 url: "/services/ConfigurationService.asmx/GetConfiguration", 
 type: "POST", 
 dataType: "json", 
 contentType: "application/json; charset=utf-8", 
 success: function (results) { 
 $.each(results.d, function (i, element) { 
 sj.configurationStore.set(element.Name, element.Value); 
 }); 
 if (undefined!== callback) 
 callback(sj.configurationStore.getConfiguration()); 
 }, 
 error: function (ex) { 
 throw ex.responseText; 
 } 
 }); 
 } 
 else { 
 if (undefined!== callback) 
 callback(sj.configurationStore.getConfiguration()); 
 } 
 },
图 6.loadConfiguration方法

首先,我们把这个 this一个变量,称为 。 这个步骤在 Silk Silk common makes makes this,即使在的意义改变时,也可以参考( 小部件)。 例如如果需要在Ajax调用的错误函数中引用小部件的值,那么就不能使用这里插件来引用小部件。 在错误函数中,是Ajax调用而不是小部件。 尽管在以后的函数中不使用 ,但如果需要的话,我们将在这里进行后续的实现。

为了利用已经加载到内存中的配置数据,我们检查 sj.configurationStore.hasData() 是否存在当前配置中的任何内容。 在 hasData 对象的基础上,你可以检查_config对象是否为空对象,但是你可以增强这里方法以包含所需的参数计数,检查每个设置的值,并检查值是否正确。 使用这里代码作为你自己项目的跳板并相应地进行定制。 如果配置了配置对象,我们将从 ConfigurationStore 中返回配置对象。 如果未填充配置对象,则通过传递配置对象来填充并执行回调参数。 使用回调参数,你可以在配置对象加载后对它的进行操作。

有时,你不需要所有配置信息。 有时,你只需要一个值或者两个页面。 考虑多个页面接口( 与单独的ASPX文件中的页类似,如 JQuery Mobile 页中的页),在这里你不需要每页的配置设置。 在需要时,可以使用 loadConfigurationSettting 方法只获取需要的信息,如图 7所示。

loadConfigurationSetting: function (options) { 
 var that = this; 
 var currentOptions = $.extend({}, options, 
 that._defaultLoadConfigSettingOptions); 
 var _config = sj.configurationStore.getConfiguration() 
 if (currentOptions.useCache && sj.configurationStore.hasData()) { 
 var _hasSetting = false; 
 for (var property in _config) { 
 if (options.name == property) { 
 if (undefined!== options.callback) 
 options.callback({ "Name": property, 
 "Value": _config[property] }); 
 _hasSetting = true; 
 } 
 } 
 if (_hasSetting) 
 return; 
 else { 
 this._populateSetting(options); 
 } 
 } 
 else { 
 this._populateSetting(options); 
 } 
 },
图 7.loadConfigurationSetting方法

注意,使用提供给方法和 _defaultLoadConfigSettingOptions 变量的选项上的$.extend jQuery 函数创建了 currentOptions。 这样可以确保提供了所有必需的选项属性。 然后检查是否在 ConfigurationStore 中加载了值。 如果是,我们将从返回值如果 useCache 被设置为 true。 提供 useCache 选项允许你强制刷新数据,如果你想。 配置数据可以能不会经常改变,所以这是不可以能的,但是它仍然可以用。 如果试图从现有的ConfigurationStore 中提取,并且找不到值,我们将调用 _populateSetting 方法( 如果 ConfigurationStore 是空的,或者我们将 seCache 设置为 false,也会调用)。 图5 中显示的populateSetting 方法是一个非常简单的Ajax调用,它使用了。

_populateSetting: function (options) { 
 sj.dataManager.sendRequest({ 
 url: "/services/ConfigurationService.asmx/GetConfigurationSetting", 
 type: "POST", 
 dataType: "json", 
 data: "{keyName:"" + options.name + ""}", 
 contentType: "application/json; charset=utf-8", 
 success: function (results) { 
 sj.configurationStore.set(results.d.Name, results.d.Value); 
 if (undefined!== options.callback) 
 options.callback({ "Name": results.d.Name, 
 "Value": results.d.Value }); 
 }, 
 error: function (ex) { 
 throw ex.responseText; 
 } 
 }); 
 }
图 8.populateSetting方法

将这些全部集成起来,我们就可以为我们的项目创建 sj.config.js 脚本文件。 有几个使用这个小部件和完整代码的示例可以在 http://code.msdn.microsoft.com/Script-Junkie-Configuration-543ece24 找到。

已经批准设置的配置小部件

在本文中,我使用项目丝丝设计设置一个小部件来从应用程序的服务器端拉出配置数据。 使用这个小部件和Web服务,可以在客户端和服务器之间创建桥,允许HTML5应用程序访问服务器组件。 尽管我使用了这种方法来访问配置数据,但是你可以将它调整到客户端。 考虑服务器上客户端可能需要访问的其他资源,例如文件流。服务器元数据等。 开始实验时,请始终记住不要向客户端提供敏感信息,以验证应用程序外部的数据。 本文介绍了一种基于的项目设计方案,结合了一些风险管理,从项目丝绸的设计中,可以构建出一个JavaScript小部件,使应用程序配置。

这篇文章是 Tim Kulp写的。 过去十年Tim一直在使用 JavaScript。ASP.NET 和 C# 构建web应用程序。 现在Tim带领FrontierMEDEX开发在线医疗和安全智能工具开发团队。 Tim是一个CISSP和 CEH,专门用于安全软件开发。

查找 Tim:


数据  proj  acc  wid  Widget