SettleLab
전체 코스
LESSON 05Security Testing

스테이블코인 보안리뷰 체크리스트

심화45분근거 6

학습 결과

  • 코드 리뷰 체크리스트를 출시 차단 기준으로 바꾼다.
  • 토큰/결제/운영 영역별 리뷰 증거를 남긴다.
  • 잔여 위험을 owner, 만료일, 승인 조건이 있는 문서로 관리한다.

선행 조건

  • Slither와 Echidna 보안 워크플로

완료 기준

  • 리뷰 범위를 권한, 상태, 서명, 외부 호출, 테스트, 운영 영역으로 나눴다.
  • 출시 차단 항목을 정의했다.
  • 잔여 위험 승인 양식을 만들었다.

스테이블코인 보안리뷰 체크리스트

도입

보안 리뷰 체크리스트는 코드 리뷰 전에 복사해서 쓰는 목록이지만, 그대로 체크만 하면 출시 판단에 약하다. 실무에서는 각 항목에 evidence, owner, status, blocker 여부를 붙여야 한다. 리뷰가 끝났을 때 남아야 하는 것은 "봤다"가 아니라 "어떤 근거로 출시 가능하다고 판단했는가"이다.

이 강의는 앞선 Solidity 기초, AccessControl, invariant, Slither/Echidna 결과를 하나의 review package로 묶는다. 특히 permit checkout처럼 서명과 token transfer가 섞이는 시스템은 권한, 상태, 서명, 외부 호출, 테스트, 운영을 모두 통과해야 한다.

학습 목표

  • 코드 리뷰 체크리스트를 출시 차단 기준으로 바꾼다.
  • 토큰/결제/운영 영역별 리뷰 증거를 남긴다.
  • 잔여 위험을 owner, 만료일, 승인 조건이 있는 문서로 관리한다.

개념 설명

판단 트리가로 스크롤 · 크게 보기 지원
스테이블코인 보안리뷰 체크리스트 판단 트리이 시각화는 보안 리뷰와 테스트 캠페인에서 진행, 보류, 재설계 결정을 가르는 질문이 무엇인지를 보여주며, '스테이블코인 보안리뷰 체크리스트'에서 남겨야 할 설계 증거를 좁힌다.
시작 질문

스테이블코인 보안리뷰 체크리스트를 제품 설계에 넣어도 되는가?

진행

권한 경계가 테스트되는가

Security review package 만들기
보류

불변조건이 실패 상태를 잡는가

가정과 실패 상태를 보강한다.
차단

운영 변경이 모니터링되는가

캡스톤 risk register에 차단 사유를 남긴다.
크게 보기
시작 질문

스테이블코인 보안리뷰 체크리스트를 제품 설계에 넣어도 되는가?

진행

권한 경계가 테스트되는가

Security review package 만들기
보류

불변조건이 실패 상태를 잡는가

가정과 실패 상태를 보강한다.
차단

운영 변경이 모니터링되는가

캡스톤 risk register에 차단 사유를 남긴다.

1. Review package 형식

표 자료가로 스크롤 · 크게 보기 지원
필드의미
Area권한, 상태, 서명, 외부 호출, 테스트, 운영
Check확인해야 할 항목
Evidence코드 링크, 테스트 결과, 도구 리포트, runbook, screenshot
Owner누가 해결하거나 승인할 것인가
StatusGreen, Yellow, Red
BlockerRed일 때 출시 차단인지
Notes남은 위험과 후속 작업
크게 보기
필드의미
Area권한, 상태, 서명, 외부 호출, 테스트, 운영
Check확인해야 할 항목
Evidence코드 링크, 테스트 결과, 도구 리포트, runbook, screenshot
Owner누가 해결하거나 승인할 것인가
StatusGreen, Yellow, Red
BlockerRed일 때 출시 차단인지
Notes남은 위험과 후속 작업

체크리스트 항목은 release gate와 연결된다. High severity 또는 P0/P1 항목은 "추후 개선"으로 넘기지 않는다.

2. 권한 리뷰

표 자료가로 스크롤 · 크게 보기 지원
CheckEvidenceBlocker
모든 privileged function에 modifier 또는 explicit check가 있다.function list와 modifier mapYes
role admin이 누구인지 문서화되어 있다.role matrixYes
마지막 admin 제거가 불가능하다.unit/invariant testYes
role grant/revoke event가 있다.event test, dashboard alertYes
production admin은 EOA가 아니라 multisig/timelock이다.Safe/timelock configYes
크게 보기
CheckEvidenceBlocker
모든 privileged function에 modifier 또는 explicit check가 있다.function list와 modifier mapYes
role admin이 누구인지 문서화되어 있다.role matrixYes
마지막 admin 제거가 불가능하다.unit/invariant testYes
role grant/revoke event가 있다.event test, dashboard alertYes
production admin은 EOA가 아니라 multisig/timelock이다.Safe/timelock configYes

권한 리뷰는 코드만 보지 않는다. Production owner가 누구인지, signer rotation은 가능한지, key compromise 때 어떤 alert가 뜨는지까지 포함한다.

3. 상태 리뷰

표 자료가로 스크롤 · 크게 보기 지원
CheckEvidenceBlocker
totalSupply와 balances 변화가 일관된다.invariant test, mint/burn testsYes
payment state transition이 단방향이다.state diagram, transition testsYes
refund/claim이 idempotent하다.duplicate refund/claim testsYes
deadline/validBefore 만료 처리가 있다.boundary testsYes
pause/freeze 상태가 모든 관련 함수에 적용된다.bypass testsYes
크게 보기
CheckEvidenceBlocker
totalSupply와 balances 변화가 일관된다.invariant test, mint/burn testsYes
payment state transition이 단방향이다.state diagram, transition testsYes
refund/claim이 idempotent하다.duplicate refund/claim testsYes
deadline/validBefore 만료 처리가 있다.boundary testsYes
pause/freeze 상태가 모든 관련 함수에 적용된다.bypass testsYes

상태 리뷰의 핵심은 "성공 경로"가 아니라 "한 번 처리된 일이 다시 처리되지 않는가"이다. 결제에서는 idempotency가 보안이다.

4. 서명 리뷰

표 자료가로 스크롤 · 크게 보기 지원
CheckEvidenceBlocker
EIP-712 domain에 chain ID와 verifying contract가 있다.domain test, typed data fixtureYes
nonce가 재사용 불가능하다.replay testYes
signature signer가 zero address가 아니다.invalid signature testYes
signature malleability를 고려한다.low-s or library usage evidenceYes
typed data에 사용자가 이해할 field가 들어간다.UI screenshot, copy reviewYellow
크게 보기
CheckEvidenceBlocker
EIP-712 domain에 chain ID와 verifying contract가 있다.domain test, typed data fixtureYes
nonce가 재사용 불가능하다.replay testYes
signature signer가 zero address가 아니다.invalid signature testYes
signature malleability를 고려한다.low-s or library usage evidenceYes
typed data에 사용자가 이해할 field가 들어간다.UI screenshot, copy reviewYellow

서명 리뷰는 보안과 UX가 만나는 영역이다. 사용자가 어떤 spender, recipient, amount, token, chain에 서명하는지 볼 수 없으면 phishing과 운영 사고 가능성이 커진다.

5. 외부 호출 리뷰

표 자료가로 스크롤 · 크게 보기 지원
CheckEvidenceBlocker
token transfer return value를 안전하게 처리한다.SafeERC20 또는 equivalent testYes
external call 전후 state update 순서가 의도적이다.CEI review noteYes
reentrancy 가능성을 검토했다.Slither finding triage, reentrancy testYes
callback이 있는 token 표준을 받지 않는다면 allowlist로 제한한다.token allowlist, route policyYes
oracle/price/route output을 신뢰할 때 freshness와 slippage를 본다.dashboard threshold, route simulationYellow 또는 Yes
크게 보기
CheckEvidenceBlocker
token transfer return value를 안전하게 처리한다.SafeERC20 또는 equivalent testYes
external call 전후 state update 순서가 의도적이다.CEI review noteYes
reentrancy 가능성을 검토했다.Slither finding triage, reentrancy testYes
callback이 있는 token 표준을 받지 않는다면 allowlist로 제한한다.token allowlist, route policyYes
oracle/price/route output을 신뢰할 때 freshness와 slippage를 본다.dashboard threshold, route simulationYellow 또는 Yes

외부 호출 리뷰는 "호출이 성공하는가"보다 "실패했을 때 어떤 상태가 남는가"를 묻는다. Refund 실패가 payment success를 덮어쓰면 안 된다.

6. 테스트 리뷰

표 자료가로 스크롤 · 크게 보기 지원
CheckEvidenceBlocker
권한 실패 테스트unit testsYes
freeze/pause 우회 테스트bypass testsYes
nonce replay 테스트signature replay suiteYes
fuzz 테스트fuzz reportYellow
invariant 테스트invariant campaign resultYes
event 검증emitted event testsYellow
fork/integration 테스트 필요성 검토dependency listYellow
크게 보기
CheckEvidenceBlocker
권한 실패 테스트unit testsYes
freeze/pause 우회 테스트bypass testsYes
nonce replay 테스트signature replay suiteYes
fuzz 테스트fuzz reportYellow
invariant 테스트invariant campaign resultYes
event 검증emitted event testsYellow
fork/integration 테스트 필요성 검토dependency listYellow

테스트는 통과 여부뿐 아니라 커버하는 위험을 보여줘야 한다. "invariant 테스트 있음" 대신 어떤 invariant를 막는지 적는다.

7. 운영 리뷰

표 자료가로 스크롤 · 크게 보기 지원
CheckEvidenceBlocker
key rotation 절차가 있다.ops runbookYellow
pause/freeze 실행 권한과 승인 절차가 있다.emergency runbookYes
monitoring alert가 있다.dashboard screenshot, alert testYes
incident response runbook이 있다.incident docYes
upgrade plan과 rollback plan이 있다.upgrade proposal templateYes
크게 보기
CheckEvidenceBlocker
key rotation 절차가 있다.ops runbookYellow
pause/freeze 실행 권한과 승인 절차가 있다.emergency runbookYes
monitoring alert가 있다.dashboard screenshot, alert testYes
incident response runbook이 있다.incident docYes
upgrade plan과 rollback plan이 있다.upgrade proposal templateYes

운영 리뷰는 감사 리포트 이후에도 남는다. 배포 후 role change, pause, freeze, upgrade event를 보지 못하면 리뷰 당시 안전했던 설계도 운영 중 무너질 수 있다.

8. 출시 차단 기준

표 자료가로 스크롤 · 크게 보기 지원
Red condition출시 판정
권한 없는 mint/burn/freeze/pause 가능No-Go
replay 가능한 서명 또는 nonce 재사용No-Go
refund/settlement 중복 처리 가능No-Go
paused/frozen 우회 경로 존재No-Go
failing invariant 또는 untriaged high Slither findingNo-Go
production admin이 단일 EOA이고 보완 통제가 없음No-Go
monitoring/runbook 없이 upgrade 가능No-Go
크게 보기
Red condition출시 판정
권한 없는 mint/burn/freeze/pause 가능No-Go
replay 가능한 서명 또는 nonce 재사용No-Go
refund/settlement 중복 처리 가능No-Go
paused/frozen 우회 경로 존재No-Go
failing invariant 또는 untriaged high Slither findingNo-Go
production admin이 단일 EOA이고 보완 통제가 없음No-Go
monitoring/runbook 없이 upgrade 가능No-Go

Yellow 항목은 제한 출시 조건과 owner가 있어야 한다. 예를 들어 typed data copy가 부족하면 beta 한도와 개선 기한을 둔다. 하지만 돈의 상태를 직접 깨뜨리는 항목은 Yellow가 아니라 Red다.

9. 잔여 위험 승인 양식

표 자료가로 스크롤 · 크게 보기 지원
Field작성 내용
Risk statement무엇이 남아 있는가
Impactuser funds, merchant settlement, compliance, availability 영향
Evidence왜 지금 release blocker가 아닌가
Compensating control한도, monitoring, manual review, timelock
Owner위험을 추적할 사람
Expiry재검토 날짜
Approverproduct/security/engineering 승인자
크게 보기
Field작성 내용
Risk statement무엇이 남아 있는가
Impactuser funds, merchant settlement, compliance, availability 영향
Evidence왜 지금 release blocker가 아닌가
Compensating control한도, monitoring, manual review, timelock
Owner위험을 추적할 사람
Expiry재검토 날짜
Approverproduct/security/engineering 승인자

Accepted risk는 영구 면제가 아니다. 만료일이 지나면 다시 Red 또는 Yellow로 돌아온다.

코드로 확인하기

앞의 보안 기준을 코드와 테스트로 확인한다. 함수가 어떤 전제를 세우고, 테스트가 어떤 실패 조건을 고정하는지 함께 읽는다.

운영리뷰 패킷 YAML 템플릿

각 강의를 통해 모은 증거를 한 곳에 모아 release gate 가 읽을 수 있는 형태로 남긴다. PR 머지 조건으로 이 파일의 모든 status가 green 또는 accepted 여야 한다.

CODE SURFACEyaml
# security-review/v1.2.0.yamlrelease: v1.2.0reviewers:  - role: security-lead    name: alice  - role: protocol-lead    name: bobsections:  permissions:    status: green    evidence:      - "test: RoleMatrix.t.sol → PASS"      - "audit: external-audit-2026-04.pdf §3.2"  signatures:    status: green    evidence:      - "test: PermitDomain.t.sol → PASS"      - "test: AuthorizationReplay.t.sol → PASS"  external_calls:    status: yellow    evidence:      - "transferFrom 실패 상태 처리 partial — issue #482"    compensating_controls:      - "merchant beta whitelist 적용"      - "결제 한도 1k USDC"    owner: payments-team    expires: 2026-07-01  invariants:    status: green    evidence:      - "echidna: 20000 runs, 0 failing"      - "foundry invariant: nightly 200 depth 통과"release_gate:  blocker:    - "any section status == red"    - "expired residual risk without re-approval"

운영CI에서 release gate 검사 — GitHub Actions

security-review yaml을 PR 머지 조건으로 강제한다. yellow는 owner와 expiry가 있어야 통과, red는 차단.

CODE SURFACEyaml
name: release-gateon:  pull_request:    paths: [security-review/**, src/**]jobs:  gate:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v4      - name: Validate review packet        run: |          npx tsx scripts/check-review-packet.ts security-review/v1.2.0.yaml
CODE SURFACEtypescript
// scripts/check-review-packet.tsimport { readFileSync } from "node:fs";import { parse } from "yaml";const packet = parse(readFileSync(process.argv[2], "utf8"));const errors: string[] = [];for (const [name, section] of Object.entries(packet.sections) as [string, any][]) {  if (section.status === "red") {    errors.push(`${name}: status=red — release blocked`);  }  if (section.status === "yellow") {    if (!section.owner) errors.push(`${name}: yellow without owner`);    if (!section.expires) errors.push(`${name}: yellow without expiry`);    else if (new Date(section.expires) < new Date()) {      errors.push(`${name}: yellow expired ${section.expires}`);    }  }}if (errors.length > 0) {  console.error(errors.join("\n"));  process.exit(1);}

강의 포인트

표 자료가로 스크롤 · 크게 보기 지원
관점강의 중 확인할 질문학습 후 남길 증거
Review scope권한, 상태, 서명, 외부 호출, 테스트, 운영을 모두 봤는가?review package
Evidence각 항목의 근거가 링크와 테스트로 남았는가?evidence column
Release gate어떤 항목이 No-Go인지 명확한가?blocker table
Residual risk남은 위험에 owner와 expiry가 있는가?승인 양식
크게 보기
관점강의 중 확인할 질문학습 후 남길 증거
Review scope권한, 상태, 서명, 외부 호출, 테스트, 운영을 모두 봤는가?review package
Evidence각 항목의 근거가 링크와 테스트로 남았는가?evidence column
Release gate어떤 항목이 No-Go인지 명확한가?blocker table
Residual risk남은 위험에 owner와 expiry가 있는가?승인 양식

실무 예시

운영[CONTRACT] 상황: permit checkout contract를 리뷰한다. 사용자는 EIP-712 permit에 서명하고, relayer가 permit + transferFrom을 실행해 merchant에게 결제한다.

표 자료가로 스크롤 · 크게 보기 지원
AreaCheckEvidenceStatus
서명domain에 chain ID와 verifying contract 포함typed data fixture testGreen
서명nonce replay 방지replay testGreen
권한relayer가 amount/recipient를 바꿀 수 없음calldata binding testGreen
외부 호출permit 성공 후 transferFrom 실패 상태 처리state transition testYellow
상태payment가 두 번 Paid 되지 않음idempotency testGreen
운영relayer outage runbookdraft onlyYellow
테스트invariant campaign 통과report linkGreen
크게 보기
AreaCheckEvidenceStatus
서명domain에 chain ID와 verifying contract 포함typed data fixture testGreen
서명nonce replay 방지replay testGreen
권한relayer가 amount/recipient를 바꿀 수 없음calldata binding testGreen
외부 호출permit 성공 후 transferFrom 실패 상태 처리state transition testYellow
상태payment가 두 번 Paid 되지 않음idempotency testGreen
운영relayer outage runbookdraft onlyYellow
테스트invariant campaign 통과report linkGreen

이 표에서 Yellow는 출시 전 제한 조건을 요구한다. 예를 들어 relayer outage runbook이 draft라면 beta merchant만 허용하고 결제 한도를 낮춘다. transferFrom 실패 상태 처리가 없으면 사용자의 permit만 소모되고 결제가 실패할 수 있으므로 구현 전에는 Green으로 바꿀 수 없다.

흔한 오해와 실패 시나리오

표 자료가로 스크롤 · 크게 보기 지원
오해실제로 확인할 것
체크리스트 항목에 체크하면 리뷰가 끝났다고 본다.evidence, owner, status가 있어야 한다.
false positive는 설명 없이 닫아도 된다고 본다.accepted risk 양식과 expiry가 필요하다.
테스트 수가 많으면 충분하다고 본다.어떤 위험을 막는 테스트인지 연결해야 한다.
운영 문서는 보안 리뷰 범위 밖이라고 본다.pause, freeze, upgrade, monitoring은 보안 경계다.
크게 보기
오해실제로 확인할 것
체크리스트 항목에 체크하면 리뷰가 끝났다고 본다.evidence, owner, status가 있어야 한다.
false positive는 설명 없이 닫아도 된다고 본다.accepted risk 양식과 expiry가 필요하다.
테스트 수가 많으면 충분하다고 본다.어떤 위험을 막는 테스트인지 연결해야 한다.
운영 문서는 보안 리뷰 범위 밖이라고 본다.pause, freeze, upgrade, monitoring은 보안 경계다.

실습 과제

  1. 운영Security review package 만들기: 권한, 상태, 서명, 외부 호출, 테스트, 운영 영역별로 check, evidence, owner, status, blocker 여부를 채운다.
  2. 컨트랙트Permit checkout 리뷰표 작성하기: permit checkout contract를 가정하고 domain separator, nonce, allowance, transferFrom, refund, monitoring 항목의 보안 리뷰 표를 작성한다.
  3. 운영잔여 위험 승인 양식 작성하기: false positive 또는 accepted risk마다 risk statement, impact, compensating control, owner, expiry, approver를 포함한 승인 양식을 만든다.

완료 기준

  1. 리뷰 범위를 권한, 상태, 서명, 외부 호출, 테스트, 운영 영역으로 나눴다.
  2. 출시 차단 항목을 정의했다.
  3. 잔여 위험 승인 양식을 만들었다.
  4. permit checkout 예시 리뷰표를 작성했다.

근거 자료

Final checkpoint

읽기를 마쳤다면 여기서 기록한다

아래 버튼은 읽기 진도를 저장한다. 체크리스트, 과제, 랩 산출물은 위 Workbook에서 따로 관리한다.

  • 리뷰 범위를 권한, 상태, 서명, 외부 호출, 테스트, 운영 영역으로 나눴다.
  • 출시 차단 항목을 정의했다.
  • 잔여 위험 승인 양식을 만들었다.

학습 자료 근거

보안리뷰 체크리스트
이 LMS 레슨의 개념, 예시, 과제 구성을 잡는 데 사용한 근거 문서.
내부 참고 문서
Solidity Security Considerations
https://docs.soliditylang.org/en/latest/security-considerations.html
OpenZeppelin Contracts: Access Control
https://docs.openzeppelin.com/contracts/5.x/access-control
OpenZeppelin Upgrades
https://docs.openzeppelin.com/upgrades/
Foundry Book: Invariant Testing
https://book.getfoundry.sh/forge/invariant-testing
Safe Smart Account Guards
https://docs.safe.global/advanced/smart-account-guards