Slither와 Echidna 보안 워크플로
도입
보안 도구는 정답 생성기가 아니라 triage 도구다. Slither는 정적 분석으로 빠르게 의심 지점을 찾고, Echidna와 Foundry는 property 기반 테스트로 상태 공간을 흔든다. 도구를 실행했다는 사실보다 중요한 것은 결과를 어떤 기준으로 분류하고, 어떤 항목을 release blocker로 삼는지다.
스테이블코인 checkout에서는 자동 도구가 access control 냄새, reentrancy pattern, unused code, property violation을 빠르게 찾아줄 수 있다. 하지만 "merchant settlement가 중복 실행되면 안 된다" 같은 비즈니스 불변식은 사람이 문장으로 정의해야 한다. 이 강의는 도구 결과를 리뷰 표, CI 실패 조건, waiver 정책으로 바꾸는 방법을 다룬다.
학습 목표
- 정적분석과 property fuzzing의 역할을 구분한다.
- 도구 결과를 triage하고 false positive를 문서화한다.
- Slither/Echidna 결과를 release gate와 CI 실패 조건으로 연결한다.
개념 설명
개념 읽기
정적분석과 property fuzzing의 역할을 구분한다.
실패 상태 확인
권한 경계가 테스트되는가
실습 산출물 작성
도구별 역할 분리표 만들기
완료 기준 대조
두 도구의 역할을 설명했다.
1. 도구별 역할을 먼저 나눈다
| 도구 | 역할 | 잘 잡는 것 | 한계 |
|---|---|---|---|
| Slither | static analyzer | access control 냄새, reentrancy pattern, unused code, dangerous low-level call | business logic과 운영 정책을 모른다. |
| Echidna | property-based fuzzer | user-defined property violation, unexpected state sequence | harness와 property 품질에 의존한다. |
| Foundry | unit/fuzz/invariant | 개발 루프에 가까운 regression, handler 기반 invariant | target/handler 설계가 약하면 얕게 돈다. |
| Manual review | 설계와 threat model 검토 | trust assumption, economics, role ownership, release risk | 사람 숙련도와 리뷰 시간에 의존한다. |
이 표는 도구 선택표가 아니라 리뷰 분업표다. Slither 경고가 없다고 business logic이 안전한 것은 아니고, Echidna property가 통과해도 property 자체가 약하면 의미가 없다.
2. Slither 결과는 detector가 아니라 impact로 분류한다
Slither detector 문서는 각 detector에 severity와 confidence를 붙인다. 리뷰팀은 이를 그대로 복사하지 않고 product impact로 번역해야 한다.
| Finding | Stablecoin에서 볼 impact | 분류 예시 |
|---|---|---|
| reentrancy pattern | refund/withdraw 중 중복 출금 가능성 | Real issue 또는 defense-in-depth |
| arbitrary ERC20 transferFrom | 사용자가 아닌 주소에서 자금 이동 가능성 | Real issue |
| unchecked low-level call | external settlement failure가 무시될 수 있음 | Real issue |
| delegatecall/proxy 관련 경고 | implementation/storage risk | Manual review required |
| unused return value | token transfer 실패 무시 가능성 | Real issue 또는 informational |
| unused code | 공격면/유지보수 비용 증가 | Informational |
| owner/admin 단일 EOA | key compromise risk | Release gate issue |
Slither는 "의심 지점"을 준다. Triage는 "이 지점이 돈의 상태를 바꾸는가, 권한을 우회하는가, release를 막아야 하는가"로 답해야 한다.
3. Echidna property는 불변식을 실행 가능한 질문으로 만든다
Echidna는 user-defined predicate나 assertion을 깨뜨리는 입력/호출 순서를 찾는 property-based fuzzer다. Stablecoin에서 Echidna에 맡길 수 있는 질문은 다음과 같다.
| Property | 의미 |
|---|---|
echidna_total_supply_consistent() | 추적 supply와 balances 관계가 깨지지 않는다. |
echidna_frozen_cannot_move() | frozen actor가 어떤 경로로도 balance를 줄이지 못한다. |
echidna_pause_blocks_transfer() | paused 상태에서 movement 함수가 성공하지 않는다. |
echidna_nonce_unique() | signed authorization nonce가 재사용되지 않는다. |
echidna_payment_idempotent() | payment/refund/settlement가 중복 처리되지 않는다. |
Property 이름은 결과 리포트에서 바로 읽히는 문장이어야 한다. echidna_test1처럼 이름이 애매하면 실패 triage가 느려진다.
4. Triage 템플릿
| Field | 작성 기준 |
|---|---|
| Tool | Slither, Echidna, Foundry, manual |
| Detector/property | detector 이름 또는 property 함수명 |
| Severity/confidence | 도구의 값과 리뷰팀 조정값을 모두 기록 |
| Affected contract/function | 출시 영향 범위 |
| Asset at risk | user balance, escrow, supply, admin key, merchant settlement |
| Classification | Real issue, defense-in-depth, false positive, informational |
| Evidence | finding output, failing sequence, code link, screenshot |
| Action | fix, add test, waive, document, monitor |
| Owner | 담당자 |
| Due date / waiver expiry | 방치 방지 |
False positive도 근거 없이 닫지 않는다. "코드 문맥상 안전"하다는 증거, 관련 테스트, 만료일이 있어야 한다. 코드가 바뀌면 false positive 판단도 다시 열릴 수 있다.
5. Stablecoin에서 우선순위 높은 경고
| 우선순위 | 경고 | 왜 중요한가 |
|---|---|---|
| P0 | 권한 없는 mint/burn 가능성 | supply 직접 손상 |
| P0 | paused/frozen 상태 우회 | compliance와 incident response 실패 |
| P0 | signature nonce 재사용 | 중복 결제 또는 unauthorized transfer |
| P1 | reentrancy 가능한 refund/withdraw | escrow 자금 중복 출금 |
| P1 | proxy storage/upgrade 위험 | token accounting 또는 role 손상 |
| P1 | unchecked token transfer return | 정산 실패를 성공으로 기록 |
| P2 | 단일 EOA admin | key compromise risk |
| P2 | unused/dead code | attack surface와 리뷰 비용 증가 |
Priority는 도구 severity와 다를 수 있다. 예를 들어 어떤 detector가 medium이라도 merchant settlement 손실로 이어지면 release blocker다.
6. CI 실패 조건
| 조건 | CI 결과 | 사람의 다음 행동 |
|---|---|---|
| 새 high severity Slither finding | fail | triage owner 지정 |
| 새 P0/P1 분류 finding | fail | fix 또는 release exception 승인 |
| Echidna property 실패 | fail | sequence 저장, 최소 재현 test 작성 |
| waiver 만료 | fail | 재검토 또는 owner 갱신 |
| unreviewed medium finding 증가 | fail 또는 warning | release meeting에서 triage |
| informational finding만 증가 | pass with report | backlog 등록 |
CI는 모든 보안 판단을 자동화하지 않는다. 대신 미검토 위험이 조용히 배포되는 것을 막는다.
7. 도구 결과를 release gate로 연결한다
이 흐름은 "도구를 한 번 돌렸다"는 체크박스를 release gate로 바꾼다. 보안 리뷰는 반복 루프다.
코드로 확인하기
앞의 보안 기준을 코드와 테스트로 확인한다. 함수가 어떤 전제를 세우고, 테스트가 어떤 실패 조건을 고정하는지 함께 읽는다.
운영Slither 기본 실행과 SARIF 출력
SARIF 형식으로 출력하면 GitHub Code Scanning이나 다른 SAST 대시보드에 그대로 올릴 수 있다. waive는 in-source NatSpec 또는 별도 yaml.
# 단발 실행slither . --filter-paths "node_modules|lib"# CI용 SARIF 출력 + machine-readableslither . --sarif slither.sarif --filter-paths "node_modules|lib"# detector를 production 기준에 맞게 좁힌다slither . \ --detect reentrancy-eth,reentrancy-no-eth,arbitrary-send,uninitialized-state,unchecked-lowlevel \ --exclude-informational \ --fail-high \ --fail-medium운영Slither .slither-config.json — waiver 관리
검토를 마친 finding은 reason과 owner를 붙여 waive한다. waiver는 임의로 두지 말고 만료일 코멘트와 함께 PR 리뷰 대상.
{ "filter_paths": "node_modules|lib|test", "exclude_dependencies": true, "exclude_informational": true, "fail_high": true, "fail_medium": true}// slither-disable-next-line reentrancy-events// Reason: emit is intentional after CEI; reviewed 2026-05-15 by sec-team// Expires: 2026-08-15 — reassess on next auditemit PaymentRefunded(paymentId);컨트랙트Echidna property — refund 멱등성
property는 contract 안에
echidna_접두사 함수로 작성한다. invariant 후보를 그대로 옮긴다.
contract EchidnaPaymentProperties { Checkout internal target; bytes32 internal probeId = keccak256("probe"); constructor() { target = new Checkout(); target.createInvoice(probeId, address(0xA11CE), address(0xB0B), token, 100); } // 같은 invoice를 두 번 refund 할 수 없다 function echidna_payment_refunded_only_once() public view returns (bool) { (, , , , Checkout.Status status) = target.invoices(probeId); // Refunded 까지 도달했다면 그 이후 어떤 호출에서도 동일 status 가 유지되어야 한다 return status != Checkout.Status.Refunded || target.refundCount(probeId) <= 1; }}운영Echidna 실행 + GitHub Actions에서 release gate
property 실패는 PR을 자동 차단한다. nightly에서는 더 긴 캠페인을 돌린다.
# .github/workflows/security.ymlname: securityon: [pull_request]jobs: static-and-property: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: foundry-rs/foundry-toolchain@v1 - name: Slither run: | pip install slither-analyzer slither . --sarif slither.sarif - name: Echidna uses: crytic/echidna-action@v2 with: files: src/Checkout.sol contract: EchidnaPaymentProperties test-limit: 20000 corpus-dir: corpus - uses: actions/upload-artifact@v4 if: failure() with: name: echidna-corpus path: corpus강의 포인트
| 관점 | 강의 중 확인할 질문 | 학습 후 남길 증거 |
|---|---|---|
| 도구 역할 | Slither/Echidna/Foundry/manual review가 각각 무엇을 맡는가? | 도구별 역할 분리표 |
| Triage | finding을 impact와 release risk로 분류했는가? | triage 템플릿 |
| Property | Echidna property가 business invariant를 담고 있는가? | property 목록 |
| CI | 새 finding과 failing property가 release gate를 막는가? | CI 실패 조건 |
실무 예시
운영[CONTRACT] 상황: Slither가 checkout contract의 refund 함수에서 reentrancy pattern을 경고했고, Echidna는 echidna_payment_idempotent()를 실패시켰다. 둘을 따로 보면 다른 경고처럼 보이지만, 실제로는 같은 위험을 가리킬 수 있다.
| 신호 | 해석 | 조치 |
|---|---|---|
| Slither reentrancy finding | 외부 token transfer 전후 상태 전이 위험 | refund CEI 검토 |
| Echidna idempotency failure | 같은 payment가 두 번 refund될 수 있음 | failing sequence를 unit test로 승격 |
| Settlement mismatch dashboard risk | merchant ledger 손상 가능 | release blocker로 분류 |
이 예시는 도구를 함께 봐야 하는 이유다. Static finding은 코드 위치를 알려주고, property failure는 실제 상태 손상을 보여준다.
흔한 오해와 실패 시나리오
| 오해 | 실제로 확인할 것 |
|---|---|
| Slither clean이면 안전하다고 본다. | business logic invariant와 manual review가 필요하다. |
| Echidna property가 통과하면 충분하다고 본다. | property가 약하면 아무것도 증명하지 못한다. |
| false positive는 삭제하면 된다고 본다. | waiver owner, evidence, expiry가 필요하다. |
| CI는 security review를 대체한다고 본다. | CI는 미검토 위험을 막는 gate이고 최종 판단은 triage다. |
실습 과제
- 운영도구별 역할 분리표 만들기: Slither, Echidna, Foundry, manual review가 각각 잘 잡는 것과 못 잡는 것을 stablecoin checkout 예시로 정리한다.
- 운영Slither/Echidna triage 템플릿 작성하기: detector/property, severity, confidence, impact, affected function, classification, owner, action, waiver expiry를 포함한 결과표를 만든다.
- 운영CI 실패 조건 정의하기: 새 high severity Slither finding, failing Echidna property, expired waiver, unreviewed medium finding이 각각 release gate에서 어떻게 처리되는지 정의한다.
완료 기준
- 두 도구의 역할을 설명했다.
- triage 템플릿을 만들었다.
- CI 실패 조건을 정의했다.
- false positive waiver에 owner와 만료일을 붙였다.
근거 자료
- 보안도구 Slither Echidna: 02-보안-테스트/05-보안도구-Slither-Echidna.md
- Slither Static Analyzer: https://github.com/crytic/slither
- Slither Detector Documentation: https://github.com/crytic/slither/wiki/Detector-Documentation
- Echidna Fuzzer: https://github.com/crytic/echidna
- Foundry Book: Invariant Testing: https://book.getfoundry.sh/forge/invariant-testing