Safari MCP를 기반으로 구축된 자동화 도구가 개발자의 대시보드 탭을 읽는 도중에 닫아버리는 사고가 발생했습니다. 이 사건은 AI 기반 에이전트가 자신에게 속하지 않은 탭을 건드리지 못하도록 막아야 하는 가드(guard)의 숨겨진 결함을 드러냈으며, "기본 안전(safe-by-default)" 카테고리가 왜 위험 요소가 될 수 있는지를 보여줍니다.

작동하던 가드—문제가 생기기 전까지는

이 도구는 자신이 생성하는 모든 탭에 내부 식별자를 태그로 붙입니다. 에이전트가 명령을 내리기 전에 가드는 해당 마커를 확인하며, 마커가 없으면 가드는 동작을 거부합니다. 실제로 이 가드는 에이전트가 열지 않은 페이지를 읽지 못하도록 차단했는데, 이는 정확히 설계된 대로의 동작이었습니다.

양식을 작성하던 중 페이지가 다른 도메인으로 리다이렉트되었습니다. 이 리다이렉트 과정에서 마커가 제거되어 탭에 라벨이 붙지 않은 상태가 되었습니다. 가드는 마커가 누락된 것을 확인하고 "소유권을 확인할 수 없으므로 이 탭을 읽지 않겠습니다"라고 보고했습니다. 이 시점까지 안전 점검은 의도한 대로 작동했습니다.

선을 넘은 정리 코드

다음으로 마커가 없는 '고아 탭(orphaned tabs)'을 닫기 위한 수동 정리 루틴이 실행되었습니다. 이 루틴은 소유권을 먼저 확인하지 않고 도구에 "탭 닫기"를 요청했습니다. 가드가 해당 탭이 자신의 것임을 증명할 수 없었기 때문에, 도구는 기본 동작인 "현재 탭 닫기"로 폴백(fallback)했습니다. 이때 현재 탭은 개발자가 읽고 있던 대시보드였으며, 고아 탭이 아니었습니다.

그 결과, 막다른 길이어야 했던 안전 경로가 오히려 파괴적인 동작을 트리거하는 결과를 초래했습니다.

"소유권 없음"을 "권한 있음"으로 처리한 세 가지 계층

  1. 명령 분류 – 명령을 그룹화한 목록에서 close_tab을 광범위한 "탭 관리(tab management)" 범주에 포함시켰습니다. 개발자는 "탭 목록 보기(list tabs)"와 같은 다른 명령들이 정보를 읽기만 하기 때문에 해당 범주의 모든 명령이 무해할 것이라고 가정했습니다. close_tab이 파괴적이라는 명시적인 표시가 없었기 때문에, 이 명령은 주변 명령들이 가진 인지된 안전성을 그대로 물려받았습니다.
  2. 확장 프로그램 수준의 정책 – 모든 브라우저 동작을 중개하는 Safari 확장 프로그램은 세션이 아무것도 소유하지 않았을 때 모든 동작을 허용했습니다. 이 규칙은 읽기 전용 동작에는 유효하지만, close_tab이 출처 확인 없이 실행될 수 있는 문을 열어주었습니다.
  3. 로직 불일치 – 정리 루틴은 한 탭의 소유권 플래그를 확인한 다음, 브라우저가 "현재(current)" 탭으로 보고한 탭에 대해 닫기 함수를 호출했습니다. 이러한 불일치로 인해 가드가 마커를 찾지 못한 실패 상황이 닫기 명령을 우회하여 잘못된 대상으로 전달되었습니다.

각 계층은 "기록된 소유권 없음"을 "작동해도 안전함"으로 간주했고, 이들이 결합되어 정당성 증명 없이 실행되는 탭 닫기 명령을 만들어냈습니다.

해결책: 파괴적인 동작에는 소유권 증명이 필수

수정된 로직은 읽기 전용 경로와 파괴적인 경로를 분리합니다. 이제 close_tab 명령이 실행되기 전에 도구는 대상 탭에 대한 유효한 마커를 제시해야 합니다. 마커가 누락되면 명령은 현재 탭을 기본값으로 사용하는 대신 에러를 발생시킵니다. 가드는 더 이상 일반적인 "무언가 수행" 분기로 폴백하지 않습니다.

이 변경 사항은 마커 누락을 "할 일이 없음" 또는 "진행해도 좋음" 중 하나로 해석할 수 있었던 모호한 상태를 제거합니다. 명시적인 실패를 강제함으로써, 도구는 사용자의 작업이 실수로 손실되는 것을 방지합니다.

개발자가 주의해야 할 사항

  • 카테고리 이름이 안전성을 결정하게 두지 마십시오 – "탭 관리"와 같은 라벨은 그 안의 각 명령이 미치는 영향을 알려주지 않습니다. 명령 자체 옆에 각 작업의 비용(읽기 vs 파괴)을 기록하십시오.
  • 가드 조건은 동작의 심각도와 일치해야 합니다 – 읽기 요청에 충분한 확인 절차가 데이터를 삭제할 수 있는 명령에는 충분하지 않습니다. 영향력의 클래스별로 별도의 검증 파이프라인을 구축하십시오.
  • 암시적인 폴백(fallback)을 피하십시오 – 가드가 소유권을 확인할 수 없을 때 가장 안전한 대응은 중단하는 것이지, 기본 대상을 선택하는 것이 아닙니다. 기본 동작은 권한 상승 버그의 흔한 원인입니다.
  • 인접성 가정을 감사하십시오 – 명령들이 나란히 배치된 모든 목록이나 메뉴를 검토하십시오. 코드가 안전성을 명시적으로 재평가하지 않으면, 무해한 명령이 주변 명령에 부여된 신뢰를 그대로 물려받을 수 있습니다.

요점

소유권 가드가 누락된 것은 버그가 아니라 설계상의 공백입니다. 모든 파괴적인 명령을 명시적인 권한 증명을 요구하는 별도의 보안 도메인으로 취급하고, "마커 없음"이 "진행해도 좋음"으로 해석되지 않도록 하십시오. 그래야만 자동화 도구가 관리하고자 하는 바로 그 탭들을 보호할 수 있습니다.