보안 연구팀 파라미터(Parameter)가 2026년 10월 공개한 ChatGPT 취약점은 공격 방식 자체보다 그것이 만들어진 과정이 더 많은 것을 시사한다. 공격자는 악의적인 대화를 하나 만들고, ChatGPT의 평범한 공유 기능으로 이를 게시한 뒤, 그 링크를 피해자에게 보내기만 하면 됐다. 피해자가 링크를 열면 공격자가 작성한 자바스크립트가 chatgpt.com 출처(origin)로, 즉 피해자가 로그인한 세션 안에서 그대로 실행됐다. 앱을 설치하거나 프로젝트에 참여하거나 코드를 붙여넣거나 개발자 도구를 열 필요도 없었다. 공유 링크를 여는 것, 그 한 번의 행동이 전부였다.
연구팀은 이 코드가 피해자의 대화 목록을 열거하고, 공격자와 한 번도 공유된 적 없는 비공개 대화의 내용을 가져오며, 그 데이터를 외부 서버로 전송할 수 있음을 확인했다. 핵심은 화면에 보이는 텍스트를 긁어오는 수준이 아니라, 피해자의 인증 세션 그대로 ChatGPT의 API를 직접 호출했다는 점이다. 이는 사실상 계정 장악에 해당한다. OpenAI는 10월 26일 이 문제를 수정했고 보고를 P1(최고 등급군)으로 분류했다.
샌드박스는 작동했지만, 옆에 있던 부품이 문을 열어줬다
공격 사슬의 출발점은 각각 따로 보면 전혀 위험하지 않은 두 기능, 그래프와 HTML 미리보기(Preview)였다. ChatGPT는 HTML 코드 블록을 작은 웹페이지로 렌더링하는데, 그 안에 임의 코드가 있을 수 있으므로 별도의 웹 주소를 가진 제한된 프레임에서 실행한다. 실제로 미리보기 안의 코드는 부모인 ChatGPT 본문 페이지(parent.document)를 직접 읽지 못했다. 격리는 의도대로 동작한 것이다.
문제는 같은 대화에 들어 있던 인터랙티브 그래프였다. 이 그래프는 내부적으로 MathLive라는 수식 라이브러리를 불러왔고, 그 과정에서 본문 페이지에 가상 키보드 관련 메시지를 받는 리스너를 설치했다. 서로 다른 프레임이 페이지를 직접 들여다볼 수 없을 때도 메시지를 주고받게 해주는 표준 수단이 postMessage다. 격리된 미리보기는 본문 안으로 걸어 들어갈 수 없지만, 우편물 투입구처럼 메시지를 밀어넣을 수는 있었다. 이것이 안전하려면 받는 쪽이 발신자를 검증해야 하는데, MathLive의 최상위 컨트롤러는 출처 정책이 'none'이어서 어떤 주소에서 온 메시지든 받아들였다. 승인된 창에서 왔는지도 확인하지 않았다.
미리보기는 두 개의 메시지를 보냈다. 하나는 가상 키보드 레이아웃을 공격자의 HTML로 교체했고, 다른 하나는 키보드를 띄우라는 지시였다. 키보드가 열리자 MathLive는 전달받은 레이아웃을 HTML 문자열로 합쳐 실제 ChatGPT 페이지의 innerHTML에 할당했다. 중간에 있던 createHTML이라는 함수는 값을 정화하는 것처럼 보였지만, 실제로는 문자열을 '신뢰됨'으로 표시만 하는 Trusted Types 항등 래퍼였을 뿐 아무것도 걸러내지 않았다.
CSP까지 넘어서 chatgpt.com으로 실행되기까지
innerHTML로 삽입된 스크립트 태그는 자동으로 실행되지 않는다. 다음 단계는 srcdoc 속성을 쓴 iframe이었다. MathLive는 이 프레임을 sandbox 속성 없이 본문에 삽입했고, 그 결과 새 문서가 생성되면서 외부 스크립트가 실행될 수 있었으며, 본문 페이지가 만든 프레임이라 브라우저는 여기에 chatgpt.com 신원을 부여했다. 마지막 장벽은 인라인 자바스크립트를 차단하는 콘텐츠 보안 정책(CSP)이었는데, 이 정책은 유튜브 스크립트는 허용하고 있었다. 연구팀은 유튜브 oEmbed 엔드포인트의 JSONP 콜백을 이용했다. 이 엔드포인트는 잘못된 콜백을 거부하려 했지만 검증 오류를 돌려주기 전에 콜백을 응답 본문에 먼저 넣었고, 콜백 끝에 ;void를 붙여 뒤에 딸려오는 오류 객체를 무해하게 만들자 브라우저는 이를 스크립트로 로드해 실행했다.
초기 버전은 연구팀이 제어하는 ChatGPT 앱을 발신자로 썼기 때문에 피해자가 먼저 그 앱을 연결하거나 호출해야 해서 현실적인 공격이 아니었다. 돌파구는 그래프와 미리보기를 하나의 평범한 공개 대화에 함께 넣는 것이었다. 공유로 내보낼 때 첨부만 남고 그래프가 사라지면 리스너를 설치하는 핵심 부품이 빠져 메시지를 받을 대상이 없어졌기 때문이다. 둘을 모두 담자 /share/ 링크가 전체 사슬을 다른 사용자에게 그대로 전달했다. 가장 안정적인 재현에서는 피해자가 미리보기 버튼을 한 번 눌렀고, HTML 블록을 자동 미리보기로 표시한 버전에서는 링크를 여는 것만으로 약 4.5초 뒤 chatgpt.com 출처의 경고창이 떴다. 그래프와 미리보기가 독립적으로 로드되는 탓에 메시지가 MathLive 준비 전에 도착할 수 있어, 최종 운반체는 일정 시간 동안 두 메시지를 재시도하고 증명·외부전송은 한 번만 실행되도록 가드를 뒀다.
실무자가 가져갈 교훈
경고창은 코드가 어디서 실행되는지를 보여줬을 뿐, 왜 위험한지는 그다음에 드러났다. chatgpt.com으로 실행되는 자바스크립트는 정규 웹 앱과 동일한 인증 맥락에서 API를 호출할 수 있었다. 페이로드는 세션 정보를 읽어 최근 대화를 열거하고 특정 비공개 대화의 메시지 데이터를 요청한 뒤, 평범한 이미지 요청으로 그 일부를 공격자 서버로 보냈다. 연구팀이 증명에 쓴 대화 엔드포인트에 국한되는 한계도 없었다. 인증된 프런트엔드 API로 노출된 다른 데이터와 동작이 같은 보안 경계 안에 있었기 때문에, 사실상 피해자 계정으로 가능한 거의 모든 행동을 수행할 수 있었다는 의미다.
이 사례가 특히 곱씹을 만한 이유는 개별 기능이 모두 합리적으로 설계됐다는 데 있다. 미리보기는 격리됐고, 그래프는 수식 키보드를 불러올 수 있었으며, 프레임은 메시지를 교환할 수 있었고, CSP는 대형 미디어 제공자를 허용했으며, 공개 대화는 공유될 수 있었다. 취약점은 이 모든 것이 한 페이지에 모였을 때 비로소 나타났다. 유튜브 응답을 막는 것은 자바스크립트로 가는 한 경로만 제거할 뿐 근본 원인은 아니었다. 핵심 결함은 postMessage 수신 측이 발신자 출처를 검증하지 않았다는 것, Trusted Types 래퍼를 정화 장치로 착각했다는 것, 그리고 sandbox 없는 iframe이 본문 출처를 상속했다는 것이다. 복합 기능을 운영하는 팀이라면 자신이 통제하지 못하는 서드파티 라이브러리가 본문 페이지에 심어두는 리스너와 메시지 신뢰 가정을, 기능이 결합되는 지점에서 함께 점검해야 한다는 교훈이 남는다. OpenAI는 두 버전의 웹 프런트엔드에 걸쳐 수정을 배포했고, 롤아웃 중 CSP 변경이 사슬의 일부를 먼저 차단한 뒤 10월 26일 양쪽 모두 해당 메시지를 거부하도록 조치하며 문제를 종결했다.