My safety guard protected 2 tools and trusted the other 20

개요

MCP 서버의 코딩 에이전트가 사용자의 실제 Safari 브라우저를 제어할 때, 에이전트가 자신의 탭만 조작하도록 하는 안전 장치(guard)가 세 번의 감사 끝에 대부분의 상황에서 제대로 작동하지 않았음이 발견되었고, 그 개선 과정과 교훈을 다룹니다.

주요 내용

* 1차 감사: 안전 장치 적용 범위 제한
* 초기에 구현된 탭 소유권 확인 기능은 safari_clicksafari_fill과 같은 일부 기능만 통과하는 래퍼(wrapper)에만 적용되었습니다.
* safari_set_cookie, safari_delete_cookies, localStorage/sessionStorage 조작 등 페이지를 변경하는 다른 20여 개의 도구는 직접 엔진을 호출하여 안전 장치의 보호를 받지 못했습니다.
* 이로 인해 에이전트가 혼동하여 사용자의 탭에 쿠키를 쓰거나 localStorage를 조작해도 이를 막을 수 없었습니다.
* 해결책으로, 탭 소유권 확인 로직을 추출하여 페이지를 변경하는 모든 함수 시작 시 호출하도록 변경했습니다.
* 2차 감사: 일괄 처리 도구의 허점
* safari_run_script와 같이 여러 단계를 일괄로 실행하는 도구는 일괄 처리 시작 전에 한 번만 소유권을 확인했습니다.
* 배치 내에서 evaluate 기능이나 switchTab, navigate 단계가 실행될 경우, 이후 단계에서 사용자의 탭에 접근해도 제재할 방법이 없었습니다.
* 개선 후에는 배치 실행 중 각 단계마다 소유권을 확인하고, 오류 발생 시 전체 배치가 중단되도록 변경되었습니다.
* 3차 감사: 부정확한 소유권 정보
* URL 경로 접두사(path-prefix)만으로 소유권을 판단하여 /org/org-evil을 동일하게 취급하는 문제가 있었습니다.
* TTL(Time To Live) 메커니즘이 세션이 길어질수록 만료되지 않고 소유권 정보가 누적되어 오래된 소유권 주장이 유효한 것처럼 작동했습니다.
* TTL 관련 로직을 순수 소유권 매칭 모듈로 분리하고, 만료 시간을 실제 사용 시점에 적용하도록 수정했습니다.
* 브라우저 확장 프로그램의 서비스 워커 재시작 시 메모리상의 소유 탭 정보가 초기화되면서 안전 장치가 비활성화되는 문제가 발생했습니다.
* 이 문제를 해결하기 위해 소유 탭 상태가 재시작 후에도 저장되도록 변경하여, 상태 초기화 시에는 안전 장치가 비활성화되지 않고(fail closed) 안전하게 작동하도록 했습니다.

시사점

코드 수정 이상의 교훈은 안전 장치를 모든 호출 지점(call site)에 적용하거나, 모든 경로가 반드시 거치는 핵심 지점을 지켜야 하며, 문자열 비교를 보안 경계로 사용할 때 신중해야 하고, 상태 재설정 시 안전 장치가 열리지 않도록(fail closed) 설계해야 한다는 것입니다. 또한, 보안 관련 로직을 테스트 가능한 순수 모듈로 분리하는 것이 중요합니다.

원문 읽기 →
원문을 불러오는 중...

댓글

GitHub Discussions