security
CHAPTER 83 / 84
읽기 약 2분
FUNCTION
실전 보안 도구: 웹 취약점 스캐너
핵심 개념
⚠️ 모듈 1~5 통합 최종 프로젝트. URL 입력 → 보안 헤더·SQL/XSS 패턴·디렉터리 탐색 → JSON/HTML 리포트.
본문
도구 아키텍처
secscan scan <URL>
├─ 1. 보안 헤더 점검 (CH.66 모듈)
├─ 2. SQL Injection 패턴 탐지 (CH.890 응용)
├─ 3. XSS 패턴 탐지 (CH.891 응용)
├─ 4. 디렉터리 탐색 (CH.892 응용)
└─ 5. JSON/HTML 리포트 생성통합 스캐너 코드
#!/usr/bin/env python3
# secscan_full.py — 통합 웹 취약점 스캐너
# ⚠️ 본인 소유 또는 허가된 환경에서만 사용하세요.
import argparse
import json
import sys
import time
from dataclasses import dataclass, field, asdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from urllib.parse import urljoin, urlparse
import requests
from bs4 import BeautifulSoup
REQUIRED_HEADERS = {
'Strict-Transport-Security': 'HSTS 필수',
'Content-Security-Policy': 'CSP 필수',
'X-Content-Type-Options': 'MIME sniffing 차단',
'X-Frame-Options': 'Clickjacking 차단',
'Referrer-Policy': 'Referrer 정보 제한',
}
XSS_PAYLOADS = [
'<script>alert(1)</script>',
'"><svg onload=alert(1)>',
"javascript:alert(1)",
]
SQLI_PAYLOADS = [
"' OR '1'='1",
"' UNION SELECT NULL--",
"1' AND SLEEP(3)--",
]
COMMON_DIRS = [
'.git/HEAD', '.env', 'admin/', 'backup.zip',
'wp-admin/', 'phpinfo.php', 'config.php.bak',
]
@dataclass
class Finding:
severity: str # high / medium / low / info
category: str
title: str
detail: str
evidence: str = ''
@dataclass
class ScanReport:
target: str
started_at: str
finished_at: str = ''
findings: list[Finding] = field(default_factory=list)
def check_headers(url: str) -> list[Finding]:
findings = []
try:
r = requests.get(url, timeout=10, allow_redirects=True)
except requests.RequestException as e:
return [Finding('high', 'connection', '연결 실패', str(e))]
for header, desc in REQUIRED_HEADERS.items():
if header not in r.headers:
findings.append(Finding(
severity='medium',
category='headers',
title=f'{header} 누락',
detail=desc,
))
return findings
def check_xss_reflection(url: str, params: dict[str, str]) -> list[Finding]:
findings = []
for payload in XSS_PAYLOADS:
for key in params:
test = {**params, key: payload}
try:
r = requests.get(url, params=test, timeout=8)
except requests.RequestException:
continue
if payload in r.text:
findings.append(Finding(
severity='high',
category='xss',
title=f'반사 XSS 가능 — 파라미터 "{key}"',
detail='페이로드가 응답에 그대로 반사됨',
evidence=payload[:80],
))
break
return findings
def check_sql_errors(url: str, params: dict[str, str]) -> list[Finding]:
sql_errors = ['SQL syntax', 'mysql_fetch', 'pg_query', 'ORA-', 'SQLite/JDBCDriver']
findings = []
for payload in SQLI_PAYLOADS:
for key in params:
test = {**params, key: payload}
try:
t0 = time.monotonic()
r = requests.get(url, params=test, timeout=10)
elapsed = time.monotonic() - t0
except requests.RequestException:
continue
for err in sql_errors:
if err.lower() in r.text.lower():
findings.append(Finding(
severity='high',
category='sqli',
title=f'SQL 에러 노출 — 파라미터 "{key}"',
detail=f'에러 시그니처 "{err}" 응답 본문에 포함',
evidence=payload,
))
break
# Time-based 추정 (SLEEP 페이로드 + 응답 지연)
if 'SLEEP' in payload and elapsed > 2.5:
findings.append(Finding(
severity='high',
category='sqli',
title=f'Time-based SQL 의심 — 파라미터 "{key}"',
detail=f'응답 지연 {elapsed:.1f}s',
evidence=payload,
))
return findings
def check_directory_listing(base_url: str) -> list[Finding]:
findings = []
for path in COMMON_DIRS:
url = urljoin(base_url, path)
try:
r = requests.head(url, timeout=5, allow_redirects=False)
except requests.RequestException:
continue
if r.status_code == 200:
findings.append(Finding(
severity='medium',
category='directory',
title=f'민감 경로 노출 — {path}',
detail=f'HTTP 200 OK at {url}',
evidence=url,
))
return findings
def render_html(report: ScanReport) -> str:
sev_color = {'high': '#ef4444', 'medium': '#f59e0b', 'low': '#3b82f6', 'info': '#6b7280'}
rows = []
for f in report.findings:
rows.append(
f'<tr>'
f'<td style="color:{sev_color.get(f.severity, "#000")}">{f.severity.upper()}</td>'
f'<td>{f.category}</td>'
f'<td>{f.title}</td>'
f'<td>{f.detail}</td>'
f'</tr>'
)
return f"""<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>secscan report</title>
<style>body{{font-family:monospace;max-width:900px;margin:2rem auto;padding:0 1rem}}
table{{border-collapse:collapse;width:100%}}
th,td{{border:1px solid #ddd;padding:8px;text-align:left}}
th{{background:#f4f4f4}}</style>
</head><body>
<h1>secscan 리포트</h1>
<p>대상: {report.target}<br>시작: {report.started_at}<br>종료: {report.finished_at}</p>
<p>발견: <strong>{len(report.findings)}</strong>건</p>
<table><tr><th>SEV</th><th>카테고리</th><th>제목</th><th>설명</th></tr>
{"".join(rows)}
</table></body></html>"""
def main():
parser = argparse.ArgumentParser(description='⚠️ 허가된 환경 전용 보안 스캐너')
parser.add_argument('url', help='대상 URL')
parser.add_argument('--params', default='', help='쿼리 파라미터 key=value,key2=value2')
parser.add_argument('-o', '--output', help='리포트 파일 (.json 또는 .html)')
args = parser.parse_args()
params = dict(p.split('=', 1) for p in args.params.split(',') if '=' in p) if args.params else {}
report = ScanReport(
target=args.url,
started_at=datetime.now(timezone.utc).isoformat(),
)
report.findings.extend(check_headers(args.url))
if params:
report.findings.extend(check_xss_reflection(args.url, params))
report.findings.extend(check_sql_errors(args.url, params))
report.findings.extend(check_directory_listing(args.url))
report.finished_at = datetime.now(timezone.utc).isoformat()
if args.output and args.output.endswith('.html'):
Path(args.output).write_text(render_html(report))
else:
out = json.dumps(asdict(report), indent=2, default=str)
if args.output:
Path(args.output).write_text(out)
else:
print(out)
print(f'[secscan] {len(report.findings)} findings', file=sys.stderr)
if __name__ == '__main__':
main()실행 예시
# 본인 소유 사이트 점검
$ python secscan_full.py https://my-site.local --params "q=test,id=1" -o report.html
[secscan] 7 findings
# JSON 리포트
$ python secscan_full.py https://my-site.local -o report.jsonAI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
이 스캐너 코드를 분석해서 누락된 검사 항목과 오탐 줄이는 방법, 성능 개선안을 실제 PR 단위로 만들어줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
내 사이트 URL을 줄게 (본인 소유). 어떤 페이로드와 파라미터 조합으로 스캔하면 실전 OWASP Top 10 커버할지 가이드해줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내가 점검한 사이트의 스캔 결과 JSON을 줄게. severity별 임팩트 평가 + CVSS 점수 추정 + 우선 수정해야 할 Top 5를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 OSS 보안 스캐너 Top 5(Nuclei/ZAP/Nikto 등)와 이 자작 스캐너의 실전 격차를 솔직히 알려줘.
⭐ 이것만 기억하세요
실전 보안 도구: 웹 취약점 스캐너는 이 3가지만 확실히 잡으세요
1.모듈 1~5의 모든 기법(헤더 점검·XSS/SQLi 패턴·디렉터리 탐색·argparse·dataclass·JSON 리포트)을 통합한 실전 도구
2.severity 4단계(high/medium/low/info)로 발견 사항을 분류 — 우선순위 매기기 가능
3.⚠️ 이 도구는 본인 소유 또는 명시적 허가된 환경에서만 사용 — 무단 스캔은 정보통신망법 위반
공유하기
진행도 83 / 84