对 Xamarin.Mac 应用进行沙盒处理
本文介绍如何对 Xamarin.Mac 应用程序进行沙盒处理,以便在 App Store 中发布。 它介绍进行沙盒处理的所有元素,例如容器目录、权利、用户确定的权限、特权分离和内核强制实施。
概述
在 Xamarin.Mac 应用程序中使用 C# 和 .NET 时,可以像使用 Objective-C 或 Swift 一样对应用程序进行沙盒处理。
在本文中,我们将介绍在 Xamarin.Mac 应用程序中进行处理沙盒的基本知识,还将介绍进行沙盒处理的所有元素:容器目录、权利、用户确定的权限、特权分离和内核强制实施。 强烈建议先阅读Hello, Mac 一文,特别是 Xcode 和 Interface Builder 简介和输出口和操作部分,因为其中介绍了我们将在本文中使用的关键概念和技术。
你可能还需要查看 Xamarin.Mac 内部机制文档的向 Objective-C 公开 C# 类/方法部分,其中介绍了用于将 C# 类连接到 Objective-C 对象和 UI 元素的 Register
和 Export
属性。
关于应用沙盒
应用沙盒通过限制应用程序对系统资源的访问,提供强大的防御,防止恶意应用程序在 Mac 上运行所造成的损害。
非沙盒应用程序具有运行应用的用户的完整权限,并且可以访问或执行用户可执行的任何操作。 如果应用包含安全漏洞(或其使用的任何框架),黑客可能会利用这些弱点,并使用该应用控制正在运行它的 Mac。
通过在每个应用程序的基础上限制对资源的访问,沙盒应用提供了一道防线,防止在用户计算机上运行的应用程序遭到盗窃、损坏或恶意意图。
应用沙盒是 macOS 中内置的访问控制技术(在内核级别强制执行),它提供了双重策略:
- 应用沙盒使开发人员能够描述应用程序如何与 OS 交互,通过这种方式,它只被授予完成作业所需的访问权限,而不被授予更多权限。
- 应用沙盒让用户能够通过“打开”和“保存”对话框、拖放操作和其他常见的用户交互,无缝地授予对系统的进一步访问权限。
准备实现应用沙盒
本文将详细讨论的应用沙盒元素如下所示:
- 容器目录
- 权利
- 用户确定的权限
- 特权分离
- 内核强制实施
了解这些详细信息后,你将能够创建一个在 Xamarin.Mac 应用程序中采用应用沙盒的计划。
首先,需要确定你的应用程序是否适合进行沙盒处理(大多数应用程序都适合)。 接下来,需要解决任何 API 不兼容问题,并确定需要的应用沙盒元素。 最后,了解如何使用特权分离来最大程度地提高应用程序的防御级别。
采用应用沙盒时,应用程序使用的一些文件系统位置将有所不同。 具体而言,应用程序将具有容器目录,该目录将用于应用程序支持文件、数据库、缓存以及并非用户文档的任何其他文件。 macOS 和 Xcode 都支持将这些类型的文件从其旧位置迁移到容器。
沙盒处理快速入门
在本部分中,我们将创建一个简单的 Xamarin.Mac 应用,该应用使用 Web 视图作为应用沙盒入门的示例(除非特别要求,否则该视图要求在沙盒处理下受到限制的网络连接)。
我们将验证应用程序实际上是否已沙盒化,并了解如何排查和解决常见的应用沙盒错误。
创建 Xamarin.Mac 项目
让我们执行以下操作来创建示例项目:
启动 Visual Studio for Mac,然后单击“新建解决方案...”链接。
从“新建项目”对话框中,选择“Mac”>“应用”>“Cocoa 应用”:
单击“下一步”按钮,输入
MacSandbox
作为项目名称,然后单击“创建”按钮:在“Solution Pad”中双击“Main.storyboard”文件,将其打开以在 Xcode 中进行编辑:
将“Web 视图”拖到窗口上,调整其大小以填充内容区域,并将其设置为随窗口一起增大和缩小:
为名为
webView
的 Web 视图创建一个输出口:返回到 Visual Studio for Mac,然后双击“Solution Pad”中的“ViewController.cs”文件,将其打开进行编辑。
添加以下 using 语句:
using WebKit;
使
ViewDidLoad
方法如下所示:public override void AwakeFromNib () { base.AwakeFromNib (); webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl("http://www.apple.com"))); }
保存所做更改。
运行应用程序并确保 Apple 网站显示在窗口中,如下所示:
对应用进行签名和预配
首先需要对 Xamarin.Mac 应用程序进行预配和签名,然后才能启用应用沙盒。
请执行以下操作:
登录到 Apple 开发人员门户:
选择“证书、标识符和配置文件”:
在“Mac 应用”下,选择“标识符”:
为应用程序创建新的 ID:
在“预配配置文件”下,选择“开发”:
创建新的配置文件并选择“Mac 应用开发”:
选择上面创建的应用 ID:
为此配置文件选择开发人员:
为此配置文件选择计算机:
为配置文件指定名称:
单击“完成”按钮。
重要
在某些情况下,可能需要直接从 Apple 开发人员门户下载新的预配配置文件,然后双击它进行安装。 你可能还需要停止再重启 Visual Studio for Mac,然后才能访问新的配置文件。
接下来,需要在开发计算机上加载新的应用 ID 和配置文件。 请执行以下操作:
启动 Xcode 并从“Xcode”菜单中选择“首选项”:
单击“查看详细信息...”按钮:
单击“刷新”按钮(左下角)。
单击“完成”按钮。
接下来,需要在 Xamarin.Mac 项目中选择新的应用 ID 和预配配置文件。 请执行以下操作:
在“Solution Pad”中,双击“Info.plist”文件,将其打开进行编辑。
确保“捆绑包标识符”与上面创建的应用 ID 匹配(示例:
com.appracatappra.MacSandbox
):接下来,双击“Entitlements.plist”文件并确保“iCloud 密钥值存储”和“iCloud 容器”都与上面创建的应用 ID 匹配(例如:
com.appracatappra.MacSandbox
):保存所做更改。
在“Solution Pad”中,双击项目文件,打开其选项进行编辑:
选择“Mac 签名”,然后选中“对应用程序捆绑包进行签名”和“对安装程序包进行签名”。 在“预配配置文件”下,选择上面创建的配置文件:
单击“完成”按钮。
重要
可能需要退出再重启 Visual Studio for Mac 才能让它识别 Xcode 安装的新应用 ID 和预配配置文件。
排查预配问题
此时,应尝试运行应用程序,并确保所有内容都已正确签名和预配。 如果应用仍然像以前一样运行,则一切正常。 如果发生故障,可能会收到如下所示的对话框:
下面是导致预配和签名问题的最常见原因:
- 应用捆绑包 ID 与所选配置文件的应用 ID 不匹配。
- 开发人员 ID 与所选配置文件的开发人员 ID 不匹配。
- 要测试的 Mac 的 UUID 未注册为所选配置文件的一部分。
如果出现问题,请在 Apple 开发人员门户中更正问题,在 Xcode 中刷新配置文件,并在 Visual Studio for Mac 中执行干净生成。
启用应用沙盒
通过在项目选项中选择复选框来启用应用沙盒。 请执行以下操作:
此时,你已启用应用沙盒,但尚未提供 Web 视图所需的网络访问权限。 如果现在运行应用程序,会看到一个空白窗口:
验证应用是否已沙盒化
除了资源阻止行为,还有三种主要的方法来判断 Xamarin.Mac 应用程序是否已成功沙盒化:
在 Finder 中,检查
~/Library/Containers/
文件夹的内容 - 如果应用已沙盒化,会有一个与你的应用捆绑包标识符类似的文件夹名称(例如com.appracatappra.MacSandbox
):在活动监视器中,系统将应用视为已沙盒化:
- 启动活动监视器(在
/Applications/Utilities
下)。 - 选择“视图”>“列”,请确保已选中“沙盒”菜单项。
- 确保对于你的应用程序,“沙盒”列设置为
Yes
:
- 启动活动监视器(在
检查应用二进制文件是否已沙盒化:
- 启动终端应用。
- 导航到应用程序的
bin
目录。 - 发出此命令:
codesign -dvvv --entitlements :- executable_path
(其中executable_path
是应用程序的路径):
调试沙盒应用
调试程序通过 TCP 连接到 Xamarin.Mac 应用,这意味着在启用沙盒时,默认情况下它无法连接到应用,因此如果尝试在没有启用适当权限的情况下运行应用,你会收到“无法连接到调试程序”错误。
调试程序需要“允许传出网络连接(客户端)”权限,启用此权限可实现正常调试。 你不能在没有此权限的情况下进行调试,因此我们已为 msbuild
更新 CompileEntitlements
目标,以便自动将该权限添加到仅针对调试生成沙盒化的任何应用的权利中。 发布版本应使用权利文件中指定的权利(未修改)。
解决应用沙盒冲突
如果沙盒化的 Xamarin.Mac 应用程序尝试访问未显式允许的资源,则会发生应用沙盒冲突。 例如,我们的 Web 视图不再能够显示 Apple 网站。
当 Visual Studio for Mac 中指定的权利设置不符合应用程序要求时,会发生最常见的应用沙盒冲突。 同样,回到我们的示例,缺少的网络连接权利,这使 Web 视图不起作用。
发现应用沙盒冲突
如果怀疑 Xamarin.Mac 应用程序中正在发生应用沙盒冲突,发现问题的最快方法是使用“控制台”应用。
请执行以下操作:
编译有问题的应用,并从 Visual Studio for Mac 运行它。
从
/Applications/Utilties/
中打开“制台”应用程序。在边栏中选择“所有消息”,然后在搜索中输入
sandbox
:
对于上面的示例应用,可以看到 Kernal 由于应用沙盒而阻止 network-outbound
流量,原因是我们没有请求该权限。
使用权利修复应用沙盒冲突
现在,我们已经了解如何查找应用沙盒冲突,让我们看看如何通过调整应用程序的权利来解决这些冲突。
请执行以下操作:
如果我们对示例应用执行上述操作,然后生成并运行它,则 Web 内容现在将按预期显示。
深入了解应用沙盒
应用沙盒提供的访问控制机制很少且易于理解。 但是,每个应用采用应用沙盒的方式是独一无二的,并且基于应用的要求。
尽管你尽了最大努力来保护 Xamarin.Mac 应用程序免受恶意代码的攻击,但应用(或它使用的库或框架之一)中只需要有一个漏洞就能控制应用与系统的交互。
应用沙盒旨在通过让你能够指定应用程序与系统的预期交互来防止接管(或限制它可能导致的损害)。 系统只会授予对应用程序完成其作业所需的资源的访问,而不会授予其他任何访问权限。
在针对应用沙盒进行设计时,你要针对最坏的情况进行设计。 如果应用程序确实受到恶意代码的入侵,则只能访问应用沙盒中的文件和资源。
权利和系统资源访问
如上所述,尚未沙盒化的 Xamarin.Mac 应用程序被授予了运行应用的用户的完整权利和访问权限。 如果受到恶意代码的入侵,未受保护的应用可以充当恶意行为的代理,并可能造成广泛的潜在损害。
通过启用应用沙盒,可删除除最小特权集以外的所有特权,然后使用 Xamarin.Mac 应用的权利根据需要重新启用这些权限。
要修改应用程序的应用沙盒资源,可编辑其 Entitlements.plist 文件,并从编辑器下拉框中勾选或选择所需的权限:
容器目录和文件系统访问
当 Xamarin.Mac 应用程序采用应用沙盒时,它有权访问以下位置:
- 应用容器目录 - 首次运行时,OS 会创建一个特殊的容器目录,所有资源都放在此目录中,只有它可以访问。 该应用将对此目录具有完全读/写访问权限。
- 应用组容器目录 - 可以向应用授予访问在同一组中的应用之间共享的一个或多个组容器的权限。
- 用户指定的文件 - 应用程序会自动获取对用户显式打开或拖放到应用程序上的文件的访问权限。
- 相关项 - 使用适当的权利,应用程序可以访问具有相同名称但扩展名不同的文件。 例如,已保存为
.txt
文件和.pdf
的文档。 - 临时目录、命令行工具目录和特定的世界可读位置 - 应用对系统指定的其他定义良好的位置中的文件具有不同程度的访问权限。
应用容器目录
Xamarin.Mac 应用程序的应用容器目录具有以下特征:
- 它位于用户主目录中的隐藏位置(通常是
~Library/Containers
),可通过应用程序中的NSHomeDirectory
函数(如下所示)进行访问。 由于它在主目录中,因此每位用户都将为应用获取自己的容器。 - 应用对容器目录及其所有子目录和目录中的文件具有不受限的读/写访问权限。
- 大多数 macOS 的路径查找 API 都是相对于应用程序的容器的。 例如,容器将有自己的“库”(通过
NSLibraryDirectory
访问)、“应用程序支持”和“首选项”子目录。 - macOS 通过代码签名建立并强制实施应用与其容器之间的连接。 即使有应用尝试使用其捆绑包标识符欺骗另一个应用,但由于代码签名,它将无法访问容器。
- 容器不适用于用户生成的文件。 而是适用于应用程序使用的文件,例如数据库、缓存或其他特定类型的数据。
- 对于沙盒类型的应用(例如 Apple 的照片应用),用户的内容将放置到容器中。
重要
遗憾的是,Xamarin.Mac 还没有 100% 的 API 涵盖率(这一点与 Xamarin.iOS 不同),因此 NSHomeDirectory
API 尚未在映射到当前版本的 Xamarin.Mac 中。
可使用以下代码来临时解决此问题:
[System.Runtime.InteropServices.DllImport("/System/Library/Frameworks/Foundation.framework/Foundation")]
public static extern IntPtr NSHomeDirectory();
public static string ContainerDirectory {
get {
return ((NSString)ObjCRuntime.Runtime.GetNSObject(NSHomeDirectory())).ToString ();
}
}
应用组容器目录
从 Mac macOS 10.7.5(及更高版本)开始,应用程序可以使用 com.apple.security.application-groups
权利来访问组中所有应用程序通用的共享容器。 可以将此共享容器用于非面向用户的内容,例如数据库或其他类型的支持文件(如缓存)。
组容器会自动添加到每个应用的沙盒容器(如果它们是组的一部分),并存储在 ~/Library/Group Containers/<application-group-id>
。 组 ID 必须以开发团队 ID 和句点开头,例如:
<team-id>.com.company.<group-name>
有关详细信息,请查看 Apple 的权利键参考中的向应用程序组添加应用程序。
应用容器外部的 Powerbox 和文件系统访问
沙盒化的 Xamarin.Mac 应用程序可通过以下方式访问其容器外部的文件系统位置:
- 在用户的特定方向(通过“打开”和“保存”对话框或拖放等其他方法)。
- 通过使用特定文件系统位置(如
/bin
或/usr/lib
)的权利。 - 当文件系统位置位于世界可读的某些目录(例如共享目录)中时。
Powerbox 是一种 macOS 安全技术,它与用户交互来扩展沙盒化的 Xamarin.Mac 应用的文件访问权限。 Powerbox 没有 API,但在应用调用 NSOpenPanel
或 NSSavePanel
应用时以透明方式激活。 Powerbox 访问权限是通过为 Xamarin.Mac 应用程序设置的权利启用的。
当沙盒应用显示“打开”或“保存”对话框时,窗口由 Powerbox(而不是 AppKit)显示,因此有权访问用户能够访问的任何文件或目录。
当用户通过“打开”或“保存”对话框选择文件或目录(或者拖动到应用图标上)时,Powerbox 会将关联的路径添加到应用的沙盒。
此外,系统会自动允许沙盒应用执行以下操作:
- 连接到系统输入法。
- 从“服务”菜单中调用用户选择的服务(仅适用于服务提供商标记为“可安全用于沙盒应用”的服务)。
- 从“打开最近使用的文件”菜单中打开用户选择的文件。
- 在其他应用程序之间使用复制和粘贴。
- 从以下世界可读位置读取文件:
/bin
/sbin
/usr/bin
/usr/lib
/usr/sbin
/usr/share
/System
- 读取和写入由
NSTemporaryDirectory
创建的目录中的文件。
默认情况下,沙盒化的 Xamarin.Mac 应用打开或保存的文件将保持可访问状态,直到应用终止(除非应用退出时文件仍处于打开状态)。 下次启动应用时,打开的文件将通过 macOS 恢复功能自动还原到应用的沙盒。
若要为 Xamarin.Mac 应用容器外部的文件提供持久性,请使用安全范围的书签(请参阅下文)。
相关项
应用沙盒允许应用访问文件名相同但扩展名不同的相关项。 此功能有两个部分:a) 应用的 Info.plst
文件中的相关扩展名列表,b) 告知沙盒应用将对这些文件执行哪些操作的代码。
这在两种情况下是有意义的:
- 应用需要能够保存文件的不同版本(具有新的扩展名)。 例如,将
.txt
文件导出到.pdf
文件。 若要处理这种情况,必须使用NSFileCoordinator
来访问文件。 首先调用WillMove(fromURL, toURL)
方法,将文件移动到新的扩展名,然后调用ItemMoved(fromURL, toURL)
。 - 应用需要打开一个具有一个扩展名的主文件和几个具有不同扩展名的支持文件。 例如,电影和字幕文件。 使用
NSFilePresenter
获取对辅助文件的访问权限。 向PrimaryPresentedItemURL
属性提供主文件,并将辅助文件提供给PresentedItemURL
属性。 打开主文件时,调用NSFileCoordinator
类的AddFilePresenter
方法来注册辅助文件。
在这两种情况下,应用的“Info.plist”文件必须声明应用可打开的文档类型。 对于任何文件类型,请将 NSIsRelatedItemType
(其值为 YES
)添加到其在 CFBundleDocumentTypes
数组中的条目。
使用沙盒应用的“打开”和“保存”对话框行为
从沙盒化的 Xamarin.Mac 应用调用 NSOpenPanel
和 NSSavePanel
时,将对其施加以下限制:
- 不能以编程方式调用“确定”按钮。
- 不能在
NSOpenSavePanelDelegate
中以编程方式更改用户的选择。
此外,以下继承修改已就绪:
- 非沙盒应用 -
NSOpenPanel
NSSavePanel``NSPanel``NSWindow``NSResponder``NSObject``NSOpenPanel``NSSavePanel``NSObject``NSOpenPanel``NSSavePanel
安全范围的书签和永久性资源访问权限
如上所述,沙盒化的 Xamarin.Mac 应用程序可以通过直接用户交互(由 PowerBox 提供)访问容器外部的文件或资源。 但是,在应用启动或系统重启时,对这些资源的访问权限不会自动保留。
通过使用安全范围的书签,沙盒化的 Xamarin.Mac 应用程序可以保留用户意向,并在应用重启后保留对给定资源的访问权限。
安全范围的书签类型
使用安全范围的书签和永久性资源访问权限时,有两个常见的用例:
应用范围的书签提供对用户指定的文件或文件夹的永久性访问权限。
例如,如果沙盒化的 Xamarin.Mac 应用程序可用于打开外部文档进行编辑(使用
NSOpenPanel
),则应用可创建一个应用范围的书签,以便它将来再次访问同一文件。文档范围的书签提供对子文件的特定文档永久性访问权限。
例如,这样一个视频编辑应用,它用于创建有权访问单个图像、视频剪辑和声音文件的项目文件,这些内容稍后将被组合成一部电影。
当用户通过 NSOpenPanel
将资源文件导入项目时,应用会为项目中存储的项创建文档范围的书签,以便该应用可始终访问该文件。
任何可打开书签数据和文档本身的应用程序都可以解析文档范围的书签。 这支持可移植性,让用户能够将项目文件发送给其他用户,并使所有书签也适用于这些文件。
重要
文档范围的书签只能指向单个文件,不能指向文件夹,并且该文件不能位于系统使用的位置(例如 /private
或 /Library
)。
使用安全范围的书签
要使用任何一种安全范围的书签,需要执行以下步骤:
- 在需要使用安全范围的书签的 Xamarin.Mac 应用中设置适当的权利 - 对于应用范围的书签,请将
com.apple.security.files.bookmarks.app-scope
权利键设置为true
。 对于文档范围的书签,请将com.apple.security.files.bookmarks.document-scope
权利键设置为true
。 - 创建安全范围的书签 - 你将为用户已提供其访问权限(例如通过
NSOpenPanel
)且应用需要对其拥有永久性访问权限的任何文件或文件夹创建此书签。 使用NSUrl
类的public virtual NSData CreateBookmarkData (NSUrlBookmarkCreationOptions options, string[] resourceValues, NSUrl relativeUrl, out NSError error)
方法创建书签。 - 解析安全范围的书签 - 当应用需要再次访问资源时(例如重启后),需要将书签解析为安全范围的 URL。 使用
NSUrl
类的public static NSUrl FromBookmarkData (NSData data, NSUrlBookmarkResolutionOptions options, NSUrl relativeToUrl, out bool isStale, out NSError error)
方法解析书签。 - 显式通知系统你想要从安全范围的 URL 访问文件 - 在获取上述安全范围的 URL 后需要立即完成此步骤,或者在放弃对资源的访问权限后想要重新获得对该资源的访问权限时执行此步骤。 调用
NSUrl
类的StartAccessingSecurityScopedResource ()
方法以开始访问安全范围的 URL。 - 显式通知系统你已经从安全范围的 URL 访问文件 - 当应用不再需要访问文件时(例如,如果用户关闭它),你应尽快通知系统。 调用
NSUrl
类的StopAccessingSecurityScopedResource ()
方法以停止访问安全范围的 URL。
放弃对资源的访问权限后,需要再次执行步骤 4 来重新建立访问权限。 如果 Xamarin.Mac 应用重启,必须返回到步骤 3 并重新解析书签。
重要
未能释放对安全范围的 URL 资源的访问权限将导致 Xamarin.Mac 应用泄漏内核资源。 因此,在重启之前,应用将无法再将文件系统位置添加到其容器中。
应用沙盒和代码签名
启用应用沙盒并(通过权利)启用 Xamarin.Mac 应用的特定要求后,必须对项目进行代码签名,才能使沙盒生效。 必须执行代码签名,因为应用沙盒所需的权利与应用的签名相关联。
macOS 强制在应用容器与其代码签名之间建立关联,这样任何其他应用程序都无法访问该容器,即使它欺骗了应用捆绑包 ID 也无法访问。 该机制的工作原理如下:
- 当系统创建应用的容器时,会在该容器上设置访问控制列表 (ACL)。 列表中的初始访问控制项包含应用的指定要求 (DR),该要求描述了(在应用升级后)如何识别应用的未来版本。
- 每次启动具有相同捆绑包 ID 的应用时,系统都会检查应用的代码签名是否与容器 ACL 中某个条目中设置的指定要求匹配。 如果系统找不到匹配项,它将阻止应用启动。
代码签名的工作原理如下:
- 在创建 Xamarin.Mac 项目之前,请从 Apple 开发人员门户获取开发证书、分发证书和开发人员 ID 证书。
- 当 Mac App Store 分发 Xamarin.Mac 应用时,它使用 Apple 代码签名进行签名。
进行测试和调试时,将使用已签名的 Xamarin.Mac 应用程序版本(它将用于创建应用容器)。 稍后,如果想要测试或安装来自 Apple App Store 的版本,它将使用 Apple 签名进行签名,并且无法启动(因为它没有与原始应用容器相同的代码签名)。 在这种情况下,你将获得如下所示的崩溃报告:
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
若要解决此问题,需要调整 ACL 条目,使其指向应用的 Apple 签名版本。
若要详细了解如何创建和下载沙盒所需的预配配置文件,请参阅上面的对应用进行签名和预配部分。
调整 ACL 条目
若要允许 Xamarin.Mac 应用的 Apple 签名版本运行,请执行以下操作:
- 打开终端应用(在
/Applications/Utilities
中)。 - 打开 Xamarin.Mac 应用的 Apple 签名版本的 Finder 窗口。
- 在终端窗口中键入
asctl container acl add -file
。 - 从 Finder 窗口拖动 Xamarin.Mac 应用的图标,并将其放到终端窗口中。
- 文件的完整路径将添加到终端中的命令。
- 按 Enter 执行该命令。
容器的 ACL 现在包含两个版本的 Xamarin.Mac 应用的指定代码要求,macOS 现在允许任一版本运行。
显示 ACL 代码要求列表
可通过执行以下操作来查看容器 ACL 中的代码要求列表:
- 打开终端应用(在
/Applications/Utilities
中)。 - 键入
asctl container acl list -bundle <container-name>
。 - 按 Enter 执行该命令。
<container-name>
通常是 Xamarin.Mac 应用程序的捆绑包标识符。
为应用沙盒设计 Xamarin.Mac 应用
为应用沙盒设计 Xamarin.Mac 应用时,应遵循一个通用的工作流。 也就是说,在应用中实现沙盒的具体细节对于给定应用的功能来说是独一无二的。
采用应用沙盒的 6 个步骤
为应用沙盒设计 Xamarin.Mac 应用通常包括以下步骤:
- 确定应用是否适合进行沙盒处理。
- 设计开发和分发策略。
- 解决任何 API 不兼容问题。
- 将所需的应用沙盒权利应用于 Xamarin.Mac 项目。
- 使用 XPC 添加特权分离。
- 实施迁移策略。
重要
你不仅必须对应用捆绑包中的主要可执行文件进行沙盒处理,还必须对该捆绑包中包含的每个帮助程序应用或工具进行沙盒处理。 这是从 Mac App Store 分发的任何应用所必需的,如果可能,应针对任何其他形式的应用分发执行此操作。
有关 Xamarin.Mac 应用捆绑包中所有可执行二进制文件的列表,请在终端中键入以下命令:
find -H [Your-App-Bundle].app -print0 | xargs -0 file | grep "Mach-O .*executable"
其中,[Your-App-Bundle]
是应用程序捆绑包的名称和路径。
确定 Xamarin.Mac 应用是否适合进行沙盒处理
大多数 Xamarin.Mac 应用都与应用沙盒完全兼容,因此适合进行沙盒处理。 如果应用需要应用沙盒不允许的行为,应考虑采用一种替代方法。
如果应用需要以下行为之一,则它与应用沙盒不兼容:
- 授权服务 - 使用应用沙盒时,不能使用授权服务 C 参考中所述的功能。
- 辅助功能 API - 不能对辅助应用进行沙盒处理,例如屏幕阅读器或控制其他应用程序的应用。
- 将 Apple 事件发送到任意应用 - 如果应用需要将 Apple 事件发送到未知的任意应用,则无法将该应用沙盒化。 对于已知的已调用应用列表,该应用仍可以沙盒化,权利需要包含已调用应用的列表中。
- 将分布式通知中的用户信息字典发送到其他任务 - 使用应用沙盒时,在发布到
NSDistributedNotificationCenter
对象来传送消息给其他任务时,不能包含userInfo
字典。 - 加载内核扩展 - 应用沙盒禁止加载内核扩展。
- 在“打开”和“保存”对话框中模拟用户输入 - 应用沙盒禁止以编程方式操作“打开”或“保存”对话框来模拟或更改用户输入。
- 在其他应用上访问或设置首选项 - 应用沙盒禁止操作其他应用的设置。
- 配置网络设置 - 应用沙盒禁止操作网络设置。
- 终止其他应用 - 应用沙盒禁止使用
NSRunningApplication
来终止其他应用。
解决 API 不兼容问题
为应用沙盒设计 Xamarin.Mac 应用时,可能会遇到与某些 macOS API 的用法不兼容的情况。
下面是一些常见问题和解决方法:
- 打开、保存和跟踪文档 - 如果使用
NSDocument
以外的任何技术管理文档,那么应切换到它,因为它内置了对应用沙盒的支持。NSDocument
自动使用 PowerBox;当用户在 Finder 中移动文档时,它支持将文档保留在沙盒中。 - 保留对文件系统资源的访问权限 - 如果 Xamarin.Mac 应用依赖于对其容器外部资源的永久性访问权限,请使用安全范围的书签来维持访问权限。
- 为应用创建登录项 - 使用应用沙盒时,不能使用
LSSharedFileList
创建登录项,也不能使用LSRegisterURL
来操作启动服务的状态。 使用 Apple 的使用服务惯例框架添加登录项文档中所述的SMLoginItemSetEnabled
函数。 - 访问用户数据 - 如果使用 POSIX 函数(例如
getpwuid
)来从目录服务获取用户的主目录,请考虑使用 Cocoa 或 Core Foundation 符号(例如NSHomeDirectory
)。 - 访问其他应用的首选项 - 由于应用沙盒将路径查找 API 定向到应用的容器,因此修改首选项发生在该容器中,并且不允许访问其他应用首选项。
- 在 Web 视图中使用 HTML5 嵌入式视频 - 如果 Xamarin.Mac 应用使用 WebKit 播放嵌入式 HTML5 视频,则还必须将应用链接到 AV Foundation 框架。 否则,应用沙盒将阻止 CoreMedia 播放这些视频。
应用所需的应用沙盒权利
需要编辑要在应用沙盒中运行的任何 Xamarin.Mac 应用程序的权利,并选中“启用应用沙盒”复选框。
根据应用的功能,可能需要启用其他权利才能访问 OS 功能或资源。 将请求的权利最小化到运行应用所需的最低限度权限时,应用沙盒效果最佳,因此只需随机启用权利即可。
若要确定 Xamarin.Mac 应用所需的权利,请执行以下操作:
- 启用应用沙盒并运行 Xamarin.Mac 应用。
- 通过应用的功能来运行。
- 打开控制台应用(在
/Applications/Utilities
中可用),并在“所有消息”日志中查找sandboxd
冲突。 - 对于每个
sandboxd
冲突,请使用应用容器而不是其他文件系统位置来解决该问题,或者应用应用沙盒权利来启用对受限 OS 功能的访问权限。 - 重新运行并再次测试所有 Xamarin.Mac 应用功能。
- 重复操作,直到解决所有的
sandboxd
冲突。
使用 XPC 添加特权分离
为应用沙盒开发 Xamarin.Mac 应用时,请查看应用在特权和访问权限方面的行为,然后考虑将高风险操作分离到它们自己的 XPC 服务中。
有关详细信息,请参阅 Apple 的创建 XPC 服务和守护程序和服务编程指南。
实施迁移策略
如果要发布以前未沙盒化的 Xamarin.Mac 应用程序的新沙盒版本,需要确保当前用户具有顺畅的升级路径。
若要详细了解如何实现容器迁移清单,请阅读 Apple 的将应用迁移到沙盒文档。
总结
本文详细介绍了 Xamarin.Mac 应用程序的沙盒化。 首先,我们创建了一个简单的 Xamarin.Mac 应用来显示应用沙盒的基础知识。 接下来,我们介绍了如何解决沙盒冲突。 然后,我们深入探讨了应用沙盒。最后,我们查看了如何为应用沙盒设计 Xamarin.Mac 应用。