본문 바로가기
C.W.K.
Lesson 04 of 06 · published

macOS에게 앱은 곧 서명이야

~15 min · platform-map, code-signing, designated-requirement, tcc

Level 0번들 열어본 사람
0 XP0/84 lessons0/17 achievements
0/100 XP to next level100 XP to go0% complete
"어제 손쉬운 사용 권한 줬는데 오늘 또 물어봐. 고장 난 건 없어. 네가 다른 앱을 배포한 거야."

원하든 말든 바이너리는 전부 서명돼 있어

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가 저장하는 세 가지 상태를 보고, 규칙이 안정적이어도 권한이 있다는 증거는 아닌 이유를 짚어. 가족이 일부러 안 가는 길(하드닝 런타임, 공증, 샌드박스)도 다뤄.

Code

서명이랑 designated requirement 읽어보기·bash
swift build
BIN="$(swift build --show-bin-path)/Spark"
codesign -dv "$BIN" 2>&1 | grep -E 'Signature|flags'
# CodeDirectory v=20400 ... flags=0x2(adhoc) ...
# Signature=adhoc
codesign -dr - "$BIN"
# # designated => cdhash H"e3e8194102faaee2a3b1946f1cc4be7e26860679"

# Rebuild after any source change and run the last command again: the hash is different.
# Now read a signed app you trust:
codesign -dr - /Applications/SomeSignedApp.app

External links

Exercise

Spark 패키지에서 swift build 하고 codesign -dr -로 designated requirement를 적어둬. 소스에서 문자열 하나만 바꾸고 다시 빌드한 다음 또 적어. 그리고 개인정보 권한을 준 적 있는 /Applications 앱 두 개에도 같은 명령을 돌려봐. 각각 다시 빌드해도 규칙이 살아남을지, 규칙의 어느 부분 때문에 그런지 적어봐.
Hint
cdhash라는 단어가 있냐, certificate leaf라는 단어가 있냐를 봐. 해시로만 된 규칙은 바이트가 바뀌면 못 버텨. 식별자랑 인증서로 된 규칙은 버텨.

Progress

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

댓글 0

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

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