[macOS] Sandbox 정책 극복기 Accessing Security Scoped Resource

by digipine posted Nov 02, 2017
?

Shortcut

PrevPrev Article

NextNext Article

ESCClose

Larger Font Smaller Font Up Down Go comment Print

안녕하세요.

macOS용 어플을 개발해 App Store에 올리는 경우라면 Sandbox 정책을 철저하게 지켜야 하는 것은 모두 아는 사실인데요.

그런데 이 Sandbox 정책이 무지막지한 것이라 몇몇 개발자들은 분노해서 App Store를 떠나기도 했습니다.

특히 비디오 오디오 재생기를 개발하는 경우 난감합니다. 자막이나 Playlist 같은 기능이 있는 경우 아주 난감합니다.

db2f9367ec38ce1eab1c08f5586afd43.png

 

 

 

그림에서 보는 것과 같이 샌드박스 정책 하에서는 다른  App이나 System의 파일들을 접근 자체가 불가능합니다.

그래서 비디오 파일을 Open 해도 별도의 자막 파일의 경우 다른 App에서 만든 파일이라고 접근 자체가 불가능합니다.

물론 사용자가 자막을 수동으로 Open 해주면되지만 매우 번거럽습니다.

Playlist 기능은 더 심각한데요. Playlist를 만들어 이 리스트에 따라 음악 파일에 접근하려고 하면 당연 다른 앱에서 만든 파일이라고 접근 자체가 안됩니다. 만약 100개의 곡이 각각 다른 폴더에 있다면 Playlist의 음악을 듣기 위해서 사용자가 100번 Open Dialog로 선택해야한다는 이야기입니다. Playlist 사용하는 의미가 전혀 없어지는 것입니다.

 

그러면 전혀 해결책이 없는 것 일까요?

아닙니다. 애플은 그렇게 무지막지한 회사가 아닙니다. 논리적으로 접근하면 해결이 가능한데요.

샌드박스 정책 상 사용자가 직접 선택한 파일이나 폴더 내의 파일은 접근이 가능합니다.  (com.apple.security.files.user-selected.read-write)

즉 Open Dialog에서 사용자가 선택한 파일이나 폴더 또는 그 폴더내의 파일은 App에서 접근이 가능해 집니다.

 

그러면 이런 생각을 해봅니다. 그냥 편하게 파일 시스템의 모든 자원에 접근할 수 있는 Super User 같은 권한을 줄 수는 없을까?

 

가능한 방법이 있습니다.

App 실행시 Open Dialog를 띄워서 사용자에게 root folder(/)를 선택하게 하면 결과적은 App에서 디스크 내의 모든 파일이 접근 가능해집니다. 특히 마운트된 USB memory stick이나 외장하드 또는 네트워크 공유 폴더를 모두 포함해서 모두 접근이 가능해 집니다.

 

 

    NSOpenPanel* openDlg = [NSOpenPanel openPanel];

    [openDlg setCanChooseFiles:NO];

    [openDlg setCanChooseDirectories:YES];

    [openDlg setAllowsMultipleSelection:YES];

    [openDlg setMessage:NSLocalizedString(@"Sandbox Permisson"@"Sandbox Permisson")];

    [openDlg setPrompt:NSLocalizedString(@"Allow Permission"@"Allow Permission")];

        

    [openDlg setNameFieldStringValue:@"/"];

 

    [openDlg setDirectoryURL:[NSURL fileURLWithPath:@"/"]];

...

 

 

위와 같이 OpenPanel을 이용해서 강제로 루트 폴더를 지정하고 사용자에 선택하게 하면됩니다.

샌드박스 정책 따위는 극복할 수 있습니다. 

물론 이 방법은 App Store에서 거절 당하지 않고 통과된다고 합니다. (사용자가 선택한 것이라서 애플도 거절할 이유가 없지요.)

 

그런데 한가지 더 매번 이렇게 App을 실행할 때마다 사용자에게 선택하게 한다면 조금 불편합니다.

 

그래서 애플에서 만든 것이 Security Scope 라는 것인데요. (자세한 내용은 애플사의 문서를 확인하세요.)

사용자가 한번 지정해 놓은 폴더나 파일은 다음 번에 App을 실행 할때 저장(Bookmark)해 두었다가 자동으로 권한을 확득할 수 있습니다. 저장하는 방법은 NSUserDefault Class를 사용해도되고 아니면 별도로 파일에 저장해도 됩니다.

 

먼저 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">

<plist version="1.0">

<dict>

<key>com.apple.security.app-sandbox</key>

<true/>

<key>com.apple.security.assets.music.read-write</key>

<true/>

<key>com.apple.security.files.user-selected.read-write</key>

<true/>

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

<true/>

</dict>

</plist>

 

위 테스트 상자를 보면 user-selected.read-write 항목과 bookmarks.app-scope 항목이 필 수 입니다.

 

아 그리고 assets.music.read-write의 경우는 음악 폴더 접근이 가능해지는데 music  App 이 아니면 설정하지 마세요. 거절 당합니다.

video App의 경우는 assets.video.read-write 라고 해서 비디오 폴더를  접근 가능하게 할 수 있습니다.

 

이렇게 설정 했다고 하면 먼더  Open Panel 에서 지정한 폴더를 저장해 두어야 합니다.

 

         if( files != nil && [files count] == 1 ) {

            NSURL *url = [files objectAtIndex:0];

            

            NSData *bookmark = nil;

            NSError *error = nil;

            bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope

                     includingResourceValuesForKeys:nil

                                      relativeToURL:nil // Make it app-scoped

                                              error:&error];

            if (error) {

                NSLog(@"Error creating bookmark for URL (%@): %@", url, error);

                [NSApp presentError:error];

            }

            

            NSLog(@"bookmark: %@", bookmark);

            NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

            [userDefaults setObject:bookmark forKey:SANDBOX_ROOT_BOOKMARK];

            [userDefaults synchronize];

 

        }

 

 

 

그리고 프로그램 시작시(대부분 AppDelegate 부분이 겠지요.) 에서 다음과 같이 저장된 폴더를 접근 Scope로 지정하시면 됩니다.

 

     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

    //[userDefaults removeObjectForKey:SANDBOX_ROOT_BOOKMARK];

    NSData *bookmark = [userDefaults objectForKey:SANDBOX_ROOT_BOOKMARK];

    

 

    

    if(bookmark != nil) {

        sandBoxUrl = [NSURL URLByResolvingBookmarkData:bookmark

                                               options:NSURLBookmarkResolutionWithSecurityScope

                                         relativeToURL:NULL

                                         bookmarkDataIsStale:NO

                                         error:NULL];

        NSLog(@"bookmark url : %@", [sandBoxUrl description]);

        [sandBoxUrl startAccessingSecurityScopedResource];

    }

    else {

        sandBoxUrl = nil;

    }

 

 

 

그리고 마지막으로 App을 종료할 때 다음과 같이 접근을 중지 해두셔야 합니다.

 

    if(sandBoxUrl != nil)

        [sandBoxUrl stopAccessingSecurityScopedResource]; 

 

 

간단한 코딩으로 App에 하드 디스크, 외장 디스크, 네트워크 공유 폴더에 모두 접근 할 수 있는 권한을 획득했습니다.

바이러스 백신이나 시스템 관련 App에서 사용하시면 아주 유용할 것 입니다.

 

 

 

역시 우물은 목마른 사람이 파야하는 가 봅니다.