WPF 部分信任安全

通常,应该限制 Internet 应用程序直接访问关键系统资源,防止恶意损坏。 默认情况下,HTML 和客户端脚本语言无法访问关键系统资源。 因为以 Windows Presentation Foundation (WPF) 浏览器为宿主的应用程序可以从该浏览器中启动,所以它们应该符合一组类似的限制。 为了实施这些限制,WPF 同时依赖于 Code Access Security (CAS) 和 ClickOnce(请参见 WPF 安全策略 — 平台安全性)。 默认情况下,以浏览器为宿主的应用程序请求 Internet 区域 CAS 权限集,而无论它们是从 Internet、本地 Intranet 还是本地计算机中启动的。 如果应用程序的运行权限小于完全权限集,那么就说该应用程序正在部分信任环境下运行。

WPF 提供了各种各样的支持,确保可以在部分信任环境中安全地使用尽可能多的功能;还与 CAS 一起为部分信任编程提供了附加支持。

本主题包含以下各节:

  • WPF 功能部分信任支持

  • 部分信任编程

  • 管理权限

WPF 功能部分信任支持

下表列出了可以在 Internet 区域权限集限制范围内安全使用的 Windows Presentation Foundation (WPF) 高级功能。

表 1:在部分信任环境中安全的 WPF 功能

功能区域

功能

常规

浏览器窗口

源站点访问

IsolatedStorage(512KB 限制)

UIAutomation 提供程序

命令

输入法编辑器 (IME)

触笔和墨迹

使用 Mouse Capture 和 Move 事件模拟的拖放

OpenFileDialog

XAML 反序列化(通过 XamlReader.Load)

Web 集成

“浏览器下载”对话框

顶级用户启动的导航

mailto:links

统一资源标识符参数

HTTPWebRequest

IFRAME 中承载的 WPF 内容

使用框架承载同一站点 HTML 页

使用 WebBrowser 承载同一站点 HTML 页

Web Services (ASMX)

Web Services(使用 Windows Communication Foundation)

脚本

文档对象模型

视觉效果

二维和三维

动画

媒体(源站点和跨域)

图像处理/音频/视频

阅读

FlowDocuments

XPS 文档

嵌入式字体与系统字体

CFF 字体与 TrueType 字体

编辑

拼写检查

RichTextBox

纯文本和墨迹剪贴板支持

用户启动的粘贴

复制选定内容

控件

常规控件

此表包括了高级 WPF 功能。 有关更多详细信息,请参见 Windows Software Development Kit (SDK) 文档,其中介绍了 WPF 中的每个成员所需的权限。 此外,以下功能含有关于部分信任执行的详细信息,其中包括特殊注意事项。

下表概括了不能在 Internet 区域权限集限制范围内安全运行的 WPF 功能。

表 2:在部分信任环境中不安全的 WPF 功能

功能区域

功能

常规

窗口(应用程序定义的窗口和对话框)

SaveFileDialog

文件系统

注册表访问

拖放

XAML 序列化(通过 XamlWriter.Save)

UIAutomation 客户端

源窗口访问 (HwndHost)

完全语音支持

Windows 窗体互操作性

视觉效果

位图效果

图像编码

编辑

RTF 格式剪贴板

完全 XAML 支持

部分信任编程

对于 XBAP 应用程序,超出默认权限集的代码将有不同的行为,具体情况视安全区域而定。 在某些情况下,用户将在尝试安装它时收到警告。 用户可以选择继续或取消安装。 下表描述每个安全区域的应用程序的行为,以及您为了使应用程序接收完全信任而必须执行的操作。

安全区域

行为

获取完全信任

本地计算机

自动完全信任

无需执行任何操作。

Intranet 和可信站点

提示完全信任

使用证书对 XBAP 进行签名,以便用户在提示中看到源。

Internet

失败,并显示“未授予信任”

使用证书对 XBAP 进行签名。

注意注意

上表中描述的行为针对的是不遵循 ClickOnce 受信任部署模型的完全信任的 XBAP。

通常,超出允许权限的代码可能是在独立应用程序和以浏览器为宿主的应用程序之间共享的公用代码。 CAS 和 WPF 提供了几个用于管理此方案的技巧。

使用 CAS 检测权限

在一些情况下,独立应用程序和 XBAPs 可能同时使用库程序集中的共享代码。 这时,代码执行的功能所需要的权限可能超出应用程序的授权权限集允许的权限。 通过使用 Microsoft .NET Framework 安全性,应用程序可以检测它是否具有某个权限。 具体来说,它可以通过在所需权限的实例上调用 Demand 方法来测试它是否具有特定权限。 以下示例对此进行了演示,该示例中的代码查询它是否能够将文件保存到本地磁盘:


Imports System.IO ' File, FileStream, StreamWriter
Imports System.IO.IsolatedStorage ' IsolatedStorageFile
Imports System.Security ' CodeAccesPermission, IsolatedStorageFileStream
Imports System.Security.Permissions ' FileIOPermission, FileIOPermissionAccess
Imports System.Windows ' MessageBox

Namespace SDKSample
    Public Class FileHandling
        Public Sub Save()
            If IsPermissionGranted(New FileIOPermission(FileIOPermissionAccess.Write, "c:\newfile.txt")) Then
                ' Write to local disk
                Using stream As FileStream = File.Create("c:\newfile.txt")
                Using writer As New StreamWriter(stream)
                    writer.WriteLine("I can write to local disk.")
                End Using
                End Using
            Else
                MessageBox.Show("I can't write to local disk.")
            End If
        End Sub

        ' Detect whether or not this application has the requested permission
        Private Function IsPermissionGranted(ByVal requestedPermission As CodeAccessPermission) As Boolean
            Try
                ' Try and get this permission
                requestedPermission.Demand()
                Return True
            Catch
                Return False
            End Try
        End Function



...


    End Class
End Namespace
using System.IO; // File, FileStream, StreamWriter
using System.IO.IsolatedStorage; // IsolatedStorageFile
using System.Security; // CodeAccesPermission, IsolatedStorageFileStream
using System.Security.Permissions; // FileIOPermission, FileIOPermissionAccess
using System.Windows; // MessageBox

namespace SDKSample
{
    public class FileHandling
    {
        public void Save()
        {
            if (IsPermissionGranted(new FileIOPermission(FileIOPermissionAccess.Write, @"c:\newfile.txt")))
            {
                // Write to local disk
                using (FileStream stream = File.Create(@"c:\newfile.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。

功能下降

对于可以从不同区域执行的代码而言,能够检测代码是否具有完成所需操作的权限是很有意义的。 能够检测区域固然不错,但如果能够为用户提供替代方法,则要好得多。 例如,完全信任应用程序通常使用户能够在他们希望的任何地方创建文件,而部分信任应用程序只能在独立存储中创建文件。 如果用于创建文件的代码存在于由完全信任(独立)应用程序和部分信任(以浏览器为宿主)应用程序共享的程序集中,并且这两个应用程序都希望用户能够创建文件,则共享代码应该首先检测它是在部分信任环境还是完全信任环境中运行,然后才能在适当的位置创建文件。 下面的代码对这两种情况进行了演示。


Imports System.IO ' File, FileStream, StreamWriter
Imports System.IO.IsolatedStorage ' IsolatedStorageFile
Imports System.Security ' CodeAccesPermission
Imports System.Security.Permissions ' FileIOPermission, FileIOPermissionAccess
Imports System.Windows ' MessageBox

Namespace SDKSample
    Public Class FileHandlingGraceful
        Public Sub Save()
            If IsPermissionGranted(New FileIOPermission(FileIOPermissionAccess.Write, "c:\newfile.txt")) Then
                ' Write to local disk
                Using stream As FileStream = File.Create("c:\newfile.txt")
                Using writer As New StreamWriter(stream)
                    writer.WriteLine("I can write to local disk.")
                End Using
                End Using
            Else
                ' Persist application-scope property to 
                ' isolated storage
                Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()
                Using stream As New IsolatedStorageFileStream("newfile.txt", FileMode.Create, storage)
                Using writer As New StreamWriter(stream)
                    writer.WriteLine("I can write to Isolated Storage")
                End Using
                End Using
            End If
        End Sub

        ' Detect whether or not this application has the requested permission
        Private Function IsPermissionGranted(ByVal requestedPermission As CodeAccessPermission) As Boolean
            Try
                ' Try and get this permission
                requestedPermission.Demand()
                Return True
            Catch
                Return False
            End Try
        End Function



...


    End Class
End Namespace
using System.IO; // File, FileStream, StreamWriter
using System.IO.IsolatedStorage; // IsolatedStorageFile
using System.Security; // CodeAccesPermission
using System.Security.Permissions; // FileIOPermission, FileIOPermissionAccess
using System.Windows; // MessageBox

namespace SDKSample
{
    public class FileHandlingGraceful
    {
        public void Save()
        {
            if (IsPermissionGranted(new FileIOPermission(FileIOPermissionAccess.Write, @"c:\newfile.txt")))
            {
                // Write to local disk
                using (FileStream stream = File.Create(@"c:\newfile.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)中,可以将自定义托管框架安装到整个客户端群的 global assembly cache (GAC) 中。 这些库可以执行需要完全信任的代码,并且可以通过使用 AllowPartiallyTrustedCallersAttribute 从只被授予部分信任的应用程序中引用(有关更多信息,请参见 安全性 (WPF)WPF 安全策略 — 平台安全性)。

浏览器宿主检测

在需要按权限进行检查时,使用 CAS 检查是否具有权限是一种恰当的方法。 然而,这一技巧依赖于在正常处理过程中捕获异常(通常不鼓励这样做),并且可能导致性能问题。 如果 XAML browser application (XBAP) 只在 Internet 区域沙盒内运行,则可以改为使用 BrowserInteropHelper.IsBrowserHosted 属性(它为 XAML browser applications (XBAPs) 返回 true)。

注意注意

IsBrowserHosted 只区分应用程序是否在浏览器中运行,而不区分应用程序正在通过哪个权限集运行。

管理权限

默认情况下,XBAPs 在部分信任环境(默认 Internet 区域权限集)下运行。 但是,根据应用程序的要求,可以更改默认的权限集。 例如,如果 XBAPs 是从本地 Intranet 启动的,则可以利用增强的权限集,如下表所示。

表 3:LocalIntranet 和 Internet 权限

权限

特性

LocalIntranet

Internet

DNS

访问 DNS 服务器

环境变量

读取

文件对话框

打开

文件对话框

无限制

独立存储

按用户隔离程序集

独立存储

未知隔离

独立存储

无限制用户配额

媒体

安全音频、视频和图像

打印

默认打印

打印

安全打印

反射

发出

安全性

托管代码执行

安全性

声明授予的权限

用户界面

无限制

用户界面

安全顶级窗口

用户界面

自己的剪贴板

Web 浏览器

HTML 中的安全框架导航

注意注意

如果由用户启动,则剪切和粘贴只允许以部分信任方式执行。

如果需要增加权限,则需要更改项目设置和 ClickOnce 应用程序清单。 有关更多信息,请参见 WPF XAML 浏览器应用程序概述。 以下各个文档可能也会有帮助。

如果 XBAP 需要完全信任,则可以使用相同的工具来增加请求的权限, 但只有从本地计算机、Intranet 或从浏览器的受信任或允许网站中列出的 URL 安装和启动 XBAP 时,它才会得到完全信任。 如果从 Intranet 或受信任网站安装应用程序,则用户将收到标准 ClickOnce 提示,通知用户提升了权限。 用户可以选择继续或取消安装。

或者,可以使用 ClickOnce 受信任部署模型从任何安全区域中进行完全信任部署。 有关更多信息,请参见受信任的应用程序部署概述安全性 (WPF)

请参见

概念

安全性 (WPF)

WPF 安全策略 — 平台安全性

WPF 安全策略 — 安全工程