V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
luoweibin
V2EX  ›  程序员

APP 沙盒那些坑

  •  
  •   luoweibin · 2017-12-17 13:06:03 +08:00 · 3637 次点击
    这是一个创建于 2534 天前的主题,其中的信息可能已经有所发展或是发生改变。

    掉坑

    记得在笨鸟海淘做仓库管理系统的时候,我用 Windows 写仓库管理软件。当时系统用的是 Windows 10。微软的官网对 UWP 吹得神乎其神,各种设备兼容,一套代码多种设备运行云云,很动人。最后,在功能快完成的时候,掉坑了。APP 没有打印的权限。那是我第一次掉进 APP 沙盒的坑。

    最近一次掉沙盒的坑是上周,写 PNG Zip (一个 PNG 压缩软件,App Store 下载链接)的时候。起初,我觉得一周的时间应该能完成所有的工作。最终,我花了一周半的时间。

    所以,我觉得我应该跟你分享一下这个掉坑和填坑的过程。看完这篇文章,你应该对沙盒机制有了一定程度的了解,如何在沙盒机制下编程,最后我还会给你一些建议。

    在说掉坑填坑之前,我们先了解一下,沙盒是什么。

    沙盒是什么

    在我们日常用到的操作系统中,应用沙盒机制已经普遍存在了。如 iOS、Android、macOS、Windows。由于手机是我们每天都用的贴身工具,里面保存了非常多的隐私数据。所以手机操作系统中,所有应用程序都是沙盒隔离的。而 PC 系统在这两年也开始推广沙盒的应用,如 macOS、Windows 10。今天我要讲的是 macOS 的沙盒。

    我们先来看看沙盒的定义。在 Wikipedia 中,沙盒是这么定义的。

    In computer security, a sandbox is a security mechanism for separating running programs, usually in an effort to mitigate system failures or software vulnerabilities from spreading. It is often used to execute untested or untrusted programs or code, possibly from unverified or untrusted third parties, suppliers, users or websites, without risking harm to the host machine or operating system.

    在计算机安全中,沙盒是一种隔离应用程序的机制,通常致力于抑制系统故障或者软件故障的扩散。它通常用于执行未测试的或者不受信任的,来自于未认证或者不受信任的第三方、供应商、用户或者网站的应用程序和代码,以免主机和操作系统受到破坏。

    在 macOS 中,沙盒是这样的。

    App Sandbox is an access control technology provided in macOS, enforced at the kernel level. It is designed to contain damage to the system and the user ’ s data if an app becomes compromised.

    应用沙盒是一种由 macOS 提供的内核级别的访问控制技术。它是为了控制系统或者用户数据受到的破坏而设计的(当一个应用程序受到劫持的时候)。

    这两个定义说的意思差不多,就是对应用进行隔离。包括运行环境和用户数据。具体就是说,第一次打开应用,你上不了网,你拍不了照片,你写不了文章。所有的一切你都需要用户授权。

    为什么需要沙盒

    答案是安全。现如今应用变得非常的复杂。而复杂的系统,不管你多么努力,制定多么严格的安全编程规范,测试得多么地充分,攻击者总能找到漏洞。沙盒的作用就是当你的应用受到攻击时,系统和用户数据受到的破坏能被控制在一定的范围内。

    如何在沙盒环境下编程

    在沙盒环境下,我们需要做一些权限相关的额外工作。

    • 声明权限
    • 获取权限
    • 保存权限
    • 重新获取权限
    • 释放权限

    1 )声明权限

    在 macOS 中,通过Entitlements来声明权限。创建 xcode 工程时,xcode 会默认创建appname.entitlements的 plist 文件。开发者可以编辑这个文件,或者通过 target 的 Capabilities 标签配置。

    aipng.entitlement

    在 Capabilities 只能声明部分权限,有些权限需要手工编辑.entitlement 文件。

    2 )获取权限

    声明了权限后,应用就可以使用相应的编程接口来实现所要的功能。例如,在用户 Home 目录读写文件。你可以通过 NSFileManager 提供的 API 来获取相应的路径。

    swift
    
    class NSFileManager
    
    var homeDirectoryForCurrentUser: URL { get }
    
    Objective-C
    
    interface NSFileManager
    
    @property(readonly, copy) NSURL *homeDirectoryForCurrentUser;
    

    对于用户 Home 目录以外的文件,需要唤起NSOpenPanel来让用户选择,或者通过拖拽的方式将文件拖拽到应用界面中。通过阅读Introduction to Drag and Drop,你可以很快地实现拖拽功能。

    3 )保存权限

    通过用户选择或者拖拽获取到的文件读写权限在应用关闭之后就会丢失。所以,需要一种方法来保存这个权限。macOS 给我们提供了Security Scope Bookmark。NSURL 提供了对应的接口来创建Security Scope Bookmark

    swift 
    
    class NSURL
    
    func bookmarkData(options: NSURL.BookmarkCreationOptions = [], 
    includingResourceValuesForKeys keys: [URLResourceKey]?, 
           relativeTo relativeURL: URL?) throws -> Data
    
    Objective-C
    
    interface NSURL
    
    - (NSData *)bookmarkDataWithOptions:(NSURLBookmarkCreationOptions)options 
         includingResourceValuesForKeys:(NSArray<NSURLResourceKey> *)keys 
                          relativeToURL:(NSURL *)relativeURL 
                                  error:(NSError * _Nullable *)error;
    

    Security Scope Bookmark以 NSData 的对象表示。NSData 可以持久化到文件,也可以通过 IPC 等方式发送给其他进程。在应用重启或者另一个进程接收到 NSData 对象后,即可重新获取权限。如果想要持久化 bookmark,可以使用 NSData 的 writeToURL 方法。

    swift 
    
    class NSData
    
    func write(to url: URL, 
    atomically: Bool) -> Bool
    
    Objective-C
    
    interface NSData
    - (BOOL)writeToURL:(NSURL *)url 
            atomically:(BOOL)atomically;
    

    4 )重新获取权限

    Security Scope Bookmark中保存了 NSURL 的相关信息。我们可以通过 NSURL 提供的接口重新获得保存的 NSURL 对象。

    swift
    
    class NSURL
    
    convenience init(resolvingBookmarkData bookmarkData: Data, 
             options: NSURL.BookmarkResolutionOptions = [], 
          relativeTo relativeURL: URL?, 
    bookmarkDataIsStale isStale: UnsafeMutablePointer<ObjCBool>?) throws
    
    Objective-C
    
    interface NSURL
    
    + (instancetype)URLByResolvingBookmarkData:(NSData *)bookmarkData 
                                       options:(NSURLBookmarkResolutionOptions)options 
                                 relativeToURL:(NSURL *)relativeURL 
                           bookmarkDataIsStale:(BOOL *)isStale 
                                         error:(NSError * _Nullable *)error;
    

    通过 NSURL 重新获取权限。

    swift
    
    class NSURL
    func startAccessingSecurityScopedResource() -> Bool
    
    Objective-C
    
    interface NSURL
    - (BOOL)startAccessingSecurityScopedResource;
    

    5 )释放权限

    一旦不再需要使用权限,尽快释放权限。

    swift
    
    class NSURL
    func stopAccessingSecurityScopedResource()
    
    Objective-C
    
    interface NSURL
    - (BOOL)stopAccessingSecurityScopedResource;
    

    人在江湖飘,哪能不挨刀。掉坑总是难免的。NSURL 提供了一个很容易让人误用的接口。当我第一眼看到这个接口时,我就把它当作持久化 bookmark 的接口了。而且这个接口也确实能持久化 bookmark 数据。然而当你重启应用重新得到 NSURL 对象后会发现,无法获取权限。

    swift
    
    class NSURL
    
    class func writeBookmarkData(_ bookmarkData: Data, 
                              to bookmarkFileURL: URL, 
                         options: NSURL.BookmarkFileCreationOptions) throws
    
    Objective-C
    
    interface NSURL
    
    + (BOOL)writeBookmarkData:(NSData *)bookmarkData 
                        toURL:(NSURL *)bookmarkFileURL 
                      options:(NSURLBookmarkFileCreationOptions)options 
                        error:(NSError * _Nullable *)error;
    

    实际上,这个接口是用于创建别名链接的。

    一些建议

    作为开发者,出于降低项目风险和减少工作量的目的。我的建议是尽可能禁用沙盒机制。因为沙盒这个机制还处于发展阶段。权限控制的粒度和编程接口都还没稳定。

    可以不开启的场景

    • 内部工具
    • 企业应用
    • 非官方应用商店渠道销售的应用

    必须开启沙盒的场景

    • 官方应用商店销售的应用
    • 手机应用

    总结

    应用沙盒提供了一种隔离机制,使得被劫持的应用能够造成的破坏被限制在一定的范围内。同时,沙盒机制是一把双刃剑。他在提供保护的同时,也加重了开发者的负担,产生额外的辅助数据,占用磁盘空间。甚至在沙盒环境下,部分功能无法实现。

    题外话

    说到底,这是个信任问题。软件之间、系统之间、人与人之间需要一种信任。当信任缺失的时候,所有参与到其中的任何一方都要付出代价。

    参考资料

    1. About App Sandox
    2. Introduction to Drag and Drop
    3. NSURL 文档
    4. NSData 文档
    5. NSFileManager 文档
    6. Swift 编程指南
    7. Objective-C 编程指南

    分享

    如果你觉得这篇文章写得不错,请分享给你的朋友。


    反馈

    如果你有不明白的地方,如果你有建设性的建议,如果你想我写某个主题的文章,随时丢个邮件给我。我的邮箱地址是 [email protected]


    圣诞节促销

    PNG Zip 限免


    原文链接

    http://www.jianshu.com/p/403593d433f1


    一个爱跳坑的码农。十年来跳了无数的坑。有些坑填平了,有些坑越过去了,有些坑却永远也无法过去。

    关注公众号,接收 [跳坑的码农] 最新动态。

    跳坑的码农

    11 条回复    2017-12-19 18:29:35 +08:00
    tinywright
        1
    tinywright  
       2017-12-17 20:52:27 +08:00
    macOS 开发的坑很多,参考资料相对 iOS 也少。
    luoweibin
        2
    luoweibin  
    OP
       2017-12-17 23:30:24 +08:00
    @tinywright 确实是,而且 macOS 的系统更新非常频繁,接口非常不稳定。前阵子还遇到 CoreText 渲染错误。
    Dashit
        3
    Dashit  
       2017-12-18 08:59:21 +08:00
    发现有错别字. 权限控制的粒度
    luoweibin
        4
    luoweibin  
    OP
       2017-12-18 10:48:18 +08:00
    @Dashit 谢谢你的回复。“权限控制的粒度”在这里指的是应该控制到什么程度。比如单个文件授权,还是整个目录,还有网络之类的。不知道这么解释可否理解,欢迎给我回复。
    ianisme
        5
    ianisme  
       2017-12-18 11:11:00 +08:00   ❤️ 1
    软件好像有点小问题 建议修复下 5555、
    我把我的一张 png 图片拖拽进去之后,然后关闭软件。我的图片就打不开了,双击图片提示“未能打开文件“ 1.png ”,因为它是空的。”
    luoweibin
        6
    luoweibin  
    OP
       2017-12-18 11:32:39 +08:00
    @ianisme 谢谢你的反馈,能把你压缩的那张图片发送给我吗,我的邮箱是 [email protected] ?另外今天刚更新了 v1.1,修复了一些 bug。欢迎体验。
    Dashit
        7
    Dashit  
       2017-12-18 14:11:53 +08:00   ❤️ 1
    @luoweibin 这就尴尬了.是我理解错了.
    luoweibin
        8
    luoweibin  
    OP
       2017-12-18 15:03:57 +08:00
    @Dashit 没关系。有你的回复就很不错了。对我已经是很大的帮助。
    ianisme
        9
    ianisme  
       2017-12-18 21:18:11 +08:00
    @luoweibin 邮件已发
    luoweibin
        10
    luoweibin  
    OP
       2017-12-19 00:25:42 +08:00
    @ianisme 谢谢你,已经找到问题了。如果选择了输出文件夹里面的图片会导致写入到源文件,甚至会导致崩溃。这个问题在 v1.1.2 版本修复,已经提交审核了,等待审核通过。我也收到了其它用户的反馈。看起来目录操作有点容易让人混淆,正在想有没有更好的操作交互。
    luoweibin
        11
    luoweibin  
    OP
       2017-12-19 18:29:35 +08:00 via iPhone
    @ianisme 审核已经通过了,请更新!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   928 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 19:54 · PVG 03:54 · LAX 11:54 · JFK 14:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.