SAST와 LLM을 결합한 보안 취약점 탐지, OWASP Top 10 자동 검출, 취약점 자동 수정 제안과 CI/CD 보안 게이트 구축을 학습합니다.
SAST(Static Application Security Testing)정적 애플리케이션 보안 테스트는 소스 코드를 실행하지 않고 보안 취약점을 탐지하는 기법입니다. 전통적인 SAST 도구는 규칙 기반으로 동작하며, 높은 오탐율(false positive)이 가장 큰 문제입니다.
| 문제 | 설명 |
|---|---|
| 높은 오탐율 | 실제 위험이 아닌 코드에도 경고 발생 (30-70%) |
| 맥락 무시 | 입력 검증이 다른 레이어에서 수행되는 경우를 인식 못함 |
| 비즈니스 로직 취약점 | 인증/인가 논리 결함은 탐지 불가 |
| 수정 제안 부재 | 문제를 지적하지만 구체적 수정안은 제공하지 못함 |
LLM은 이 한계들을 보완합니다. 코드의 맥락을 이해하여 오탐을 줄이고, 수정 코드까지 생성할 수 있습니다.
OWASP Top 10은 웹 애플리케이션에서 가장 빈번하게 발생하는 보안 위험 10가지입니다. LLM 기반 분석은 이 취약점들을 맥락 인식 방식으로 탐지합니다.
# 취약한 코드: SQL Injection
def get_user(username: str) -> dict | None:
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
return cursor.fetchone()전통 SAST는 문자열 포매팅을 사용한 SQL 쿼리를 탐지합니다. LLM은 여기서 한 단계 더 나아가 수정 코드를 생성하고, 호출하는 모든 코드의 영향까지 분석합니다.
# 수정된 코드: 매개변수화된 쿼리
def get_user(username: str) -> dict | None:
query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))
return cursor.fetchone()// 취약한 코드: Stored XSS
function CommentDisplay({ comment }: { comment: string }) {
return <div dangerouslySetInnerHTML={{ __html: comment }} />;
}// 수정된 코드: 안전한 렌더링
import DOMPurify from "dompurify";
function CommentDisplay({ comment }: { comment: string }) {
const sanitized = DOMPurify.sanitize(comment);
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}from dataclasses import dataclass
from enum import Enum
class VulnerabilityType(Enum):
SQL_INJECTION = "A03:2021-Injection"
XSS = "A03:2021-Injection"
BROKEN_AUTH = "A07:2021-Authentication"
SENSITIVE_DATA = "A02:2021-Cryptographic Failures"
BROKEN_ACCESS = "A01:2021-Broken Access Control"
SSRF = "A10:2021-SSRF"
INSECURE_DESERIALIZATION = "A08:2021-Software Integrity"
class Severity(Enum):
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
INFO = "info"
@dataclass
class Vulnerability:
vuln_type: VulnerabilityType
severity: Severity
file_path: str
line_number: int
description: str
fix_suggestion: str
fix_code: str | None
confidence: float
is_false_positive: bool
SECURITY_ANALYSIS_PROMPT = """보안 전문가로서 다음 코드를 분석하세요.
파일: {filepath}
코드:
---
{code}
---
SAST 도구가 탐지한 잠재적 취약점:
{sast_findings}
호출 컨텍스트:
{call_context}
각 SAST 발견에 대해 다음을 판단하세요:
1. 실제 취약점인지 오탐인지 (이유 포함)
2. 실제 취약점이라면 심각도 (critical/high/medium/low)
3. OWASP 분류
4. 구체적인 수정 코드
5. 확신도 (0.0-1.0)
추가로 SAST가 놓친 취약점이 있다면 보고하세요:
- 비즈니스 로직 취약점
- 인증/인가 결함
- 경쟁 조건(race condition)
- 안전하지 않은 기본 설정"""
class SecurityAnalyzer:
"""SAST + LLM 하이브리드 보안 분석기"""
def __init__(self, sast_scanner, llm_client):
self.sast_scanner = sast_scanner
self.llm_client = llm_client
async def analyze(
self, filepath: str, source: str, call_context: str = ""
) -> list[Vulnerability]:
# 1단계: SAST 스캔
sast_findings = self.sast_scanner.scan(filepath, source)
# 2단계: LLM 컨텍스트 분석
prompt = SECURITY_ANALYSIS_PROMPT.format(
filepath=filepath,
code=source,
sast_findings=self._format_findings(sast_findings),
call_context=call_context,
)
response = await self.llm_client.generate(prompt)
vulnerabilities = self._parse_response(response)
# 3단계: 오탐 필터링
real_vulns = [
v for v in vulnerabilities
if not v.is_false_positive and v.confidence > 0.7
]
return sorted(
real_vulns,
key=lambda v: v.severity.value,
)
def _format_findings(self, findings: list[dict]) -> str:
return "\n".join(
f"- [{f['rule_id']}] {f['message']} (줄 {f['line']})"
for f in findings
)
def _parse_response(self, response: str) -> list[Vulnerability]:
# LLM 응답 파싱 (간략화)
return []LLM이 SAST 결과를 필터링하면 오탐율을 30-70%에서 5-10%로 줄일 수 있습니다. 이는 보안 팀의 업무 부담을 극적으로 감소시킵니다. 다만 보안 분야에서는 미탐(false negative)이 더 위험하므로, LLM이 "안전하다"고 판단한 코드도 주기적으로 사람이 검토해야 합니다.
취약점을 탐지하는 것만으로는 충분하지 않습니다. 수정 코드를 자동으로 생성하고 검증하는 파이프라인이 필요합니다.
@dataclass
class SecurityFix:
vulnerability: Vulnerability
original_code: str
fixed_code: str
explanation: str
breaking_changes: list[str]
test_cases: list[str]
FIX_GENERATION_PROMPT = """다음 보안 취약점을 수정하는 코드를 생성하세요.
취약점: {vuln_description}
심각도: {severity}
OWASP 분류: {owasp_category}
원본 코드:
---
{original_code}
---
규칙:
1. 최소한의 변경으로 취약점을 수정합니다
2. 기존 기능을 유지합니다
3. 새로운 취약점을 도입하지 않습니다
4. 수정 이유를 설명합니다
5. 변경으로 인한 영향(breaking changes)을 나열합니다
6. 수정을 검증하는 테스트 케이스를 제안합니다"""
class SecurityFixer:
"""보안 취약점 자동 수정기"""
def __init__(self, llm_client):
self.llm_client = llm_client
async def generate_fix(
self, vulnerability: Vulnerability, source: str
) -> SecurityFix:
prompt = FIX_GENERATION_PROMPT.format(
vuln_description=vulnerability.description,
severity=vulnerability.severity.value,
owasp_category=vulnerability.vuln_type.value,
original_code=source,
)
response = await self.llm_client.generate(prompt)
parsed = self._parse_fix(response)
# 수정된 코드가 새로운 취약점을 도입하지 않는지 검증
await self._verify_no_new_vulnerabilities(
parsed["fixed_code"],
vulnerability.file_path,
)
return SecurityFix(
vulnerability=vulnerability,
original_code=source,
fixed_code=parsed["fixed_code"],
explanation=parsed["explanation"],
breaking_changes=parsed["breaking_changes"],
test_cases=parsed["test_cases"],
)
async def _verify_no_new_vulnerabilities(
self, fixed_code: str, filepath: str
):
"""수정된 코드에 새로운 취약점이 없는지 검증"""
# SAST 재스캔
new_findings = self.sast_scanner.scan(filepath, fixed_code)
if new_findings:
raise SecurityError(
"수정된 코드에서 새로운 취약점이 발견되었습니다: "
+ str(new_findings)
)
def _parse_fix(self, response: str) -> dict:
return {
"fixed_code": "",
"explanation": "",
"breaking_changes": [],
"test_cases": [],
}PR(Pull Request) 코드 리뷰에 보안 분석을 자동으로 통합할 수 있습니다.
@dataclass
class SecurityReviewComment:
file_path: str
line_number: int
severity: str
comment: str
suggestion: str | None
class SecurityReviewer:
"""PR 보안 자동 리뷰"""
def __init__(self, security_analyzer, github_client):
self.analyzer = security_analyzer
self.github = github_client
async def review_pr(self, pr_number: int) -> list[SecurityReviewComment]:
# PR의 변경된 파일 목록 가져오기
changed_files = await self.github.get_pr_files(pr_number)
comments = []
for file_info in changed_files:
if not self._is_code_file(file_info["filename"]):
continue
# 변경된 코드만 분석
patch_content = file_info.get("patch", "")
added_lines = self._extract_added_lines(patch_content)
if not added_lines:
continue
# 보안 분석 실행
vulns = await self.analyzer.analyze(
filepath=file_info["filename"],
source=added_lines,
)
for vuln in vulns:
comments.append(SecurityReviewComment(
file_path=vuln.file_path,
line_number=vuln.line_number,
severity=vuln.severity.value,
comment=(
f"[{vuln.severity.value.upper()}] "
f"{vuln.description}"
),
suggestion=vuln.fix_code,
))
# GitHub PR에 코멘트 작성
if comments:
await self._post_review(pr_number, comments)
return comments
def _is_code_file(self, filename: str) -> bool:
code_extensions = {
".py", ".ts", ".tsx", ".js", ".jsx",
".java", ".go", ".rs",
}
return any(filename.endswith(ext) for ext in code_extensions)
def _extract_added_lines(self, patch: str) -> str:
lines = []
for line in patch.splitlines():
if line.startswith("+") and not line.startswith("+++"):
lines.append(line[1:])
return "\n".join(lines)
async def _post_review(
self,
pr_number: int,
comments: list[SecurityReviewComment],
):
review_body = self._build_review_summary(comments)
await self.github.create_review(
pr_number=pr_number,
body=review_body,
comments=[
{
"path": c.file_path,
"line": c.line_number,
"body": f"{c.comment}\n\n제안: {c.suggestion}"
if c.suggestion else c.comment,
}
for c in comments
],
)
def _build_review_summary(
self, comments: list[SecurityReviewComment]
) -> str:
critical = sum(1 for c in comments if c.severity == "critical")
high = sum(1 for c in comments if c.severity == "high")
medium = sum(1 for c in comments if c.severity == "medium")
return (
f"보안 분석 결과: "
f"Critical {critical}건, High {high}건, Medium {medium}건"
)Checkmarx와 Snyk는 AI를 통합한 보안 분석 플랫폼입니다. 이들을 CI/CD 파이프라인에 통합하면 코드가 머지되기 전에 보안을 자동으로 검증할 수 있습니다.
# GitHub Actions 보안 게이트 예시
name: Security Gate
on:
pull_request:
branches: [main]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SAST 스캔
uses: checkmarx/ast-github-action@v2
with:
project_name: my-project
cx_tenant: ${{ secrets.CX_TENANT }}
- name: 의존성 취약점 스캔
uses: snyk/actions/node@master
with:
args: --severity-threshold=high
- name: LLM 보안 분석
run: |
python scripts/llm_security_review.py \
--pr-number=${{ github.event.pull_request.number }} \
--threshold=medium
- name: 보안 게이트 판정
run: |
python scripts/security_gate.py \
--block-on=critical,high \
--warn-on=mediumfrom dataclasses import dataclass
@dataclass
class GatePolicy:
block_severities: list[str]
warn_severities: list[str]
max_open_vulns: int
require_fix_plan: bool
class SecurityGate:
"""CI/CD 보안 게이트"""
def __init__(self, policy: GatePolicy):
self.policy = policy
def evaluate(self, vulnerabilities: list[dict]) -> dict:
blocked = [
v for v in vulnerabilities
if v["severity"] in self.policy.block_severities
]
warnings = [
v for v in vulnerabilities
if v["severity"] in self.policy.warn_severities
]
passed = len(blocked) == 0
return {
"passed": passed,
"blocked_count": len(blocked),
"warning_count": len(warnings),
"message": self._build_message(passed, blocked, warnings),
}
def _build_message(
self,
passed: bool,
blocked: list[dict],
warnings: list[dict],
) -> str:
if passed and not warnings:
return "보안 게이트 통과: 취약점이 발견되지 않았습니다."
parts = []
if blocked:
parts.append(
f"차단: {len(blocked)}건의 심각한 취약점이 발견되었습니다."
)
if warnings:
parts.append(
f"경고: {len(warnings)}건의 중간 수준 취약점이 있습니다."
)
return " ".join(parts)보안 게이트는 "차단(block)"과 "경고(warn)"를 구분해야 합니다. Critical/High 취약점은 머지를 차단하고, Medium은 경고만 표시하되 추적합니다. 모든 수준을 차단하면 개발 속도가 지나치게 저하되므로, 조직의 보안 성숙도에 맞게 정책을 조정해야 합니다.
SAST와 LLM의 하이브리드 접근은 전통 보안 분석의 높은 오탐율을 극적으로 줄이면서, SAST가 탐지하지 못하는 비즈니스 로직 취약점까지 커버합니다. LLM은 취약점 탐지를 넘어 수정 코드 생성, PR 보안 리뷰 자동화, 그리고 CI/CD 보안 게이트 구축까지 지원합니다.
핵심은 도구의 조합입니다. SAST가 1차 탐지를 수행하고, LLM이 맥락 분석으로 오탐을 필터링하며, 자동화된 검증 파이프라인이 수정의 안전성을 보장합니다. Checkmarx, Snyk 같은 플랫폼이 AI를 통합하면서 이 파이프라인이 더욱 성숙해지고 있습니다.
8장에서는 코드 분석의 거시적 관점인 아키텍처 분석과 시각화를 다룹니다. 모듈 의존성 분석, 순환 의존성 감지, 레이어 위반 탐지, 마이크로서비스 경계 제안, 그리고 Mermaid/PlantUML을 활용한 아키텍처 다이어그램 자동 생성을 학습합니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
LLM을 활용한 아키텍처 분석, 순환 의존성 감지, 레이어 위반 탐지, 마이크로서비스 경계 제안과 아키텍처 다이어그램 자동 생성을 학습합니다.
LLM을 활용한 언어/프레임워크 마이그레이션 자동화를 학습합니다. Java에서 Kotlin, React Class에서 Hooks로의 전환과 의미 보존 검증 기법을 다룹니다.
LLM 기반 코드 분석을 CI/CD 파이프라인에 통합하는 방법을 학습합니다. PR별 자동 분석, 품질 게이트, 기술 부채 대시보드와 GitHub Actions 구축을 다룹니다.