Digital Credentials: UI process independently verifies user activation
The privileged process trusted the sandbox to say the user tapped.
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxyInternals.h
Transient activation("user gesture")은 원래 WebContent process 내부에서 추적되고 검증됩니다. WebContent process는 WebKit의 multi-process architecture에서 sandbox 처리된, attacker가 도달 가능한 renderer에 해당합니다. 반면 UI process는 실제 input 이벤트를 소유하고 native chooser UI를 표시하는 privileged process입니다.
이번 commit은 Digital Credentials chooser를 표시하기 전에, WebContent process의 검증 결과를 그대로 신뢰하는 대신 UI process가 독자적으로 transient user activation을 검증하도록 변경합니다. 구체적으로는, 실제 input 이벤트로부터만 설정되는 자체 lastActivationTimestamp를 transient-activation window와 비교합니다. 또한 lastConsumedDigitalCredentialsActivationTimestamp를 기록해서, 하나의 gesture로 chooser가 두 번 이상 열리는 상황을 막습니다.
Before: After:
WebContent (untrusted) WebContent (untrusted)
checks activation, sends IPC sends IPC regardless
UIProcess UIProcess (trusted)
trusts IPC, shows chooser compares own lastActivationTimestamp
vs transient-activation window
AND vs lastConsumed...Timestamp
shows chooser only if fresh & unconsumed
Significance
Compromise된 renderer가 실제 user gesture 없이도 모바일 운전면허증이나 신분증 같은 native credential picker를 띄울 수 있었던 spoofing 경로를 차단하며, 하나의 gesture로 여러 chooser를 여는 재생 공격도 막습니다.
WebKit의 process-trust 모델 하에서는, WebContent 내부에서만 수행된 검증에 전적으로 의존하는 보안 판단은 그 자체로 의심 대상입니다. JS 엔진이나 DOM 버그를 통한 renderer compromise가 발생하면, 공격자가 IPC 인자를 위조해 해당 검증을 우회할 수 있기 때문입니다. attestation을 UI process 쪽으로 옮기는 방식은 permission dialog나 file picker에서도 동일하게 쓰이는 trust-boundary 패턴이며, 이를 통해 gesture 증명이 신뢰할 수 있는 근거로 자리잡게 됩니다.
Audit directions
이번 UI-process 측 게이팅은 이번 fix만 재점검할 대상이 아니라, 앞으로도 계속 패턴을 추적할 가치가 있습니다. 좁게 보면, 다른 digital-credentials 관련 또는 유사하게 게이팅된 IPC entry point들 — native UI에 도달하는 showXChooser나 permission-prompt handler 전반 — 이 여전히 WebContent 측 activation 검증에만 의존하고 UI-process 측 재검증이 없는지 확인할 필요가 있습니다. 좀 더 넓게 보면, renderer에서 계산되어 IPC로 전달된 보안 판단(activation, permission, origin)에 기반해 동작하는 모든 UI-process handler를 점검할 필요가 있습니다. 이 경우 패턴은 신뢰할 수 없는 process의 assertion에 의해 게이팅되는 privileged action입니다. 가장 넓게 보면, privileged 쪽이 sandbox 처리된 쪽에서 발급한 trust claim을 자체 state(input 이벤트, 실제 timestamp 등 privileged 쪽만 소유한 정보)로부터 다시 도출하지 않고 그대로 소비하는 two-process split 구조 전반이 대상이 됩니다. 리뷰에서 눈여겨봐야 할 신호는, internals().lastActivationTimestamp나 이에 준하는 locally-owned source를 참조하는 대신 activation/permission flag를 메시지 payload에서 곧바로 읽어오는 UI-process IPC handler입니다. 그리고 consumed-timestamp 방식에 한해서는, 비교는 되지만 갱신되지 않는(혹은 경합하는 요청들 사이에서 non-atomic하게 갱신되는) "last consumed" 필드가 TOCTOU의 신호이며, 다른 single-use-gesture 게이트를 점검할 때도 함께 확인해야 할 대상입니다.