OPEN HYPER STEP
← 목록으로 (화이트햇 보안)
SECURITY · 66 / 84
security
CHAPTER 66 / 84
읽기 약 2
FUNCTION

보안 헤더 자동 점검 도구 만들기


핵심 개념

지금까지 배운 것을 종합해서 — 웹사이트의 보안 헤더를 자동 점검하는 CLI 도구를 만든다.

본문

종합 프로젝트: secheader CLI

이 챕터에서 만드는 것은 실용 가치 있는 미니 도구입니다.

📋 코드 (8줄)
$ python secheader.py --url https://github.com
✅ Strict-Transport-Security: max-age=31536000
✅ X-Frame-Options: deny
✅ X-Content-Type-Options: nosniff
⚠️  Content-Security-Policy: 너무 약함 (unsafe-inline 포함)
❌ Referrer-Policy: 누락

점수: 3.5/5.0 (Warn)

전체 구현

PYTHON📋 코드 (170줄)
# ⚠️ 이 코드는 허가된 환경에서만 사용하세요.
# secheader.py — 보안 헤더 자동 점검 CLI

import argparse
import json
import sys
import logging
from typing import Optional
import requests

log = logging.getLogger('secheader')

CHECKS = {
    'Strict-Transport-Security': {
        'required': True,
        'recommended': 'max-age=31536000; includeSubDomains',
        'description': 'HTTPS 강제 (HSTS)',
    },
    'Content-Security-Policy': {
        'required': True,
        'recommended': "default-src 'self'",
        'description': 'XSS 방어 (CSP)',
        'warn_if': lambda v: 'unsafe-inline' in v or 'unsafe-eval' in v,
        'warn_msg': 'unsafe-inline/unsafe-eval 포함 — XSS 위험',
    },
    'X-Frame-Options': {
        'required': True,
        'recommended': 'DENY',
        'description': '클릭재킹 방어',
    },
    'X-Content-Type-Options': {
        'required': True,
        'recommended': 'nosniff',
        'description': 'MIME 스니핑 방어',
    },
    'Referrer-Policy': {
        'required': True,
        'recommended': 'strict-origin-when-cross-origin',
        'description': 'Referer 정보 제어',
    },
    'Permissions-Policy': {
        'required': False,
        'recommended': 'geolocation=(), microphone=()',
        'description': '브라우저 API 권한 제어',
    },
}

def check_url(url: str, timeout: float = 5.0) -> dict:
    """단일 URL의 보안 헤더 점검."""
    try:
        r = requests.get(
            url,
            timeout=timeout,
            allow_redirects=True,
            headers={'User-Agent': 'secheader/1.0 (security audit)'},
        )
    except requests.exceptions.RequestException as e:
        return {'url': url, 'error': str(e)}

    results = []
    score = 0.0
    max_score = 0.0

    for header, spec in CHECKS.items():
        weight = 1.0 if spec['required'] else 0.5
        max_score += weight

        value = r.headers.get(header)
        result = {
            'header': header,
            'description': spec['description'],
            'present': value is not None,
            'value': value,
            'status': 'PASS',
            'note': None,
        }

        if value is None:
            result['status'] = 'FAIL' if spec['required'] else 'WARN'
            result['note'] = f"권장: {spec['recommended']}"
        else:
            result['status'] = 'PASS'
            score += weight
            # 약한 설정 경고
            if 'warn_if' in spec and spec['warn_if'](value):
                result['status'] = 'WARN'
                result['note'] = spec.get('warn_msg', '약한 설정')
                score -= weight * 0.5  # 부분 감점

        results.append(result)

    return {
        'url': r.url,
        'final_url': r.url,
        'status_code': r.status_code,
        'score': round(score, 1),
        'max_score': round(max_score, 1),
        'percentage': round(score / max_score * 100, 1),
        'grade': grade(score / max_score),
        'checks': results,
    }


def grade(ratio: float) -> str:
    if ratio >= 0.9:  return 'A (Excellent)'
    if ratio >= 0.75: return 'B (Good)'
    if ratio >= 0.5:  return 'C (Warn)'
    if ratio >= 0.25: return 'D (Poor)'
    return 'F (Critical)'


def print_report(report: dict) -> None:
    """사람이 읽기 쉬운 콘솔 출력."""
    if 'error' in report:
        print(f"❌ {report['url']}: {report['error']}")
        return

    print(f"\n🔍 {report['url']}")
    print(f"   상태 코드: {report['status_code']}")
    print(f"   점수: {report['score']}/{report['max_score']} ({report['percentage']}%) — {report['grade']}")
    print()

    for check in report['checks']:
        icon = {'PASS': '✅', 'WARN': '⚠️ ', 'FAIL': '❌'}[check['status']]
        line = f"  {icon} {check['header']}"
        if check['value']:
            line += f": {check['value'][:60]}"
        if check['note']:
            line += f"\n      → {check['note']}"
        print(line)


def main():
    parser = argparse.ArgumentParser(description='웹사이트 보안 헤더 자동 점검 도구')
    parser.add_argument('--url', '-u', required=True, help='점검할 URL (https://example.com)')
    parser.add_argument('--json', '-j', action='store_true', help='JSON 형식 출력')
    parser.add_argument('--output', '-o', help='결과 저장 파일 경로')
    parser.add_argument('--timeout', '-t', type=float, default=5.0)
    parser.add_argument('--verbose', '-v', action='store_true')
    args = parser.parse_args()

    logging.basicConfig(
        level=logging.DEBUG if args.verbose else logging.WARNING,
        format='%(asctime)s [%(levelname)s] %(message)s',
    )

    report = check_url(args.url, timeout=args.timeout)

    if args.json:
        out = json.dumps(report, indent=2, ensure_ascii=False)
        print(out)
        if args.output:
            with open(args.output, 'w', encoding='utf-8') as f:
                f.write(out)
    else:
        print_report(report)
        if args.output:
            with open(args.output, 'w', encoding='utf-8') as f:
                f.write(json.dumps(report, indent=2, ensure_ascii=False))

    # exit code: 0=PASS, 1=WARN, 2=FAIL
    if 'error' in report or report.get('grade', '').startswith('F'):
        sys.exit(2)
    if 'WARN' in str(report.get('checks', [])) or report.get('grade', '').startswith(('C', 'D')):
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()

사용 예시

BASH📋 코드 (8줄)
# 기본 점검
$ python secheader.py --url https://github.com

# JSON 리포트 저장
$ python secheader.py --url https://example.com --json --output report.json

# CI/CD 파이프라인 통합 (exit code 활용)
$ python secheader.py --url https://my-app.com || echo "보안 헤더 미흡!"

확장 아이디어

배운 내용으로 더 발전시킬 수 있는 방향:

  1. 여러 URL 일괄 점검--urls-file targets.txt
  2. CSV/HTML 리포트 — 경영진 보고용
  3. GitHub Actions 통합 — PR마다 자동 점검
  4. SecurityHeaders.com API 연동 — 비교 점수
  5. OWASP ZAP 연계 — 더 깊은 점검

🎯 다음 트랙: 모듈 3 ~ 7

지금 배운 것은 보안 도구 제작의 기초 12챕터입니다. 후속 모듈에서:

  • 모듈 3: 네트워크 스캐닝 (socket, scapy)
  • 모듈 4: 웹 취약점 자동 탐지 (SQLi, XSS)
  • 모듈 5: 암호학 실습 (cryptography)
  • 모듈 6: 시스템 보안 (paramiko, OS hardening)
  • 모듈 7: 자동화 + CTF 실전

AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내가 만든 secheader 도구의
예외 처리·재사용성·확장성을 코드 리뷰해주고
프로덕션 수준으로 리팩토링해줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

argparse + requests + json 조합으로
다른 보안 도구(포트 스캐너/SSL 점검/JWT 분석기)를
비슷한 구조로 만드는 템플릿을 보여줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 secheader 코드 전체를 분석해서
메모리 사용·네트워크 효율·동시성을
100개 도메인 일괄 점검에 최적화하도록 개선해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 보안 헤더 자동화 도구 시장 —
Mozilla Observatory/SecurityHeaders.com 같은 SaaS와
자체 도구의 트레이드오프를 솔직히 알려줘.

⭐ 이것만 기억하세요
보안 헤더 자동 점검 도구 만들기 이 3가지만 확실히 잡으세요
1.12개 챕터의 모든 기법(requests/BeautifulSoup/argparse/logging/예외처리)을 종합해 실용 가치 있는 보안 도구를 완성했다
2.argparse + JSON 리포트 + exit code로 CI/CD 파이프라인에 통합 가능한 수준의 도구다
3.다음 모듈(3~7)에서 네트워크 스캐닝·웹 취약점·암호학·시스템 보안·CTF로 확장한다


공유하기
진행도 66 / 84