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

무서운 권한 둘

~11 min · permissions, tcc, accessibility, microphone

Level 0불씨 없음
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"내가 말하는 전부를 듣고, 내가 쓰는 모든 앱에 타이핑할 수 있어라." 다시 읽어봐. 그게 받아쓰기 도구의 스펙이면서 동시에 키로거의 스펙이라는 것도 보일 거야.

요청

Firekeeper 는 사려 깊은 유저라면 멈칫할 만한 권한 둘이 필요해. 마이크. 널 들을 수 있어. 접근성. 다른 앱의 포커스된 요소를 읽고 거기에 손댈 수 있어. 받아쓰기 앱이라면 전부 정확히 이게 필요하고, 멀웨어도 똑같이 이게 필요해. 이걸 안 받고 넘어가는 똑똑한 구조 같은 건 없어. 말하는 걸 타이핑해 주는 도구라면 듣고 타이핑할 수 있어야 하니까. 손댈 수 있는 건 그 너머로는 아무것도 요청하지 않는 것, 그리고 받은 허가를 정당화하는 방식으로 행동하는 거야.

권한 표면은 설계에서 나오는 결과야

마이크             필수        -- 널 들어야 함
접근성             필수        -- 선택, 타깃 맥락, 튼튼한 삽입
Input Monitoring   회피됨      -- 설계가 이벤트 탭 대신 flagsChanged 고름
Automation         회피됨      -- 한 앱 프로파일이 요구 안 하면 AppleScript 없음
네트워크           스코프됨    -- 네가 고른 프로바이더만, 기본값으론 절대 아님

Input Monitoring 이 사업 비용으로 받아들여진 게 아니라 피해졌다 는 걸 봐. 앞에서 내린 단축키 결정이 여기서 배당금을 내는 거야. 활성화가 이벤트 스트림을 가로채는 대신 모디파이어 플래그를 관찰하니까, 앱이 '모든 키 입력을 봐도 될까요' 를 물을 일이 아예 없어. 권한 표면은 기능 목록이 정해 주는 고정값이 아니라 아키텍처가 만들어 내는 결과고, 줄어들도록 설계할 수 있어.

솔직하게, 그리고 일찍 물어

습관 둘이 이 요청을 공정하게 만들어. 첫째, 받아쓰기 중간에 유저를 기습하지 말고 실행할 때 마이크를 요청해. 말을 시작하는 순간 뜨는 권한 프롬프트는 놀라운 데다 첫 단어를 놓치게 만드니까. 둘째, 상태를 읽을 수 있게 만들어. 지금 권한이 어떤지 실시간으로 보여 주고, 해당 시스템 설정 창을 바로 여는 버튼까지 붙은 Setup 패널을 둬. 그래야 유저가 자기가 뭘 줬는지 늘 알고 언제든 거둬들일 수 있어. 뭘 쥐고 있는지 애매하게 구는 앱은 그걸 쥘 자격이 있다는 논쟁을 이미 진 거야.

권한 표면은 발견하는 게 아니라 설계하는 거야. 이 기능엔 이 무서운 허가가 필요하다고 받아들이기 전에, 다른 구현으로 같은 인터랙션을 더 작은 요청으로 줄 수 있는지 물어봐. Firekeeper 의 맨 Fn 홀드든 키로거급 이벤트 탭이든 유저가 느끼는 UX 는 똑같아. 그런데 '모든 키 입력을 보게 해 달라' 고 요구하는 건 둘 중 하나뿐이지. 지킬 값어치가 있던 건 인터랙션이었지 권한이 아니었어.
넌 멀웨어랑 똑같은 능력을 요청하고 있어. 과장이 아니고, 방어적으로 굴 이유도 아니야. 엄격해질 이유지. 이 트랙에 있는 전부가, 그러니까 안 쌓아 두기, 보안 필드 거부, 보이는 녹음, 보내기 전 프로바이더 선택이 존재하는 이유가 이거야. '이 앱이 스파이웨어일 수도 있잖아' 에 대한 솔직한 답은 안심시키는 말이 아니라 유저가 직접 확인할 수 있는 아키텍처거든.

Code

일찍 묻고, 상태를 드러내고, 여분을 안 쥠·swift
func applicationDidFinishLaunching() {
    // 실행 때 물음 — 발화 중간에 유저를 절대 기습 안 하고,
    // 권한 시트에 첫 단어를 절대 안 잃음.
    AVCaptureDevice.requestAccess(for: .audio) { _ in }

    // 접근성은 선택 + 튼튼한 삽입에 필수.
    // 드러내; 조용히 격하돼 유저가 추측하게 두지 마.
    let trusted = AXIsProcessTrustedWithOptions(
        [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary)
    setupPanel.accessibilityGranted = trusted
}

// 없는 걸 봐: Input Monitoring 요청 없음, Automation 없음.
// 단축키 설계가 그것들을 불필요하게 만들었어.

External links

Exercise

네가 쓰는 앱이 요청하는 권한을 나열해봐. 각각에 대해 물어. 이건 기능 자체가 요구하는 거야, 아니면 개발자가 고른 특정 구현이 요구하는 거야? 다른 설계였으면 요청이 줄었을 자리 하나, 그리고 아무리 해도 못 줄이는 자리 하나를 찾아봐.
Hint
못 줄이는 요청은 제품의 본질에서 곧장 나와. 받아쓰기 도구는 널 들어야 하니까. 줄일 수 있는 요청은 구현 편의에서 나오고. 좁은 API 보다 넓은 게 쓰기 쉬웠던 거지. 이 구별이 중요한 건 유저가 밖에서 둘을 구별할 수 없기 때문이야. 안 줄인 권한 하나하나가, 안 써도 됐을 신뢰를 쓰는 거거든.

Progress

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

댓글 0

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

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