본문 바로가기
C.W.K.
Stream
Lesson 02 of 05 · published

왜 macOS 가 자꾸 널 잊는가

~12 min · codesign, tcc, gotcha, designated-requirement

Level 0불씨 없음
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"리빌드할 때마다 macOS 는 앱을 보고 낯선 사람을 봤어. 기술적으론 맞는 얘기였으니까."

증상

접근성을 허가해. 마이크를 허가해. 오타 하나 고치려고 앱을 리빌드해. 그런데 macOS 가 둘 다 다시 물어. 또, 빌드할 때마다. 망가진 OS 처럼 느껴지고 망가진 앱처럼도 느껴져. 둘 다 아니야. macOS 가 두 바이너리를 '같은 앱' 으로 볼지 정하는 방식에서 완벽하게 논리적으로 나오는 결과야.

원인은 지정 요구사항(Designated Requirement)

macOS 는 TCC 허가를, 그러니까 접근성이나 마이크 같은 걸 앱의 지정 요구사항, 줄여서 DR 에 묶어. '이게 그 앱이다' 라고 말해 주는 코드 서명 규칙이지. 평범한 swift build애드혹 서명을 만드는데, 그 지정 요구사항은 바이너리의 cdhash 에 못 박혀 있어. 소스를 한 글자만 바꿔도 cdhash 가 바뀌고, 그러면 DR 이 바뀌고, 방금 빌드한 앱이 유저가 허가해 준 앱이랑 안 맞게 돼. macOS 가 잊는 게 아니야. 이건 이름만 같은 다른 앱이라고 정확하게 결론 내리는 거야.

swift build -> 애드혹 서명 -> DR 이 cdhash 에 고정
     |
     v  한 줄 편집, 리빌드
cdhash 바뀜 -> DR 바뀜 -> TCC: "넌 누구야?" -> 영원히 재프롬프트

해법: 안정된 아이덴티티 하나로 서명 -> DR 안정 -> 허가 지속

해법, 그리고 해법 안의 함정

해법은 모든 앱이 같이 쓰는 안정된 자체 서명 아이덴티티 하나야. 앱마다 인증서를 따로 만드는 게 아니고. 그건 문제를 곱하기만 해. 기계마다 한 번 임포트해 두고 빌드 스크립트가 그걸로 서명하게 하면 DR 이 더는 안 바뀌어. 그리고 그 해법 안에 알아 둘 함정이 하나 더 있어. security find-identity -v 는 Gatekeeper 기준으로 유효한 아이덴티티만 걸러 내는데, 자체 서명 인증서는 유효로 안 쳐. 신뢰 안 됨으로 보고되거든. 그래서 네 아이덴티티가 아예 없는 것처럼 보여. -v 를 빼고 security find-identity -p codesigning 으로 확인해. 플래그 하나가 '설계대로 잘 돌아감' 과 한 시간짜리 혼란 사이에 서 있는 거야. 마지막으로, 제대로 서명한 첫 빌드가 나오면 예전 애드혹에 묶여 있던 허가를 tccutil reset 으로 한 번 지우고 다시 허가해 줘. 그때부터는 계속 유지돼.

OS 가 자꾸 '잊으면', 실제로 뭘 기준으로 삼는지 물어. 권한 시스템은 앱을 이름이나 경로로 추적하지 않아. 암호학적 아이덴티티로 추적하지. 허가가 증발했다면 버그를 만난 게 아니야. 네가 모르는 사이에 아이덴티티를 바꾼 거지. OS 가 뭘 네 아이덴티티로 보는지 찾아내서 고정하면, 증상을 우회하는 게 아니라 뿌리에서 사라져.
앱마다 인증서를 만들지 마. 이걸 알고 나면 본능적으로 '그럼 앱마다 자기 인증서로 서명하지' 가 떠올라. 그건 인증서가 더 늘고, 기계마다 임포트할 게 더 늘고, 만료될 것도 더 느는 거야. 공유 문제를 N 번 푸는 셈이지. 앱 가족 전체가 아이덴티티 하나를 같이 쓰는 쪽이 일도 확실히 적고 실패할 구멍도 확실히 적어.

Code

감지 함정, -v 가 네 자체 서명 아이덴티티를 숨겨·bash
# 틀림 — Gatekeeper-유효 아이덴티티로 필터링.
# 자체 서명 인증서는 CSSMERR_TP_NOT_TRUSTED 로 보고돼 숨겨져서,
# 이건 아무것도 안 찍고 넌 아이덴티티가 설치 안 됐다고 결론 내려.
security find-identity -v

# 맞음 — 유효성 필터 없이 코드서명 아이덴티티 나열.
security find-identity -p codesigning

# 공유 아이덴티티 하나로 서명 (빌드 스크립트가 자동으로 함):
codesign --force --sign "cwk-codesign" --options runtime dist/YourApp.app

# 첫 제대로-서명된 빌드 후, 낡은 애드혹 허가를 한 번 지움:
tccutil reset Accessibility com.example.yourapp
tccutil reset Microphone    com.example.yourapp
# 한 번 다시 허가. 여기서부터 리빌드를 넘어 지속됨.

External links

Exercise

OS 나 서비스가 이미 설정해 둔 걸 자꾸 '잊었던' 때를 떠올려봐. 권한이든 신뢰된 기기든 저장해 둔 자격증명이든. 그게 뭘 기준으로 삼고 있었고, 시도할 때마다 네가 모르고 뭘 바꾸고 있었어? 그 아이덴티티를 어떻게 고정할래?
Hint
이 패턴은 코드 서명을 훨씬 넘어서 통해. 상태가 자꾸 증발한다면, 네가 상수라고 믿고 있는 뭔가가 사실은 변하고 있는 거야. 해시든, 기기 지문이든, 설치 id 든, 컨테이너 id 든. 해법은 '더 조심해서 다시 허가하기' 가 아니야. 계속 바뀌는 그 기준값을 찾아내서 못 박는 거지. 아이덴티티가 흔들리는 문제를 증상만 고치면 반드시 돌아와.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.