大多数浏览器和
Developer App 均支持流媒体播放。
-
通过环境约束保护你的 Mac App
了解如何通过环境约束来提高 Mac App 的安全性。我们将向你展示如何设置进程启动方式的约束,确保你的启动代理和启动守护进程不被篡改,并防止无关代码在你的地址空间中运行。
章节
- 0:00 - Introduction
- 2:47 - Environment constraints
- 7:48 - Defining constraints
- 10:16 - Adopting constraints
- 16:22 - Wrap-up
资源
相关视频
WWDC22
WWDC20
-
下载
♪ ♪
Robert:你好 我是 Robert Kendall-Kuppe 今天我将向你介绍 macOS 的 新功能“环境约束” 功能丰富的 Mac App 通常 不仅仅是一个进程或文件 通过框架和库 你可以在自己的 App 或其他开发人员的 App 中 重复使用代码 辅助工具、 辅助 App 和 XPC 服务 可以让你分拆工作 减少攻击面的出现 启动代理、启动守护进程和登录项 可以让你在后台 或用户登录时执行工作 App 扩展可以让你在其他 App 中继续提供有用的功能 但是 你的 App 运行在 一个潜在的恶意环境中 App 架构师需要考虑 与自身软件同时运行 或使用自身框架的 未知软件的潜在影响 辅助工具或 XPC 服务的 执行是否会 让攻击者趁机访问你的钥匙串数据? 你的 iCloud 数据或 其他特权进程又会如何? 如果有意想不到的代码注入 你的进程 会发生什么情况? 就像现实中的父子关系一样 父进程对子进程的行为有很大影响 在 macOS 上 posix_spawn 另一个进程的能力 使父进程能够控制 子进程的几乎所有输入 父进程还可以限制子进程 对系统资源的访问 这种程度的控制可能导致 子进程加载意想不到的代码 运行意想不到的功能 或者使进程的行为更容易受到攻击 除了父子关系外 进程还信任其生成的磁盘布局 能够修改磁盘上文件的恶意进程 可能会向受害进程 提供意想不到的数据、 移除受害进程的运行时保护、 在系统上持久执行 或窃取某个进程的权限 面对所有这些威胁 macOS 为你提供了 保护 App 安全的工具 特别是 你可以采用 App 沙盒 来限制 App 受到攻击时 产生的影响 你还可以采用 Hardened Runtime 和库验证 在运行时保护进程的完整性 门禁和公证也有助于 使客户系统免受 已知恶意代码的影响 考虑到我前面提到的种种威胁 我们意识到现有的保护措施 侧重于运行中的进程 而不是这些进程的执行环境 这就是我们引入环境约束的原因 环境约束为你提供了 一种全新控制级别 使你可以控制进程运行环境 以及在进程中可以混合代码的方式 在接下来的介绍中 我们将讨论 环境约束如何融入 macOS 的安全架构 环境约束是如何构成的 以及如何在 App 中 采用环境约束 开始正题前 我们先谈谈 macOS 的安全堆栈 默认情况下 macOS 会利用 所有安全技术 来确保引导链的安全 与操作系统的完整性 执行权限分离 并保护用户免受恶意软件的侵害 在 macOS Ventura 中 我们开始使用环境约束 更好地保护 操作系统进程之间的关系 环境约束为操作系统的安全性 提供了一个新的维度 在 macOS Sonoma 中 我们扩大了环境约束的使用范围 将其开放给你的 App 使用 那么什么是环境约束呢? 从根本上说 环境约束是 一种描述代码的方式 它不仅描述代码的内容 还描述代码在系统中的 存在和运行方式 在 macOS 中 我们将 环境约束用于多种用途 例如 为帮助确保进程 使用受信任的软件包资源 我们要求操作系统进程从 Signed System Volume 中运行 为确保特权守护进程 不会使用意外参数 或 Mach 端口运行 我们要求系统守护进程只能基于 受保护的 launchd.plist 运行 为了减少系统 App 的攻击面 我们要求它们作为 App 从启动服务运行 并且为了确保用户对 后台项目的批准有效 我们使用环境约束来检测更改 现在你可能要问了 “我的 App 需要使用环境约束吗?” 我想强调的是 环境约束 并不是非要不可 但环境约束可以 减少所有 App 的攻击面 稍后我们将讨论一些更具体的用例 但我要指出的是 如果你的 App 有多个进程或 加载了由不同开发团队签名的代码 环境约束可能会特别有用 环境约束有几种不同的类型 我们先来谈谈启动约束 启动约束被嵌入到一个 特定的二进制文件中 并定义了该进程的属性、 可成为其父进程的进程的属性 或负责该进程的进程的属性 我们将这些属性称为“自身约束”、 “父进程约束” 和“责任进程约束” 你可以应用全部三种属性 也可以选择最合理的属性 如果所需的任意属性都无法满足 具有嵌入式启动约束的 进程将无法运行 现在我会向你介绍一些进程关系 并谈谈如何使用 启动约束来保护它们 首先假设 MyDemo.app 是你的 App 你可以在 MyDemo.app 上 设置自身约束 要求它作为 App 从启动服务启动 当你的 App 请求 连接到你的 XPC 服务时 launchd 会生成 XPC 服务 并且是该 XPC 服务的父进程 但你的 App 对该 XPC 服务“负责” 你可以在 MyXPCDemo.xpc 上 设置一个负责进程约束 表明只有 MyDemo.app 对其负责 如果你的 App 随后使用 NSTask 或 posix_spawn 来启动一个 辅助进程 那么它既是该辅助 进程的父进程 也对其负责 你可以在 MyFirstHelper 上 设置一个父进程约束 要求只有 MyDemo.app 可以是它的父进程 然后 如果 MyFirstHelper posix_spawn 了 MySecondHelper 那么 MyFirstHelper 就是 MySecondHelper 的父进程 但 App 对 MySecondHelper 负责 对于 MySecondHelper 你可以设置一个父进程约束 要求其只能由 MyFirstHelper 启动 你也可以设置一个责任进程约束 要求只有 MyDemo.app 对它负责 你还可以在 launchd plist 中 为启动代理和启动守护进程 指定环境约束 当你使用 SMAppService API 注册你的 plist 时 操作系统将强制要求 只有符合约束的进程 才能代表你的 plist 启动 该功能可确保恶意代码不会 因用户批准了 App 的后台活动 而获得持续执行 最后 你可以使用库加载约束 专门控制可以加载到 地址空间中的代码 在使用库加载约束之前 你是否采用库验证并不造成影响 库验证允许你的进程加载 你签名或 Apple 签名的代码 通过库加载约束 你可以描述一组 比库验证所允许的限制更少的代码 同时防止任意代码 被加载到你的进程中 但请注意 你不能将 Apple 签名的代码 排除在进程加载之外 你需要指定一个或多个属性 来允许你自己的代码加载 现在你已经知道什么是环境约束 以及如何使用环境约束 让我们讨论一下如何定义环境约束 环境约束描述了代码 必须满足的一系列条件 这些约束以字典的形式编码 字典中的键代表代码中 必须为真的事实 或者表示事实或谓词之间 必要关系的运算符 在顶层 隐式地将每个键值对的结果 通过 AND 运算加在一起 以决定是否满足约束 请注意 由于这些都是字典 所以每个键在每个字典级别 只能出现一次 让我们来看看你可能 会用到的一些事实 左边是相关的环境约束键 右边是 codesign 命令的输出 签名标识符键允许你 指定一个字符串 这个字符串对于 给定的代码来说是唯一的 但在不同版本的代码中保持不变 signing-identifier 键指的是 codesign 输出中的标识符字段 cdhash 键允许你为代码指定 一个唯一的哈希值 而 team-identifier 键允许你指定 由特定开发团队签字的代码 事实表示代码的特定属性 操作符可用于逻辑组合事实集 或定义事实的可接受值集 正如你所期望的 $and 和 $or 操作符 允许你指定谓词字典 这些字典将在确定之后 进行逻辑组合 $and-array 和 $or-array 操作符的存在是为了限制字典嵌套 在这种情况下 你可能需要与 多个 $or 谓词进行 AND 运算 或与多个 $and 谓词 进行 OR 运算 最后 $in 操作符允许你指定 一个数组的值来满足一个事实 让我们来看看这个约束示例 左侧是约束的 plist 表示 右侧是显示 XML 含义的伪码 在 plist 的顶层 有一个 叫做 $or-array 的键 该值是由三个元组组成的数组 每个元组包含一个运算符 和一个应用该运算符的字典 因此这意味着该约束允许所有 由你的 team-identifier 或由第二个 team-identifier 签名的库 B 或由第三个 team-identifier 签名的库 C 签名的代码 对于第一个元组 由于它是单个元素 我们也可以使用 $or 运算符 现在我们可以定义约束了 让我们看看如何在你的 项目中采用这些约束 为了讨论更加具体 我们假设有这样一个主 App 它包含一个启动代理、 一个辅助工具、 一个包含 XPC 服务的框架和 一个由其他开发团队签署的库 现在让我们考虑一下环境约束 可以缓解的一些潜在问题 也许你已经为辅助工具 分配了一些权限 比如访问钥匙串数据 或访问 iCloud 容器 你可能希望确保辅助工具 只能由你的 App 启动 而不能由其他 App 启动 你可以通过在辅助工具上 设置父进程约束 确保只有你的 App 才能 启动辅助工具 为此 请创建一个 代码要求 plist 文件 该文件需要你的 team-identifier 和主 App 的 signing-identifier 然后在“Launch Constraint Parent Process Plist”设置中 将该约束添加到辅助工具 的签名配置中 让我们仔细看看 我在 Xcode 中创建了一个演示项目 该项目具有我提到的属性 MyDemo.app 是主 App 目标 demohelper 是辅助工具 让我们启动这个 App 当我按下这个按钮时 App 会生成辅助工具 然后辅助工具会执行一些工作 并向 App 提供响应 让我们看看终端中 demohelper 的签名
可以看到该 App 没有设置启动约束 我们也可以运行 demohelper 但是看这儿 如果我们使用 --cloud 参数 运行 demohelper 那么 demohelper 就可以 访问 App 的 iCloud 数据 我们不希望任意进程 可以运行 demohelper 并更改我们的 iCloud 数据 让我们回到 Xcode 并为 demohelper 设置一个父约束
在这里 我已经填充了 一个约束 plist 文件 来标识主 App MyDemo
将该约束添加到我们的签名配置中
重新试试这个 App
我们可以再次启动这个 App……
主 App 仍然可以生成辅助程序
但是当我们回到终端……
可以看到 现在 demohelper 有了一个启动约束…… 它不能再从终端运行了 当由于违反启动约束 而导致启动受阻时 将生成一份崩溃报告 指出违反约束 现在让我们来谈谈环境约束 可以缓解的一些其他问题 我们鼓励你使用 XPC 服务 来区分不同进程之间的权限 但是构建 XPC 服务时 你有可能会从自己的软件包中 提取这些服务并从 其他代码中调用服务 如果你已经为你的 XPC 服务分配了某些权限 你需要确保 只有预期内的进程才能获得该权限 有一种方法可以确保只有 你的代码可以访问 XPC 服务 那就是设置一个 负责进程的启动约束 这里展示的是一个 启动约束 plist 它允许你的团队对代码进行签名 并列出软件包中每个应该能够访问 XPC 服务的进程的 signing-identifier 列表 你可以在“Launch Constraint Responsible Process Plist”设置中 将该约束添加到你的签名配置中 还有一个需要考虑的问题 从 macOS Ventura 开始 用户会被要求批准 代表 App 安装的后台任务 这意味着用户希望该工作 仅代表你的 App 进行 如果攻击者可以替换 plist 期望运行的代码 则攻击者可能会代表你的 App 获得持续的后台执行 为了确保你注册的 plist 只能用于运行你期望的代码 你可以使用 SpawnConstraint 键 设置 launchd plist 约束 这里你可以看到一个完整的带有 SpawnConstraint 键的 launchd plist 这个约束标识了我们的团队 和 DemoMenuBar 代理 最后 我们来谈谈库加载 如果你有义务在不修改的情况下 链接来自其他开发团队的库 那么为了让你的 App 通过加固运行时进行公证 你必须采用 disable-library-validation 权限 不幸的是 这意味着你的 App 现在可以加载由任何人签名的代码 而不仅仅是你从可信开发人员 那里获得的库 为了解决这个问题 你可以 采用库加载约束 这里我们展示了一个约束 它允许你加载 由你的团队或可信库供应方 签名的任何代码 你还可以使用一个或多个 signing-identifier 事实 将其进一步限制为 特定的库或库集合 与启动约束一样 当你在 Xcode 签名配置中 设置“Library Load Constraint Plist”时 库加载约束会被签名到你的进程中 那么环境约束在哪里可用呢? 库加载约束 和 launchd plist 约束 可以在适用于任何 macOS 版本的 App 中使用 这些约束从 macOS Sonoma 开始强制执行 启动约束可以添加到 macOS 13.3 或更高版本的 App 中 并且从 macOS 13.3 开始强制执行 请注意 在不同版本的 macOS 中 可用于环境约束的键和值的 集合可能会发生变化 请参阅文档了解完整的可用性信息 建议你查看一下 App 中的 进程关系、launchd plists 和库 看看是否可以使用环境约束 将 App 打造的更加安全 感谢你的观看 ♪ ♪
-
-
9:35 - Example constraint
// Example constraint <dict> <key>$or-array</key> <array> <array> <string>$and</string> <dict> <key>team-identifier</key> <string>M2657GZ2M9</string> </dict> </array> <array> <string>$and</string> <dict> <key>signing-identifier</key> <string>com.smith.libraryB</string> <key>team-identifier</key> <string>P9Z4AN7VHQ</string> </dict> </array> <array> <string>$and</string> <dict> <key>signing-identifier</key> <string>com.friday.libraryC</string> <key>team-identifier</key> <string>TA1570ZFMZ</string> </dict> </array> </array> </dict>
-
11:02 - Example parent launch constraint
<dict> <key>team-identifier</key> <string>M2657GZ2M9</string> <key>signing-identifier</key> <string>com.demo.MyDemo</string> </dict>
-
14:06 - Example process launch constraint
<dict> <key>team-identifier</key> <string>M2657GZ2M9</string> <key>signing-identifier</key> <dict> <key>$in</key> <array> <string>com.demo.MyDemo</string> <string>com.demo.DemoMenuBar</string> <string>demohelper</string> </array> </dict> </dict>
-
14:52 - Example launchd plist constraint
// Example launchd plist constraint <dict> <key>Label</key> <string>com.demo.DemoMenuBar.agent</string> <key>BundleProgram</key> <string>Contents/Library/LaunchAgents/DemoMenuBar.app/Contents/MacOS/DemoMenuBar</string> <key>KeepAlive</key> <dict> <key>SuccessfulExit</key> <true/> </dict> <key>RunAtLoad</key> <true/> <key>SpawnConstraint</key> <dict> <key>team-identifier</key> <string>M2657GZ2M9</string> <key>signing-identifier</key> <string>com.demo.DemoMenuBar</string> </dict> </dict>
-
15:29 - Example library load constraint
// Example library load constraint <dict> <key>team-identifier</key> <dict> <key>$in</key> <array> <string>M2657GZ2M9</string> <string>P9Z4AN7VHQ</string> </array> </dict> </dict>
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。