롤업 브리지는 왜 “증명 통과”만으로 충분하지 않을까: Taiko 사고로 보는 메시지 출처 검증
L1 브리지가 어떤 withdrawal proof를 검증했다고 해서, 그 메시지가 정말 L2에서 발생했다고 믿어도 될까?
롤업 브리지는 왜 “증명 통과”만으로 충분하지 않을까: Taiko 사고로 보는 메시지 출처 검증
- 카테고리: blockchain_dev
- 예상 읽기 시간: 10분
- 오늘의 질문: L1 브리지가 어떤 withdrawal proof를 검증했다고 해서, 그 메시지가 정말 L2에서 발생했다고 믿어도 될까?
- 핵심 출처:
- The Block - Ethereum Layer 2 Taiko halts block production following exploit - 2026-06-22, 확인일 2026-07-12
- taikoxyz/taiko-mono PR #21820 - port hack recovery hooks to v3 - 병합 2026-06-23, 확인일 2026-07-12
- taikoxyz/taiko-mono PR #21847 - disable forced inclusion submission and add hack recovery deploy script - 병합 2026-06-25, 확인일 2026-07-12
- taikoxyz/taiko-mono PR #21833 - Proposal0017 L1 recovery bundle - 2026-06-23, 확인일 2026-07-12
- Taiko Labs - What is SGX, how it works, what are its pros and cons - 2023-09-07, 확인일 2026-07-12
1. 왜 지금 봐야 하나
2026년 6월 22일, Taiko는 체인 상태 검증 메커니즘이 compromise되었다고 확인했고, Taiko 위에 배포된 모든 브리지의 보안 가정을 더 이상 신뢰할 수 없으니 사용자가 자금을 회수하라고 안내했다. The Block 보도는 Taiko의 X 공지를 인용해, forged message proof가 L1에서 받아들여졌고 source chain에 대응되는 legitimate event 없이 bridge와 token vault에서 fraudulent withdrawal이 발생했다고 정리했다. 추정 손실은 당시 약 170만 달러로 보도되었다.
이 글에서 중요한 것은 가격이나 토큰 반응이 아니다. 개발자가 봐야 할 질문은 이것이다.
브리지의 검증 함수가 “이 proof는 통과한다”고 말할 때, 그것은 “이 메시지가 올바른 출처에서 실제로 발생했다”는 뜻과 같은가?
Taiko의 공개 GitHub PR들은 사고 이후 어떤 상태를 무효화하고 어떤 경로를 잠갔는지 꽤 구체적으로 보여준다. PR #21820은 SignalService의 signal/checkpoint에 version을 두어 이전 state transition이 bridge withdrawal이나 L2 state verification에 재사용되지 못하게 하고, 이미 prove되었지만 실행되지 않은 retriable message를 DONE 처리하며, Inbox state를 공격 이전 지점으로 되돌리는 one-time initializer를 추가했다. PR #21847은 recovery bundle 배포 스크립트와 함께 forced inclusion submission을 비활성화했다. PR #21833은 Proposal0017 L1 recovery bundle이 SignalService, Bridge, Inbox를 업그레이드하고 stale verifier trust를 제거한다고 설명한다.
즉 사고의 학습 포인트는 “Taiko가 해킹됐다”가 아니라, rollup bridge의 신뢰 경계는 proof verifier 한 줄이 아니라 message origin, signal namespace, retry status, verifier registry, forced inclusion queue까지 이어진 상태 머신이라는 점이다.
2. 핵심 개념
롤업 브리지는 보통 아래 흐름으로 동작한다.
L2에서 MessageSent 이벤트 또는 signal 생성
→ L2 state root / checkpoint / proof 생성
→ L1 verifier가 proof 또는 signal inclusion을 검증
→ L1 bridge가 message status를 갱신
→ 사용자가 process/retry로 자산 수령
여기서 “proof가 검증됐다”는 말은 여러 층으로 나뉜다.
- 형식 검증: proof 데이터 구조가 맞는가?
- 암호 검증: verifier가 기대한 signature, zk proof, SGX attestation, merkle proof가 맞는가?
- 출처 검증: 그 proof가 참조하는 message가 실제 source chain에서 발생했는가?
- 상태 버전 검증: 지금도 유효한 checkpoint/signal namespace에 속하는가?
- 재실행 검증: 이미 prove된 RETRIABLE message가 사고 후에도 다시 실행될 수 있는가?
- 운영 권한 검증: 어떤 verifier, pauser, Security Council, DAO proposal이 이 상태를 바꿀 수 있는가?
Taiko 사고에서 publicly checked source들이 공통으로 가리키는 지점은 3번과 4번이다. The Block은 Taiko의 update를 인용해 “forged message proofs were accepted on L1 without a legitimate event on the source chain”이라고 썼고, PR #21820은 이전 state transition이 withdrawal 또는 L2 state verification에 쓰이지 못하도록 SignalService checkpoint versioning을 추가했다. 이것은 단순한 access control patch가 아니라 과거에 통과한 증거와 현재 신뢰할 수 있는 출처 상태를 분리하는 작업이다.
SGX도 여기서 중요한 배경이다. Taiko Labs의 2023년 글은 multi-prover 접근을 설명하면서 SGX proof가 rollup proof diversity의 한 축이 될 수 있지만, Intel/TEE와 key custody라는 별도 신뢰 가정을 가진다고 설명한다. 즉 SGX는 “zk proof보다 쉽고 빠른 대체재”가 아니라, verifier registry와 key provenance가 함께 관리되어야 하는 proof source다.
3. 최신 이슈와 연결
사고 직후 나온 PR들을 보면 복구가 단순히 “버그 함수 수정”으로 끝나지 않았다는 점이 보인다.
- SignalService versioning: PR #21820은 signal/checkpoint에 version을 둔다. 목적은 이전 state transition이 더 이상 bridge withdrawal이나 L1의 L2 state verification에 사용되지 못하게 하는 것이다. 사고 후에는 “옛날 proof도 한때 유효했다”는 사실이 오히려 위험하다.
- Bridge.init3(bytes32[]): 이미 prove되었지만 실행되지 않은 retriable message를 무효화한다. PR 설명은 이를
DONE으로 표시해 proofless retry와 fresh message 재처리를 막는다고 쓴다. - Inbox.init2(...): chain core state를 공격 이전 지점으로 복구한다. 브리지 사고는 vault 잔액만 맞추면 끝나는 것이 아니라, rollup inbox의 진행 상태도 함께 정리해야 한다.
- forced inclusion 비활성화: PR #21847은 forced inclusion submission을 비활성화했다. 사고 대응 중 processing이 막혔는데 submission만 계속 받으면 사용자가 fee를 내고 queue에 갇힐 수 있기 때문이다.
- stale verifier trust 제거: PR #21833은 기존 RISC0, SP1, SGX attester의 stale verifier trust를 제거하는 recovery bundle을 언급한다. 브리지 검증에서 “어떤 verifier를 믿는가”는 코드 주소와 governance 상태를 포함한다.
이 흐름은 개발자에게 매우 현실적인 교훈을 준다. rollup bridge는 암호학만으로 안전해지지 않는다. 암호 검증은 필요조건이지만, 사고가 나면 아래 질문이 바로 운영 문제가 된다.
- 이미 prove된 메시지는 어떻게 무효화할 것인가?
- 어떤 checkpoint namespace를 폐기할 것인가?
- 사용자가 forced inclusion이나 retry path에 새로 자금을 넣지 못하게 할 것인가?
- verifier set을 교체할 때 기존 state와 어떻게 분리할 것인가?
- “브리지를 열었다”는 말이 어떤 quota, pauser, verifier, message status 조건을 의미하는가?
4. 개발자 관점 해석
첫째, proof validity와 origin validity를 같은 것으로 취급하면 안 된다. proof verifier가 통과했다는 것은 “이 verifier의 규칙으로는 맞다”는 뜻이다. 하지만 bridge가 실제로 필요한 명제는 더 강하다. “source chain에서 이 message가 발생했고, 그 source state가 현재도 신뢰 가능한 namespace에 있으며, 이 message는 아직 실행되지 않았다”까지 포함해야 한다.
둘째, message status는 보안 상태다. 많은 브리지 코드에서 NEW, RETRIABLE, DONE, FAILED 같은 상태는 UX 상태처럼 보인다. 하지만 사고 상황에서는 이미 prove된 RETRIABLE message가 가장 위험한 잔여물일 수 있다. PR #21820이 malicious retriable message를 DONE으로 marking한 이유가 여기에 있다. “삭제”보다 “재처리 불가능한 최종 상태”가 더 안전한 경우가 있다.
셋째, versioning은 긴급 복구용 회로 차단기다. SignalService의 checkpoint versioning은 storage migration 없이 이전 checkpoint를 한꺼번에 격리하는 방식이다. 평소에는 과해 보이지만, proof acceptance layer가 오염되었을 때는 “이전 namespace 전체를 더 이상 신뢰하지 않는다”고 표현할 방법이 필요하다.
넷째, forced inclusion은 검열 저항 장치이면서 사고 중에는 queue risk가 된다. 정상 상태에서는 forced inclusion이 proposer censorship을 완화한다. 하지만 proof soundness를 검토하느라 proposal/proving path가 막혀 있는 동안에도 submission을 받으면, 사용자는 fee를 내고 탈출구 없는 queue에 들어갈 수 있다. 검열 저항 장치도 pause semantics가 필요하다.
다섯째, multi-prover는 key-management 없이 완성되지 않는다. Taiko의 SGX 설명 글은 multi-prover가 proof-system diversity를 만든다고 말한다. 맞다. 하지만 SGX류 proof는 “서명한 실행 환경이 정말 승인된 환경인가”라는 identity/authentication 문제를 추가한다. verifier contract, registry, deployment script, secret scanning, key rotation, incident invalidation이 함께 설계되어야 한다.
5. 내 프로젝트에 적용할 체크포인트
-
브리지 메시지의 명제를 쪼개라
- “proof가 맞다”
- “source event가 있었다”
- “source checkpoint가 현재 신뢰된다”
- “message가 아직 실행되지 않았다”
- “verifier가 현재 승인된 trust root다”를 별도 assert로 적어라.
-
message status 전이를 threat model에 넣어라
NEW → PROVEN/RETRIABLE → DONE전이에서 replay, retry, rollback, partial execution이 가능한지 테스트하라.- 사고 대응 시 특정 message hash를 어떤 상태로 고정할지 runbook을 만들어라.
-
checkpoint/signal namespace versioning을 검토하라
- bridge proof가 과거 checkpoint에 의존한다면, 사고 후 “old checkpoint 전체 무효화”가 가능한가?
- storage migration 없이 namespace를 바꿀 수 있는가?
-
forced inclusion과 permissionless proving의 pause semantics를 문서화하라
- processing을 막을 때 submission도 같이 막는가?
- 이미 낸 fee와 queued item은 어떻게 환불/소비/무효화하는가?
-
verifier registry를 배포 주소 목록이 아니라 신뢰 문서로 관리하라
- verifier별 proof type, key custody, rotation path, revocation path, emergency pauser를 기록하라.
- SGX/TEE 기반 proof라면 secret scanning과 key provenance 확인을 CI/CD에 넣어라.
-
복구 PR을 보면 사고의 실제 신뢰 경계가 보인다
- postmortem만 읽지 말고 recovery PR, proposal script, deployment script, quota manager, pauser 변경을 같이 읽어라.
- “어디를 고쳤는가”보다 “어떤 상태를 더 이상 믿지 않기로 했는가”를 찾아라.
6. 오늘 10분 액션
- 내 프로젝트 또는 관심 있는 L2 브리지의 message lifecycle을 한 줄로 그린다.
proof valid,event exists,checkpoint trusted,message not executed,verifier authorized를 서로 다른 체크박스로 분리한다.- 이미 prove된 메시지를 무효화해야 할 때 사용할 수 있는 상태 전이를 찾는다.
- forced inclusion, retry, withdrawal quota가 pause 중 어떤 동작을 하는지 문서에서 확인한다.
- verifier registry 또는 prover key가 있는 프로젝트라면 key rotation과 revocation 경로를 README가 아니라 운영 runbook으로 적는다.
7. 더 볼 자료
- The Block - Taiko confirms exploit: Taiko의 공개 경고, forged message proof, bridge-wide withdrawal 안내를 사건 흐름으로 확인할 수 있다.
- PR #21820: SignalService versioning, Bridge.init3, Inbox.init2가 어떤 문제를 막으려는지 볼 수 있다.
- PR #21847: recovery deploy bundle, QuotaManager, forced inclusion submission 비활성화가 운영 리스크와 어떻게 연결되는지 확인할 수 있다.
- PR #21833: Proposal0017이 stale verifier trust와 L1 recovery bundle을 어떻게 다루는지 볼 수 있다.
- Taiko Labs - SGX and multi-prover: SGX proof가 rollup multi-prover에서 어떤 장점과 신뢰 가정을 갖는지 배경을 잡을 수 있다.
중복 회피 메모
로컬 content/generated와 Supabase의 최근 blockchain_dev 글을 확인했다. 최근 글은 OP Stack fault proof의 BLOBBASEFEE 재현성, ERC-4626 vault NAV 조작, P-256/passkey precompile, MODEXP gas repricing, ERC-1271 fail-closed 검증, Arbitrum BoLD finality, EIP-8184 encrypted mempool, EIP-7702 delegation, Syscoin/Axelar bridge parsing을 다뤘다. 이번 글은 기존 Syscoin의 cross-layer parsing이나 OP Stack fault proof soundness를 반복하지 않고, Taiko 사고와 복구 PR을 통해 rollup bridge의 message-origin verification, SignalService checkpoint versioning, retriable message invalidation, forced inclusion pause semantics에 집중한다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)