스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
환경 제약 조건으로 Mac 보호하기
환경 제약 조건을 채택하여 Mac 애플리케이션의 보안을 개선하세요. 프로세스 실행에 대한 제약을 설정하고, 실행 에이전트와 실행 데몬이 조작되는 걸 막고, 주소 공간에서 원치 않은 코드가 실행되는 걸 방지하는 방법을 알아봅니다.
챕터
- 0:00 - Introduction
- 2:47 - Environment constraints
- 7:48 - Defining constraints
- 10:16 - Adopting constraints
- 16:22 - Wrap-up
리소스
관련 비디오
WWDC22
WWDC20
-
다운로드
♪ ♪
안녕하세요 제 이름은 Robert Kendall-Kuppe이며 '환경 제약 조건'이라는 macOS의 새 기능을 설명할 거예요 기능을 많이 사용하는 Mac 앱은 하나의 프로세스나 파일 이상이죠 프레임워크와 라이브러리를 통해 여러분의 앱이나 다른 개발자의 코드를 재사용할 수 있어요 헬퍼 툴, 헬퍼 앱과 XPC 서비스는 작업을 나누고 공격 표면을 감소하게 해 주죠 실행 에이전트, 실행 데몬과 로그인 항목들은 백그라운드나 사용자 로그인 때 작업을 하게 해 줘요 앱 확장 프로그램도 다른 앱에 유용한 기능을 제공하죠 하지만 여러분의 앱이 적대적 환경에서 실행돼요 앱 설계자들은 알려지지 않은 소프트웨어의 동시 실행이나 프레임워크 이용의 잠재적 효과를 고려해야 하죠 헬퍼 툴이나 XPC 서비스 실행으로 해커가 여러분의 키체인 데이터에 접근할 수 있게 될까요? 여러분의 iCloud 데이터나 다른 권한은 어떨까요? 여러분의 프로세스에 예상 밖의 코드가 인젝션되면 어떨까요? 실제 부모와 자식의 관계처럼 부모 프로세스는 자식의 행동에 엄청난 영향력을 행사해요 macOS에서는 다른 프로세스를 posix_spawn 하는 경우 부모가 자식 프로세스의 거의 모든 입력을 제어하게 되죠 부모 프로세스가 자식의 시스템 자원 접근도 제한해요 이런 수준의 제어로 인해 자식이 예상 밖의 코드를 로딩하여 예상 밖의 기능을 실행하거나 프로세스가 공격에 취약해지도록 행동할 수 있죠 부모 자식의 관계를 넘어서 프로세스는 자신이 스폰되는 디스크 레이아웃을 신뢰해요 악성 프로세스는 디스크의 파일을 수정하고 대상 프로세스에 예상 밖의 데이터를 주입하여 대상 프로세스에서 런타임 보호를 제거하고 시스템의 영속 실행 권한을 얻거나 프로세스의 권한을 빼앗죠 이런 위협에 맞서 macOS가 앱을 보호하는 도구들을 제공해요 특히 App Sandbox를 채택하여 앱이 침투됐을 때 영향을 최소화할 수 있죠 Hardened Runtime과 라이브러리 유효성 검사로 런타임 때 프로세스의 무결성을 보호할 수 있어요 Gatekeeper와 공증은 고객 시스템을 알려진 악성 코드로부터 보호하죠 이전에 언급했던 위협을 생각해 보면 현재의 보호는 프로세스 실행에 초점이 맞춰져 있고 실행 환경을 신경 쓰지 않았어요 그래서 이번에 환경 제약 조건을 도입하는 거죠 환경 제약 조건은 새로운 수준의 제어를 통해 프로세스가 실행되는 상황이나 프로세스에 코드를 조합하는 방식을 통제할 수 있어요 나머지 세션에서는 환경 제약 조건이 어떤 식으로 macOS의 보안 아키텍처에 맞아떨어지는지 논의하고 환경 제약 조건의 구조를 파악하여 여러분 앱에 환경 제약 조건을 채택하는 법을 다루도록 하죠 잠시 화제를 전환하여 macOS 보안 스택을 얘기할게요 기본적으로 macOS는 이 모든 기술을 활용하여 부트체인을 보호하고 운영 체제의 무결성을 확보하며 권한 분리를 시행하고 악성 소프트웨어로부터 보호하죠 macOS Ventura부터 환경 제약 조건을 사용하여 운영 체제 프로세스의 관계를 보호하고자 했어요 운영 체제를 위한 새로운 차원의 보안을 제공하죠 macOS Sonoma에서 환경 제약 조건 사용을 확대하여 여러분의 앱에서도 사용할 수 있게 했어요 그럼 환경 제약 조건이 뭘까요? 근본적으로는 코드를 기술하는 방식이에요 어떤 코드인지만 설명하지 않고 시스템에서 어떤 방식으로 존재하고 실행되는지 설명하죠 macOS에서는 환경 제약 조건을 다양한 목적으로 사용해요 프로세스가 신뢰할 수 있는 번들 리소스를 사용하게 하고 운영 체제 프로세스를 서명된 시스템 볼륨에서 실행하죠 권한이 있는 데몬이 예상 밖의 인수나 Mach 포트와 실행되지 않도록 보호된 launchd.plist를 바탕으로 시스템 데몬이 실행되게 해요 시스템 앱의 공격 표면을 줄이기 위해 Launch Services에서 앱으로 실행되도록 요구하며 백그라운드 항목에 대한 사용자 승인을 강화하기 위해 환경 제약 조건을 이용하여 변화를 감지하죠 여러분의 앱도 환경 제약 조건을 채택해야 하는지 궁금할 거예요 여기서 강조하고 싶은 건 환경 제약 조건이 선택 사항이지만 모든 앱의 공격 표면을 줄일 수 있다는 거죠 이후 세션에서 구체적인 예시를 들게요 한 가지 말씀드리자면 환경 제약 조건이 앱에 다수의 프로세스가 있거나 서로 다른 개발자 팀이 서명한 코드를 실행할 때 특히 유용하다는 거예요 환경 제약 조건에는 몇 가지 종류가 있는데 실행 제약 조건부터 얘기해 보죠 실행 제약 조건은 특정 바이너리에 임베딩돼 있고 프로세스의 프로퍼티나 부모가 될 수 있는 프로세스의 프로퍼티 또는 이를 책임질 수 있는 프로세스의 프로퍼티를 정의해요 이런 프로퍼티를 칭할 용어는 각각 '자체 제약 조건' '부모 프로세스 제약 조건' '책임 프로세스 제약 조건'이죠 바이너리에 3개를 모두 적용하거나 제일 적당한 걸 고르세요 실행 제약 조건이 임베딩 된 프로세스는 필수 프로퍼티가 하나라도 만족하지 않으면 실행되지 않죠 이제 일부 프로세스 관계를 살펴보고 실행 제약 조건을 사용해 보호하는 법을 얘기해 볼게요 먼저 여러분의 앱이 MyDemo.app이라고 가정하죠 MyDemo.app에 자체 제약 조건을 설정하여 Launch Services에서 앱으로 실행되도록 요구할 수 있어요 여러분의 앱이 XPC 서비스에 연결을 요구하면 launchd가 XPC 서비스를 스폰하고 XPC 서비스의 부모가 되지만 여러분의 앱이 XPC 서비스를 책임져요 MyXPCDemo.xpc에 책임 프로세스 제약 조건을 설정하여 MyDemo.app만이 프로세스를 책임진다고 나타낼 수 있죠 만약 여러분의 앱이 NSTask나 posix_spawn으로 헬퍼를 실행하면 앱이 그 헬퍼의 부모이자 책임을 지게 돼요 부모 프로세스 제약 조건을 MyFirstHelper에 설정하여 MyDemo.app만이 부모가 될 수 있게 요구할 수 있죠 첫 번째 헬퍼가 두 번째 헬퍼를 posix_spawn 하는 경우 첫 번째 헬퍼가 두 번째 헬퍼의 부모지만 앱이 두 번째 헬퍼를 책임져요 MySecondHelper를 위해 부모 프로세스 제약 조건을 설정해 MyFirstHelper를 통해서만 실행될 수 있도록 하고 책임 프로세스 제약 조건을 설정하여 MyDemo.app만이 책임지게 할 수 있죠 launchd plist의 실행 에이전트와 실행 데몬에 환경 제약 조건을 명시할 수도 있어요 SMAppService API를 이용하여 plist를 등록할 때 운영 체제에서 제약 조건을 만족하는 프로세스만 plist를 대신하여 실행될 수 있도록 하죠 이 기능을 사용하면 악성 코드가 여러분 앱 백그라운드 활동의 사용자 승인을 바탕으로 영속 실행 권한을 얻지 못해요 마지막으로 라이브러리 로딩 제약 조건을 이용하여 주소 공간에 로딩되는 코드를 구체적으로 제어할 수 있죠 라이브러리 로딩 제약 조건 전에는 라이브러리 유효성 검사를 채택하거나 하지 않았죠 라이브러리 유효성 검사는 여러분이나 Apple이 서명한 코드를 프로세스가 실행하도록 하죠 라이브러리 로딩 제약 조건이 있으면 유효성 검사의 허용 수준보다 덜 제한적인 코드를 기술하면서 임의의 코드가 프로세스에 로딩되는 걸 방지해요 하지만 Apple이 서명한 코드는 프로세스 로딩에서 배제할 수 없으며 여러분의 코드는 1개 이상의 프로퍼티를 명시해야 허용할 수 있어요 이제 환경 제약 조건이 뭔지 알았고 어떻게 사용할 수 있는지 알았으니 어떻게 정의되는지 논의해 보죠 환경 제약 조건은 코드가 충족할 일련의 조건을 기술해요 딕셔너리로 인코딩되어 있으며 키가 나타내는 건 코드에 관해 참인 사실이거나 사실과 술어 사이의 필수 관계를 나타내는 연산자죠 최고 수준에서는 암시적으로 각 키-값 쌍의 결과가 AND로 묶여 제약 조건을 만족하는지 결정해요 이는 딕셔너리기 때문에 딕셔너리 수준 당 키가 한 번만 나타날 수 있죠 여러분이 사용할 만한 사실들을 살펴볼게요 왼쪽에는 관련된 환경 제약 조건 키가 있고 오른쪽에는 codesign 명령의 출력값이 있죠 서명-식별자 키를 통해 주어진 코드에 고유한 string을 명시하도록 하며 해당 코드의 버전에 동일하게 유지돼요 서명-식별자 키는 codesign 출력값의 식별자 필드를 참조하죠 cdhash 키는 허용되는 코드에 대한 고유의 해시를 명시하게 해 줘요 팀-식별자 키는 특정 개발팀이 서명한 코드를 명시할 수 있게 해 주죠 사실이 코드의 특정 프로퍼티를 나타낸다면 연산자를 사용하여 일련의 사실을 논리적으로 결합하거나 사실에 대한 용인되는 값들을 정의할 수 있어요 여러분의 예상대로 $and와 $or 연산자는 술어의 딕셔너리를 명시할 수 있게 하여 결정된 이후에 논리적으로 결합하죠 $and-array와 $or-array 연산자는 AND와 다수의 $or 술어나 OR와 다수의 $and 술어를 원할 때 딕셔너리 네스팅을 제한하기 위해 존재해요 마지막으로 $in 연산자는 값의 배열이 사실을 만족하도록 명시하게 해 주죠 예시 제약 조건을 살펴볼게요 왼쪽에는 plist로 나타낸 제약 조건이 있고 오른쪽에는 XML의 의미를 보여 주는 의사코드가 있죠 plist의 최고 수준에는 $or-array 키가 있어요 이 값은 튜플 3개의 배열이죠 각 튜플은 연산자와 연산자가 적용될 딕셔너리를 포함하므로 이 제약 조건이 여러분 팀의 식별자가 서명한 모든 코드나 두 번째 팀 식별자가 서명한 B 라이브러리나 세 번째 팀 식별자가 서명한 C 라이브러리를 허용해요 첫 번째 튜플은 단일 요소이므로 $or 연산자를 사용할 수도 있었죠 이제 제약 조건들을 정의할 수 있으니 프로젝트에 채택하는 방법을 살펴보죠 논의를 진행하기 위해 주요 앱이 있다고 가정하고 그 안에 실행 에이전트와 헬퍼 도구, XPC 서비스를 포함하는 프레임워크와 다른 개발 팀에서 서명한 라이브러리가 있다고 가정할게요 환경 제약 조건이 완화할 수 있는 잠재적 문제를 생각해 보죠 어쩌면 헬퍼 툴에 일부 권한을 지정했을지도 몰라요 키체인 데이터나 iCloud 컨테이너의 접근 권한일 수 있죠 이런 때 헬퍼 툴이 앱 외에 다른 것에 의해 실행되길 원하지 않을 거예요 여러분의 앱만이 헬퍼 툴을 실행하게 하려면 헬퍼 툴에 부모 프로세스 제약 조건을 설정하면 되죠 그 작업을 위해 코드 요건 plist 파일을 만들어서 주요 앱의 팀 식별자와 서명 식별자를 요구하세요 그리고 'Launch Constraint Parent Process Plist' 설정에서 헬퍼 툴 서명 구성에 제약 조건을 추가하세요 더 자세히 살펴보죠 Xcode의 데모 프로젝트에 언급했던 프로퍼티가 있어요 MyDemo.app이 주요 앱이고 demohelper가 헬퍼 툴이죠 앱을 실행할게요 버튼을 누르면 앱이 헬퍼 툴을 스폰하고 헬퍼 툴이 일부 작업 후에 앱에 응답을 제공하죠 Terminal에서 demohelper의 시그니처를 볼게요
실행 제약 조건이 설정돼 있지 않아서 demohelper를 실행할 수 있죠 근데 이걸 보세요 demohelper를 --cloud 인자와 실행하면 demohelper가 앱의 iCloud 데이터에 접근할 수 있죠 우리는 임의의 프로세스가 demonhelper를 실행하여 iCloud 데이터를 바꾸는 걸 원치 않아요 다시 Xcode로 가서 demohelper에 부모 제약 조건을 설정하죠
이미 제약 조건 plist 파일을 통해 주요 앱 MyDemo를 식별하고 있어요
서명 구성에 제약 조건을 추가하죠
이제 앱을 다시 빌드하세요
다시 앱을 실행하면...
주요 앱이 여전히 헬퍼를 스폰할 수 있죠
근데 Terminal로 돌아가면...
demohelper에 실행 제약 조건이 있으며... Terminal에서 실행할 수 없어요 실행 제약 조건 위반으로 실행이 멈춘다면 제약 조건을 위반했다는 내용의 크래시 리포트가 생성되죠 이제 환경 제약 조건으로 피할 수 있는 문제를 더 알아보죠 XPC 서비스를 이용하여 서로 다른 프로세스의 권한 분리를 권장하지만 XPC 서비스를 빌드할 때 번들에서 해당 서비스를 추출하여 다른 코드에서 호출할 수 있죠 XPC 서비스에 일부 권한을 할당했다면 확실한 작업으로 예상했던 프로세스만 해당 권한에 접근할 수 있도록 하세요 여러분의 코드가 XPC 서비스에 접근하는 걸 보장하는 방법은 책임 프로세스 실행 제약 조건을 설정하는 거죠 여기서 실행 제약 조건 plist가 여러분 팀이 서명한 코드를 허용하고 각 번들 내 프로세스의 서명 식별자 목록을 통해 XPC 서비스에 접근할 수 있어요 'Launch Constraint Responsible Process Plist' 설정의 서명 구성에서 제약 조건을 추가할 수 있죠 고려할 문제가 또 있어요 macOS Ventura부터 앱을 대신해 설치한 백그라운드 태스크의 사용자 승인을 요구하므로 사용자가 여러분의 앱으로만 작업이 진행되는 걸 기대해요 plist의 실행 예상 코드를 공격자가 대체할 수 있으면 여러분의 앱을 통해 영속적 백그라운드 실행의 권한을 얻을 수 있죠 등록된 plist가 예상되는 코드만 실행하도록 보장하려면 SpawnConstraint 키로 launchd plist 제약 조건을 설정하세요 SpawnConstraint가 포함된 전체 launchd plist예요 제약 조건이 팀과 DemoMenuBar 에이전트를 식별하죠 끝으로 라이브러리 로딩을 얘기할게요 다른 개발 팀의 라이브러리를 의무적으로 수정 없이 링크해야 해서 Hardened Runtime으로 앱을 공증받아야 한다면 disable-library-validation 권한을 채택해야 하는데 안타깝게도 그러면 앱에서 아무나 서명한 코드를 로딩할 수 있으며 라이브러리를 제공한 신뢰하는 개발자로 제한할 수 없어요 이 문제를 해결하려면 라이브러리 로딩 제약 조건을 채택해야 하죠 이건 여러분의 팀이나 신뢰하는 라이브러리 공급자가 서명한 코드를 로딩하도록 허용하는 제약 조건이에요 특정 라이브러리나 1개 이상의 서명-식별자 사실을 사용하는 라이브러리로 제약을 강화할 수 있죠 실행 제약 조건과 같이 Xcode 서명 구성의 'Library Load Constraint Plist'를 설정하는 경우 라이브러리 로딩 제약 조건이 프로세스에 서명돼요 환경 제약 조건은 어디에서 구할 수 있을까요? 라이브러리 로딩 제약 조건과 launchd plist 제약 조건은 모든 macOS 버전을 타겟하는 앱에 포함할 수 있으며 macOS Sonoma부터 집행되죠 실행 제약 조건은 macOS 13.3 이후 버전에 추가할 수 있고 macOS 13.3부터 집행돼요 환경 제약 조건에 사용될 수 있는 일련의 키와 값이 macOS 버전마다 다를 수 있음을 유의하세요 종합적인 이용 가능 정보는 문서를 참고하세요 프로세스 관계, launchd plist와 라이브러리를 살펴보고 환경 제약 조건을 이용한 안전한 앱을 만들어 보세요 시청해 주셔서 감사해요 ♪ ♪
-
-
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>
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.