Oasis Sapphire를 이용한 기밀 버그 바운티 플랫폼 구축
사적인 취약점 보고서, 기밀 트리아지(triage), 검증 가능한 바운티 지급에 대한 고급 튜토리얼
버그 바운티 플랫폼은 어려운 보안 문제를 가지고 있습니다.
연구자가 스마트 컨트랙트의 취약점을 발견하고 보고서를 제출합니다. 이 보고서에는 익스플로잇 단계, 개념 증명(proof-of-concept) 코드, 영향을 받는 엔드포인트 또는 다른 사람이 이슈를 재현할 수 있게 하는 세부 정보가 포함될 수 있습니다.
만약 보고서가 너무 일찍 공개되면, 공개 절차가 공격자의 사용 설명서로 변질될 수 있습니다.
하지만 모든 것을 사적으로 만드는 것은 또 다른 문제를 야기합니다. 연구자들은 자신들의 제출물이 타임스탬프(timestamped)되고, 공정하게 평가되며, 명확한 규칙에 따라 보상받을 것이라는 확신이 필요합니다.
여기에 Oasis Sapphire가 흥미로운 사용 사례를 제공합니다.
Sapphire는 기밀 스마트 컨트랙트 상태 및 실행을 위한 EVM 호환 환경을 제공합니다. 민감한 보고서 내용을 공개 컨트랙트 저장소나 이벤트에 직접 넣는 대신, 보고서는 기밀로 유지하면서 제출 약속(submission commitments), 바운티 규칙, 최종 지급 기록은 독립적으로 검증할 수 있는 시스템을 설계할 수 있습니다.
이 튜토리얼에서는 Solidity와 Oasis Sapphire를 사용하여 고급 기밀 버그 바운티 플랫폼을 설계해 보겠습니다.
다룰 내용은 다음과 같습니다:
- 기밀 취약점 제출
- 보고서 무결성을 제공하는 약속(Commitments)
- 사적인 트리아지 및 심각도 평가
- 바운티 정책 시행
- 중복 제출 처리
- 통제된 공개(Controlled disclosure)
- 검증 가능한 지급 기록
- 위협 모델링 및 프로덕션 고려 사항
핵심 설계 원칙은 간단합니다:
공개하기 안전할 때까지 취약점을 기밀로 유지하되, 처음부터 바운티 프로세스를 책임감 있게 만듭니다.
1. 아키텍처: 보고서와 그 증거를 분리하기**
첫 번째 실수는 전체 취약점 보고서를 온체인(onchain)에 올리는 것입니다.
Solidity 변수가 private으로 표시되어 있더라도, 해당 키워드는 일반적인 공개 EVM 체인에서 기밀성을 제공하지 않습니다. Sapphire의 기밀 실행 환경(confidential execution environment)은 우리에게 다른 기반을 제공하지만, 우리는 여전히 이벤트(events), 공개 인터페이스(public interfaces), 또는 트랜잭션 메타데이터를 통해 민감한 데이터가 실수로 유출되는 것을 방지해야 합니다.
우리는 시스템을 세 가지 구성 요소로 분리할 것입니다.
flowchart TD
R[보안 연구원] -->|암호화된 제출| S[Sapphire 컨트랙트]
S --> C[기밀 보고서 상태]
...
보고서는 취약점의 기술적 세부 사항을 담고 있습니다. 커밋먼트(commitment)는 특정 보고서가 제출되었다는 연구원에게 증거를 제공합니다. 지급 기록(payout record)은 익스플로잇 자체를 공개하지 않으면서도 재정적인 결과를 감사 가능하게 만듭니다.
이들은 별개의 보장입니다.
커밋먼트가 보고서가 유효하다는 것을 증명하지는 않습니다. 기밀 컨트랙트가 모든 트랜잭션 필드를 자동으로 비공개로 만들지는 않습니다. 지급 기록이 분류(triage) 결정이 정확했다는 것을 증명하지도 않습니다.
시스템은 이러한 경계들을 명시적으로 만들어야 합니다.
2. 제출 라이프사이클 정의
바운티 플랫폼은 보고서가 제출에서 지급으로 바로 건너뛰지 못하도록 하는 라이프사이클이 필요합니다.
우리는 다음 상태를 사용할 것입니다:
stateDiagram-v2
[*] --> Submitted
Submitted --> UnderReview
...
실제 구현에서는 결제가 공개 전에 필요한지, 논란이 되는 보고서는 어떻게 처리하는지, 그리고 프로젝트가 응답하지 않을 경우 무엇이 발생하는지를 정의해야 합니다.
이번 튜토리얼에서 중요한 구분은 **보고서 기밀성(report confidentiality)**과 프로세스 투명성(process transparency) 사이입니다. 우리는 보고서를 비공개로 유지하면서도 제한적이고 민감하지 않은 상태 및 최종 정산 기록을 노출할 수 있습니다.
3. 프로젝트 구조
우리는 Solidity, Hardhat, 그리고 TypeScript를 사용할 것입니다.
confidential-bounty/
├── contracts/
│ ├── ConfidentialBounty.sol
...
계약은 제출 상태와 정책 적용을 처리합니다. 클라이언트는 Sapphire 호환 트랜잭션 흐름을 통해 보고서를 암호화하고 제출할 책임이 있습니다. 재무부(treasury)는 바운티 시스템이 자금에 대한 무제한적인 수탁을 필요로 하지 않도록 별도로 정산 처리를 합니다.
4. 보고서 신중하게 모델링하기
보고서는 문자열 이상의 것이 필요합니다.
최소한 식별자(identifier), 커밋먼트(commitment), 제출자(submitter), 상태(status), 그리고 바운티 정책 버전이 필요합니다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
...
이는 시작점일 뿐, 완전한 프로덕션 계약은 아닙니다.
예제는 의도적으로 보고서 본문을 이벤트(event)에 포함시키지 않았습니다. 커밋먼트는 특정 보고서가 등록된 제출물과 일치함을 확립하는 데 도움이 될 수 있지만, 보고서 ID와 제출자 주소는 여전히 이벤트에서 표시됩니다.
만약 제출자의 신원이나 제출 시기가 민감하다면, 이 메타데이터조차 추가적인 설계가 필요합니다.
Sapphire에서는 기밀 보고서 본문이 기밀 계약 상태(confidential contract state)를 통해 저장 및 처리되어야 하며, 공개 인터페이스는 공개 정책이 명시적으로 허용하는 정보만을 드러내야 합니다.
5. 보고서를 공개하지 않고 커밋하기
연구자가 제출 후 보고서가 조용히 대체되지 않았음을 어떻게 증명할 수 있을까요?
유용한 시작점은 암호학적 커밋먼트입니다.
다음과 같이 정의합니다:
report: 전체 보고서 내용
salt: 높은 엔트로피의 무작위 비밀 값
version: 보고서 형식을 식별하는 값
연구자는 다음을 계산합니다:
commitment = keccak256(
encode(version, report, salt)
)
이 커밋먼트는 보고서 ID와 함께 등록될 수 있습니다. 나중에 연구자는 보고서와 비밀 값을 공개하여 공개된 내용이 원래의 커밋먼트와 일치함을 입증할 수 있습니다.
하지만 여기에는 중요한 주의사항이 있습니다.
해시(hash)는 낮은 엔트로피 정보(low-entropy information)를 자동으로 보호하지 못합니다. 보고서가 예측 가능하다면, 누군가는 후보 내용을 추측하고 그 해시값을 비교할 수 있습니다. 무작위 솔트(random salt)는 안전하게 생성되어 공개 시점까지 비밀로 유지되어야 합니다.
클라이언트는 표준화된 인코딩 형식(canonical encoding format)을 사용해야 하며, 컨트랙트(contract)는 커밋먼트(commitment)에 포함되는 필드(field)가 무엇인지 정확히 정의해야 합니다. 그렇지 않으면 동일한 논리적 보고서의 다른 인코딩이 일관성 없는 커밋먼트를 생성할 수 있습니다.
기밀 소스 코드나 익스플로잇 세부 정보(exploit details)를 포함하는 보고서의 경우, 커밋먼트가 암호화된 저장소(encrypted storage)를 대체해서는 안 됩니다. 이는 기밀성 메커니즘(confidentiality mechanism)이 아니라 무결성 메커니즘(integrity mechanism)을 제공합니다.
6. 버전 관리 정책으로 바운티 규칙 강제 적용하기 (Enforce Bounty Rules with Versioned Policies)
바운티 프로그램은 보상 한도, 지원 대상, 심각도 정의 등을 자주 변경하는 경향이 있습니다.
만약 보고서가 검토되는 도중에 정책이 변경된다면, 어떤 버전의 정책을 적용해야 할까요?
답변은 명확해야 합니다.
모든 보고서는 제출 당시 활성화되어 있던 정책 버전을 참조해야 합니다. 이 정책은 다음 사항들을 정의해야 합니다:
- 적격 컨트랙트 또는 시스템 (Eligible contracts or systems)
- 심각도 카테고리 (Severity categories)
- 보상 범위 (Reward ranges)
- 중복 보고서 규칙 (Duplicate-report rules)
- 제외 항목 (Exclusions)
- 공개 요구 사항 (Disclosure requirements)
- 검토 마감일 (Review deadlines)
예를 들어:
struct BountyPolicy {
uint128 criticalMax;
uint128 highMax;
...
실제 운영 환경(production)에서 정책을 변경할 때는 기존 보고서와 관련된 약관을 조용히 재작성하는 대신 새로운 버전을 생성해야 합니다.
컨트랙트는 선택된 정책 버전이 존재하는지, 그리고 해당 보고서가 프로그램에 의해 허용되는 버전을 참조하는지 검증해야 합니다.
보상 승인 프로세스(reward authorization process) 역시 보고서가 적격 상태인지, 제안된 보상이 관련 한도 내에 있는지, 그리고 요청이 이미 사용되지 않았는지 확인해야 합니다.
7. 트리아지 과정은 기밀하게 유지하되, 책임 소재를 명확히 하기 (Keep Triage Confidential, but Make It Accountable)
기밀 보고서에는 소수의 검토 팀만 접근할 수 있는 정보가 포함될 수 있습니다.
하지만 그렇다고 해서 트리아지(triage) 과정 자체가 무제한적인 블랙박스(black box)여야 한다는 의미는 아닙니다.
프로덕션 디자인은 다음 권한들을 분리해야 합니다:
- 보고서 읽기.
- 상태 변경하기.
- 심각도 할당하기.
- 보상 승인하기.
- 공개 승인하기.
이러한 권한들이 같은 주소에 자동으로 속해서는 안 됩니다.
멀티시그(multisig) 또는 거버넌스 제어 역할이 검토자 멤버십을 관리할 수 있으며, 전용 승인 경로가 보상 한도를 강제합니다. 더 복잡한 오프체인 분석의 경우, ROFL 애플리케이션은 신뢰 실행 환경(trusted execution environment) 내부에서 선택된 계산을 수행하고 해당 계산의 출처에 대한 증거를 제공할 수 있습니다.
ROFL을 마법 같은 정확성 오라클로 취급해서는 안 됩니다. 원격 증명(Remote attestation)은 실행 중인 환경과 코드를 검증하는 증거를 제공할 수 있지만, 근본적인 취약점 평가가 객관적으로 정확하다는 것을 증명하지는 못합니다.
검토자는 여전히 건전한 보안 추론이 필요하며, 프로토콜은 분쟁에 대한 명확한 프로세스를 갖추어야 합니다.
8. 더 안전한 공개 워크플로우
공개(Disclosure)는 시스템에서 가장 중요한 부분 중 하나입니다.
보고서가 단순히 상태가 Accepted로 변경되었다고 해서 공적으로 공개되어서는 안 됩니다.
대신, 명시적인 조건이 있는 별도의 공개 프로세스를 정의해야 합니다:
sequenceDiagram
participant R as Researcher
participant C as Sapphire Contract
...
이는 개념적 워크플로우입니다. 프로덕션 시스템은 인증된 검토자 접근, 권한 확인, 마감일, 분쟁 해결 및 안전한 보고서 전달을 구현해야 합니다.
특히, 보고서 본문을 반환하는 공개 함수는 기밀성을 훼손할 수 있습니다. 검토자 접근은 Sapphire가 지원하는 기밀 호출(confidential call) 및 인증 패턴을 사용해야 하며, 민감한 보고서 데이터는 일반적인 공개 이벤트에 절대 방출되어서는 안 됩니다.
9. 위협 모델
배포 전에 최소한 다음의 위협들을 고려하십시오.
| 위협 (Threat) | 완화 (Mitigation) |
|---|
| 이벤트(events)를 통해 보고 내용이 유출됨 | 승인된 메타데이터만 방출함 |
| ... | |
| Confidential computing 역시 한계가 있습니다. 트랜잭션 타이밍, 가스 사용량, 입력 크기, 관측 가능한 상태 전환 등을 통해 정보가 노출될 수 있기 때문입니다. Sapphire 개발자들은 플랫폼의 보안 지침을 검토하고 암호화된 상태가 모든 측면 채널(side channel)을 제거한다고 가정하는 것을 피해야 합니다. | |
10. 버그 바운티 외에 무엇이 유용한가?
동일한 설계 패턴은 기밀 제출과 책임 있는 의사결정이 필요한 다른 애플리케이션들을 지원할 수 있습니다:
- 비공개 보안 감사 (Private security audits)
- 기밀 취약점 공개 (Confidential vulnerability disclosure)
- 책임 있는 AI 사고 보고 (Responsible AI incident reporting)
- 비공개 규정 준수 보고서 (Private compliance reports)
- 기밀 보조금 신청 (Confidential grant applications)
- 내부 고발자 제출 시스템 (Whistleblower submission systems)
각 경우 모두 핵심 문제는 유사합니다. 즉, 민감한 정보가 기본적으로 공개되지 않으면서도 권한이 있는 검토자에게 제공되어야 하며, 이 과정에는 증거(evidence), 권한(permissions), 그리고 명확한 결과(clear outcome)가 필요하다는 것입니다.
가장 중요한 아키텍처 결정은 기밀 콘텐츠 (confidential content), 공개 증거 (public evidence), 그리고 제출물에 대한 **조치 권한 (authority to act on a submission)**을 분리하는 것입니다.
Oasis Sapphire는 이러한 종류의 애플리케이션을 구축하기 위한 기밀 EVM 환경을 제공합니다. ROFL은 검증 가능한 오프체인 컴퓨팅(verifiable offchain computation)이 유용할 때 아키텍처를 확장할 수 있습니다. 어느 쪽도 신중한 접근 제어, 암호화 설계 또는 잘 정의된 공개 프로세스의 필요성을 제거하지는 않습니다.
결론
버그 바운티 플랫폼은 기밀성(confidentiality)과 투명성(transparency)이 상반될 필요가 없다는 것을 보여주는 유용한 예시입니다.
취약점 보고서는 기밀로 유지될 수 있습니다. 제출물 커밋먼트(submission commitment)는 무결성의 증거를 제공할 수 있습니다. 정책은 보상을 제한할 수 있습니다. 정산(settlement)은 독립적으로 검증될 수 있습니다. 그리고 공개는 적절한 조건이 충족될 때만 발생할 수 있습니다.
이는 모든 세부 사항을 온체인(onchain)에 게시하거나 모두에게 사설 데이터베이스를 신뢰하도록 요구하는 것보다 훨씬 더 유용한 모델입니다.
전체 프로세스를 눈에 보이지 않게 만드는 것이 목표가 아닙니다.
중요한 결정은 책임 소재를 명확히 하면서 민감한 정보만 비공개로 유지하는 것입니다.
자료 (Resources)