一般来说,Internet 应用程序应该被限制直接访问重要系统资源的能力,以防止恶意的破坏。默认设置下,HTML 和客户端脚本语言都不能够访问关键的系统资源。 因为 Windows Presentation Foundation(WPF) 浏览器宿主应用程序可以从浏览器启动,它们也应遵循一组相似的限制。为了实施这些限制,WPF 同时依赖代码访问安全性 (CAS:Code Access Security) 和 ClickOnce (请参阅 Windows Presentation Foundation Security Strategy - Platform Security)。 默认设置下,浏览器宿主应用程序总是请求 ”Internet 区域“ CAS 组的权限,而不管它们是从 Internet、 本地 Intranet 或本地计算机启动的。 以任何小于完整权限集的权限运行的应用程序都被称为运行在部分信任安全环境中的应用程序。
WPF 提供了多种支持以确保尽可能多的功能能够安全的用于部分信任环境,并且与CAS一道为部分信任环境编程提供了额外的支持。
本主题包含以下各节:
部分信任环境下支持的WPF特性
下表列出了可以在Internet 区域权限集的限制下安全使用的WPF高级特性。
表 1:在部分信任环境下可以安全使用的 WPF 功能
功能区域 | 功能 |
---|---|
常规 | 浏览器窗口 源站点访问 IsolatedStorage(隔离存储:512KB 限制) UIAutomation 提供程序 命令 输入法 (IME) Tablet Stylus and Ink 使用鼠标捕获和移动事件的模拟拖 / 放 OpenFileDialog XAML 反序列化 (通过 XamlReader.Load) |
Web 集成 | 浏览器下载对话框 用户启动的顶级导航 mailto:links 统一资源标识符参数 HTTPWebRequest 内嵌于IFRAME的WPF 内容 承载相同站点的 HTML 页 Web Services(ASMX) |
视觉效果 | 2D和3D 动画 媒体 (源站点和跨域) 图像 / 音频 / 视频 图像编码 |
阅读 | FlowDocuments XPS 文档 嵌入式及系统字体 CFF和TrueType 字体 |
编辑 | 拼写检查 RichTextBox 纯文本和Ink剪贴板支持 用户启动的粘贴 复制选定的内容 |
控件 | 常用控件 |
此表粗略概括了 WPF 功能, 有关更多详细信息请参见SDK文档,其中描述了WPF中每项功能所需要的权限。在部分信任环境中,以下WPF功能有特殊的注意事项:
- XAML (请参阅 XAML 概述).
- 弹出窗口 (请参阅 System.Windows.Controls.Primitives.Popup).
- 拖放 (请参阅 拖/放概述).
- 剪贴板 (请参阅 System.Windows.Clipboard).
- 图像处理 (请参阅 System.Windows.Controls.Image).
- 打开文件对话框 (请参阅 Microsoft.Win32.OpenFileDialog).
下表概括了在Internet 区域权限集限制内不能安全运行的 WPF 功能。
表 2:在部分信任环境中不安全的WPF 功能
功能区域 | 功能 |
---|---|
常规 | 窗口 (应用程序定义的窗口和对话框) SaveFileDialog 文件系统 注册表访问 拖/放 XAML 序列化 (通过 XamlWriter.Save) UIAutomation 客户端 源窗口访问 (HwndHost) 完全语音支持 Windows Forms 互操作性 |
Web 集成 | Web Services( Windows Communication Foundation) 脚本 文档对象模型(DOM) |
视觉效果 | 位图效果 |
编辑 | RTF 格式剪贴板 完全 XAML 支持 |
部分信任环境下的编程
对于 XBAP 应用程序(WPF Browser Application),超出默认的 Internet 区域权限集的代码将被CAS检测 到,并且将引发安全异常导致应用程序终止。 在保护用户的同时,这种方式却并没有提供最佳的用户体验。
一般而言,可能会超过所允许的权限的代码很可能是那些在独立的应用程序和浏览器宿主应用程序之间共享的代码。CAS 和 WPF 提供几种技术来处理这种情况。
使用CAS检测权限
在某些情况下,库程序集中的共享代码可能同时用于独立的应用程序和 XBAP应用。 在这些情况下,代码可能执行需要比应用程序授予的权限集更多权限的功能。通过使用 Microsoft NET Framework 3.0的安全机制,应用程序可以检测它是否拥有特定的权限。特别的,它可以通过在所需的权限实例上调用 Demand 方法来测试它是否拥有特定的权限。在下面的示例代码中,将展示如何通过代码查询是否能将文件保存到本地磁盘:
{
...
public static void Save()
if( IsPermissionGranted(new FileIOPermission(FileIOPermissionAccess.Write, @"c: ewfile.txt") ) ) {
// Write to local disk
using (FileStream stream = File.Create(@"c: ewfile.txt"))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.WriteLine("I can write to local disk.");
}
}
else
{
MessageBox.Show("I can't write to local disk.");
}
}
// Detect whether or not this application has the requested permission
bool IsPermissionGranted(CodeAccessPermission requestedPermission)
{
try
{
// Try and get this permission
requestedPermission.Demand();
return true;
}
catch
{
return false;
}
}
}
如果应用程序没有所需的权限, Demand调用将引发安全异常。否则,权限将被授予。 IsPermissionGranted 方法 封装此行为并相应返回 true 或 false。
优雅的功能降级
对于可以从不同的区域执行的代码,关注的是能够检测代码是否具有所需的权限。虽然检测区域是一种解决方法,但如果可能的话,最好能够为用户提供一种替代方法。 例如,完全信任的应用程序通常能让用户在任何想要的位置上创建文件,而部分信任的应用程序只能在独立存储区中创建文件。 如果创建文件的代码位于共享的程序集 (.dll)中,而该程序集被完全信任 (独立的应用程序) 和部分信任 (浏览器的宿主应用程序)所共享,并且这两种应用程序都希望用户能够创建文件,那么共享代码在适当的位置创建一个文件之前应该检测它是在部分信任环境中运行还是在完全信任环境中运行。以下的代码演示了这两种情况:
{
public static void Save()
{
if ( IsPermissionGranted( new FileIOPermission(FileIOPermissionAccess.Write, @" c: ewfile.txt " ) ) ) {
// Write to local disk
using (FileStream stream = File.Create( @" c: ewfile.txt " ))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.WriteLine( " I can write to local disk. " );
}
}
else
{
// Persist application-scope property to
// isolated storage
IsolatedStorageFile storage =
IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream =
new IsolatedStorageFileStream(
" newfile.txt " , FileMode.Create, storage))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.WriteLine(
" I can write to Isolated Storage " );
}
}
}
// Detect whether or not this application has the requested permission
bool IsPermissionGranted(CodeAccessPermission requestedPermission)
{
try
{
// Try and get this permission
requestedPermission.Demand();
return true ;
}
catch
{
return false ;
}
}
}
在很多情况下,您应该能够找到一个部分信任环境下的替代方法。在一个受控环境中(例如Intranet),自定义的托管程序集可以安装到客户端的全局程序集缓存 (GAC)中。 这些共享库可以执行需要完全信任的代码,并且通过使用 AllowPartiallyTrustedCallersAttribute (请参阅 Windows Presentation Foundation Security 和 Windows Presentation Foundation Security Strategy - Platform Security 获得详细信息)让只允许部分信任的应用程序所引用。
浏览器主机检测
当您需要在每权限基础上进行检查时,使用 CAS 来检查权限是一种合适的技术。 然而,这种技术依赖于将捕获异常作为正常处理流程的一部分,通常并不推荐这么做,并且可能会有性能方面的问题。如果您的 XAML 浏览器应用程序 (XBAP) 只运行在 Internet 区域沙箱中,那么您可以使用 System.Windows.Interop.BrowserInteropHelper.IsBrowserHosted ,对于 XAML 浏览器应用程序 (XBAPs)它返回true。以下是代码示例:
{
using System;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Navigation;
public class TaskLauncher : PageFunction < TaskContext >
{
TaskData taskData = new TaskData();
protected override void Start()
{
// Retain instance (and state) in navigation history until task is complete
this .KeepAlive = true ;
this .WindowTitle = " Task Launcher " ;
// Is this assembly running in a browser-hosted application (XBAP)?
if (BrowserInteropHelper.IsBrowserHosted)
{
// If so, use browser-style UI
UseTaskPageUI();
}
else
{
// If not, use window-style UI
UseTaskDialogBoxUI();
}
}
#region Task Page UI
void UseTaskPageUI()
{
// Launch the task
TaskPage taskPage = new TaskPage( this .taskData);
taskPage.Return += new ReturnEventHandler < TaskResult > (taskPage_Return);
this .NavigationService.Navigate(taskPage);
}
void taskPage_Return( object sender, ReturnEventArgs < TaskResult > e)
{
// Task was completed (finished or canceled), return TaskResult and TaskData
OnReturn( new ReturnEventArgs < TaskContext > ( new TaskContext(e.Result, this .taskData)));
}
#endregion
#region Task Dialog Box UI
void UseTaskDialogBoxUI()
{
// Create and show dialog box
TaskDialog taskDialog = new TaskDialog( this .taskData);
taskDialog.Owner = Application.Current.MainWindow;
bool dialogResult = ( bool )taskDialog.ShowDialog();
TaskResult taskResult = (dialogResult == true ? TaskResult.Finished : TaskResult.Canceled);
// Return results
OnReturn( new ReturnEventArgs < TaskContext > ( new TaskContext(taskResult, this .taskData)));
}
#endregion
}
}
注意: |
---|
IsBrowserHosted 只判断应用程序是否在浏览器中运行,而不管应用程序是以哪一组权限在运行。 |
管理权限
默认情况下,XBAP应用程序运行于部分信任环境 (默认的 Internet 区域权限集)。 但是根据应用程序的要求,它可以更改权限集而不再使用默认值。 例如,如果一个 XBAP是从本地Intranet启动的,那么它就可以充分利用增加的权限集下, 如下表3所示。
表 3: LocalIntranet 和 Internet 权限
权限 | 属性 | LocalIntranet | Internet |
---|---|---|---|
DNS | 访问 DNS 服务器 | 是 | 否 |
环境变量 | 读取 | 是 | 否 |
文件对话框 | 打开 | 是 | 是 |
文件对话框 | 不受限制 | 是 | 否 |
独立存储 | 由用户的程序集隔离 | 是 | 否 |
独立存储 | 未知的隔离 | 是 | 是 |
独立存储 | 无限制的用户配额 | 是 | 否 |
媒体 | 安全的音频、 视频和图像 | 是 | 是 |
打印 | 默认打印 | 是 | 否 |
打印 | 安全打印 | 是 | 是 |
反射 | 发出(Emit) | 是 | 否 |
安全 | 托管代码执行 | 是 | 是 |
安全 | 断言授予的权限 | 是 | 否 |
用户界面 | 不受限制 | 是 | 否 |
用户界面 | 安全的顶级窗口 | 是 | 是 |
用户界面 | 拥有剪贴板 | 是 | 是 |
Web 浏览器 | 到 HTML的安全框架导航 | 是 | 是 |
如果您的 XBAP 需要完全信任,可以使用相同的工具来增加所请求的权限,尽管一个 XBAP应用程序只有被安装到本地计算机并且从本地计算机上启动时才能得到完全信任。这这意味着你失去了在将XBAP发布到 Web 服务器时所获得的自动更新支持。