本文作者系360奇舞团前端开发工程师

引言

在日常工作中,Mac 电脑上装置的运用首要来自两个渠道:一是经过 App Store 下载,由苹果审阅团队确保内容、技能、隐私的合规性;二是从网站下载运用,以 pkg、dmg 格局供给,由装置者自行决定是否信任。这两种渠道下的运用或许在权限、操作办法、数据安全等方面都存在差异,而苹果的技能文档杂乱并且一同面向这两种状况,导致作为开发者,在开发需求上架 App Store 的 Mac 运用时,或许会面临技能、设计和上线合规等应战。切当了解哪些技能合规、哪些权限需关注变得十分重要。

本篇文章咱们将一同了解,开发一款 Mac App Store 运用有必要要知道的常识点。

App Sandbox

要经过Mac App Store发布macOS运用,苹果要求有必要启用运用沙盒功用。

App Sandbox : 运用程序沙箱是 macOS 在内核级供给并履行的一种拜访控制技能。沙箱的首要功用是在用户履行受进犯的运用程序时,约束对体系和用户数据形成的损害。尽管沙箱并不能阻挠针对你的运用程序的进犯,但它能够将你的运用程序约束在正常运转所需的最低权限规模内,然后减少进犯成功或许形成的危害。

构建 Mac App Store 运用之必备常识
敞开沙盒权限:

构建 Mac App Store 运用之必备常识

敞开沙盒权限转存失败,建议直接上传图片文件 假如添加了App Sandbox功用,则会将相应的权力添加到项目的装备中。.entitlements这是添加沙箱后的项目文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">100
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
</dict>
</plist>

当运用程序启用沙盒后,会约束其拜访体系资源和履行某些操作。以下是一些首要的约束:

  1. 文件体系拜访约束
  2. 网络拜访约束
  3. 进程间通讯约束
  4. 硬件拜访约束
  5. 脚本履行约束
  6. 装置软件包约束

从App Sandbox中拜访文件

运用程序沙盒经过约束运用程序对受维护资源的拜访来进步Mac的整体安全性。 但 MacOS 答应你的运用程序不受约束地拜访 Mac文件体系的有限部分

运用程序对其沙盒容器(~/Library/Containers)有彻底的读写权限,也能够运转坐落那里的程序。

构建 Mac App Store 运用之必备常识

关于沙盒之外文件的拜访,苹果供给了规范的用户交互式拜访文件的办法:NSOpenPanelNSSavePanel。操作体系会在一个独自的进程中显现翻开和保存面板,并扩展咱们运用程序的沙盒,以包括选定的URL,让咱们的运用能够对该URL进行安全规模的拜访。

首先需求装备沙盒的文件拜访权限为只读或读写:

构建 Mac App Store 运用之必备常识

用户挑选文件选项答应拜访用户运用 AppKit 的 NSOpenPanel 和 NSSavePanel 挑选的恣意方位。

<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.files.user-selected.read-write</key>
	<true/>
</dict>

然后运用NSOpenPanel进行拜访。

构建 Mac App Store 运用之必备常识

经过NSOpenPanelNSSavePanel实现安全规模内的文件拜访,会随着程序的关闭而结束。如何让运用程序下次运转时依旧坚持对文件的拜访?

Security-Scoped URL 书签继续拜访文件

假如想为沙盒运用程序供给对文件体系资源的持久拜访,则有必要启用security-scoped bookmark

在沙盒权力文件.entitlements装备

 <key>com.apple.security.files.bookmarks.app-scope</key>
 <true/>

构建 Mac App Store 运用之必备常识

运用Foundation创建一个具有明确安全规模的URL书签,运用程序能够存储并检索,不管咱们的运用程序是否在拜访期间退出,都能够在随后拜访URL上的资源。

func saveBookmarkWithFilePath(filePath: String) ->Data? {
    let url = NSURL.fileURL(withPath: filePath)
    //假如运用程序不需求在随后的拜访中写入文件,
    //请在函数的`options`字段传入`securityScopeAllowOnlyReadAccess`。
    let data = try? url.bookmarkData(options: .withSecurityScope)
    if let data {
        UserDefaults.standard.set(data, forKey: filePath)
        UserDefaults.standard.synchronize()
    }
    return data
}

拜访文件的URL书签,有必要遵从:解析书签为Url –> 重建书签(if need) –> 敞开拜访 startAccessingSecurityScopedResource()–> 履行操作 –> 停止拜访 stopAccessingSecurityScopedResource()。详见下列代码注释

///拜访文件的URL书签
func accessingSecurityScopedResourceWithFilePath(filePath:String)->Bool {
    let data = UserDefaults.standard.object(forKey: filePath) as? Data
    var success = false
    if let data {
        var isStale = false
        //1.解析书签
        let url = try? URL(resolvingBookmarkData: data, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
        if isStale, let url { //2.bookmarkDataIsStale == true 更新书签
            let data = try? url.bookmarkData(options: .withSecurityScope)
            if let data {
                UserDefaults.standard.set(data, forKey: filePath)
                UserDefaults.standard.synchronize()
            }
        }
        if let url {
            ///3. 敞开安全规模内的文件拜访
            success = url.startAccessingSecurityScopedResource()
        }
    }
    return success
}
///4. 履行文件拜访操作
///.....
///5. 吊销安全规模内的文件拜访
func stopAccessingSecurityScopedResourceWithFilePath(filePath:String) -> Bool {
    let data = UserDefaults.standard.object(forKey: filePath) as? Data
    var success = false
    var isStale = false
    if let data {
        //1.解析书签
        let url = try? URL(resolvingBookmarkData: data, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
        if let url {
            ///2. 吊销安全规模内的文件拜访
            url.stopAccessingSecurityScopedResource()
            success = true
        }
    }
    return success
}

进程之间同享 URL 书签

运用程序也能够把这个书签传递给另一个进程,比方启动署理或XPC服务。假如要拜访接纳到的进程中的资源,需求遵从以下示例:

do {
    let location = try URL(resolvingBookmarkData: bookmark)
    defer {
      location.stopAccessingSecurityScopedResource()
    }
    // Use the resource at the location URL.
}
catch let error {
    // Handle any errors.
} 

接纳进程在解析书签时隐式地试图扩展其沙盒以覆盖书签资源,就像它在解析期间调用startAccessingSecurityScopedResource()相同。要避免在解析书签数据时扩展进程的沙盒,请经过选项 withoutImplicitStartAccessing

URL(resolvingBookmarkData:bookmarkData, bookmarkDataIsStale: &stale, options: .withoutImplicitStartAccessing)

运用程序组之间同享文件

运用程序组答应单个开发团队开发的多个运用程序拜访同享容器并运用进程间通讯 (IPC) 进行通讯。运用程序或许归于一个或多个运用程序组。作为同一个运用组成员的运用程序都能够拜访一个同享容器~/Library/Group Containers/,它们能够用来交流文件。它们的标识符格局如下:

//iOS
group.<group name>
//macOS
<team identifier>.<group name>

同享容器方位的获取:

// 格局let groupId = "TEAM_ID.com.domain"
let appGroupID = "88L2Q4487U.com.qihoo"
let sharedContainerFolderURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)

桌面、下载、文稿文件的拜访

根据苹果审阅准则5.1.1 法令 – 隐私 – 数据搜集和存储,当咱们拜访DownloadsDesktopDocuments目录,需求在info.plist中装备隐私提示,解说运用为何需求拜访这些方位。

构建 Mac App Store 运用之必备常识

装备结束,当咱们拜访对应目录时,便会弹出对应的授权弹框。

构建 Mac App Store 运用之必备常识

别的,Downloads还需在Target-> Signing & Capabilities -> App Sandbox中根据需求装备相应文件拜访权限。

构建 Mac App Store 运用之必备常识

除此之外,图片、音乐和电影库的目录拜访也需求在此处进行装备,这三个目录只要当运用程序需求办理图片、音乐或电影库,才能够写入,故而大多数状况下都是只读形式。 Mac App Programming Guide

硬件拜访

沙盒运用程序需求拜访体系上的硬件服务,USB、打印或内置摄像头和麦克风,也需求敞开对应的沙盒权力。

构建 Mac App Store 运用之必备常识

对应的沙盒权力文件.entitlements装备

<key>com.apple.security.device.audio-input</key>
      <true/>
<key>com.apple.security.device.bluetooth</key>
      <true/>
<key>com.apple.security.device.camera</key>
      <true/>
<key>com.apple.security.device.usb</key>
      <true/>

一同也不要忘记装备隐私拜访的描绘字符串,解说运用程序需求拜访的原因

构建 Mac App Store 运用之必备常识

当然地址簿、方位、日历这些个人信息与硬件信息相同同属沙盒下受维护的资源,也需求敞开权力并装备对应的隐私描绘。

构建 Mac App Store 运用之必备常识

网络安全

苹果运用ATS(运用程序传输安全)来进步一切运用程序和运用程序扩展的隐私和数据完整性,要求运用所进行的网络衔接有必要经过传输层安全(TLS)协议,并运用可靠的证书和暗码来确保安全 (HTTPS) 。ATS 会阻挠不符合最低安全要求的衔接。

ATS 在 iOS 9.0 或 macOS 10.11 或 更高版本的运用中是默许敞开的。当运用程序链接到旧版的SDK 时,不管运用程序运转在哪个版本的操作体系上,ATS 都会被禁用。

假如在Mac运用中运用网络请求,则需求先装备权力:

构建 Mac App Store 运用之必备常识

<!-- 答应传出衔接 ->
<key>com.apple.security.network.client</key> 
<true/>
<!-- 答应传入衔接 ->
<key>com.apple.security.network.server</key>
<true/>

假如在运用中要运用HTTP,能够装备运用的元数据info.plist:

  1. 对一切网络衔接禁用ATS,彻底运用HTTP进行网络衔接

构建 Mac App Store 运用之必备常识
2. 针对部分内容衔接禁用ATS,如:Web ContentMedia

构建 Mac App Store 运用之必备常识

  1. 针对特定域名禁用ATS

构建 Mac App Store 运用之必备常识

运用采用上述办法,提交到App Store审阅时,需求供给无法树立安全链接的理由。Developer Forums

进程

在 Mac OS 运用的沙盒环境下,进程间通讯(IPC)会变得愈加杂乱,由于传统的IPC办法或许会受到约束。以下是一些进程间通讯的计划:

  1. XPC

    XPC 是 Apple 供给的一种根据音讯传递的进程间通讯机制。XPC 支持在运用间创建安全的衔接。 在沙盒环境中,XPC 能够经过权限别离进一步进步安全性,将一个运用程序分成更小的部分,负责运用程序的一部分行为。每个XPC服务都有自己的沙盒,所以XPC服务能够更容易实现恰当的权限别离。这使得它在沙盒环境中十分安全可行。一同,XPC 也是苹果官方引荐的进程间通讯办法。Creating XPC services

  2. NSDistributedNotificationCenter

    NSDistributedNotificationCenter 答应运用之间发送和接纳通知。这种办法在沙箱环境中是有约束的,在苹果文档 Protecting user data with App Sandbox 与沙盒不兼容的条款中有说到:沙盒环境是不能运用NSDistributedNotificationCenter向其他使命发送 userInfo。

  3. App Groups 和同享容器

    运用App Groups和同享容器,能够在沙盒运用之间同享文件和数据。尽管不是直接的IPC,无法实时通讯,但能够用于同享信息。这个在上文有说到。

  4. Mach Ports

    Mach ports 是底层的进程间通讯机制,用于在Mac OS内核中进行进程通讯。它们能够在沙盒运用之间运用,前提是多个进程有必要是同一个运用程序组,一同需求慎重处理权限和阻隔。缺点是 Mach ports 过于底层,编程杂乱。且苹果文档 Mac Technology Overview 有说到:

Note: Mach ports are another technology for transferring messages between processes. However, messaging with Mach port objects is the least desirable way to communicate between processes. Mach port messaging relies on knowledge of the kernel interfaces, which might change in a future version of OS X. The only time you might consider using Mach ports directly is if you are writing software that runs in the kernel.

  1. Sockets

    UNIX Domain Sockets 也是一种用于进程间通讯的办法,与 Mach ports 状况相同,IPC的进程有必要是同一个运用程序组,一同需求慎重处理苹果关怀的沙盒下权限和安全问题。

除了上述五种办法还有同享内存、AppleEvents、Pasteboard等,具体的实现细节能够参阅书籍:【Interprocess Communication with macOS – Apple IPC Methods】

最终在引用一段苹果文档 App Groups Entitlement,关于沙盒进程IPC的表述:

Apps within a group can communicate with other members in the group using IPC mechanisms including Mach IPC, POSIX semaphores and shared memory, and UNIX domain sockets. In macOS, use app groups to enable IPC communication between two sandboxed apps, or between a sandboxed app and a non-sandboxed app.

参阅

App Sandbox

App Groups Entitlement

Preventing Insecure Network Connections

IOS Using App Groups for communication between macOS/iOS apps from the Same Vendor

App Store Review Guideline

Best Practices for Submitting Scriptable and AppleScript Apps to the Mac App Store