"어제 손쉬운 사용 권한 줬는데 오늘 또 물어봐. 고장 난 건 없어. 네가 다른 앱을 배포한 거야."
원하든 말든 바이너리는 전부 서명돼 있어
Apple 실리콘에선 네이티브 코드가 서명이 없으면 아예 안 돌아가. 네가 서명 얘기를 한마디도 안 해도 링커가 알아서 서명해줘. 뒤에 인증서가 없는 애드혹 서명이지. 방금 swift build한 결과물에 codesign -dv를 돌려보면 Signature=adhoc이 보일 거야. 프로그램은 잘 돌고, 다 멀쩡해 보여.
함정은 애드혹 서명이 정체성 노릇을 거의 못 한다는 거야. 서명에는 designated requirement가 들어 있어. 나중에 온 코드가 "같은 앱"으로 인정받으려면 만족해야 하는 규칙이지. 애드혹 코드의 규칙은 바이트를 그대로 해시한 값, 곧 cdhash H"e3e8…"야. 다시 빌드하면 바이트가 바뀌고, 해시가 바뀌고, 규칙은 이제 세상에 없는 프로그램을 가리켜.
그 규칙을 누가 쓰냐면
- TCC: 손쉬운 사용, 마이크, 화면 기록, 입력 모니터링, 캘린더 접근을 관리하는 개인정보 시스템이야. 권한은 앱의 코드 규칙에 묶여서 저장돼.
- 키체인: 항목마다 붙은 접근 목록이 그 항목을 만든 앱의 규칙을 신뢰해.
- Gatekeeper와 공증: 내려받은 앱을 열어줄지 말지를 정해.
- iOS의 실행 제약, App Group, 프로비저닝: 전부 서명 신원이랑 팀에 묶여.
그래서 손쉬운 사용이 필요한 애드혹 메뉴 막대 유틸리티는 이렇게 굴어. 권한을 주면 잘 되다가, 다시 빌드하면 macOS가 새 빌드를 낯선 사람 취급해. 가족의 받아쓰기 앱이 딱 이랬어. 빌드할 때마다 손쉬운 사용 권한을 다시 물어보다가, 안정적인 인증서로 서명한 뒤에야 한 번 준 권한이 유지됐어.
규칙의 세 가지 모양
| 무엇으로 서명했나 | designated requirement 모양 | 다시 빌드해도 살아남나 |
|---|---|---|
| 아무것도 안 함(링커 애드혹) | cdhash H"…" | 아니 |
| 자체 서명 인증서 | identifier "com.example.spark" and certificate leaf = H"…" | 응, 같은 인증서로 서명하는 모든 Mac에서 |
| 애플이 발급한 인증서 | identifier "com.example.spark" and anchor apple generic and … certificate leaf[subject.OU] = EXAMPLE_TEAM | 응, 그 팀 안에서 |
식별자랑 인증서, 이 둘이 빌드가 바뀌어도 앱을 그 앱으로 만들어줘. 6트랙에서 이걸 끝까지 파고들 거야. 이 가족이 Mac에서 쓰는 자체 서명 신원과 TCC가 저장하는 세 가지 상태를 보고, 규칙이 안정적이어도 권한이 있다는 증거는 아닌 이유를 짚어. 가족이 일부러 안 가는 길(하드닝 런타임, 공증, 샌드박스)도 다뤄.