AI가 생성한 Python 코드의 취약한 암호화
요약
AI 어시스턴트가 생성한 코드에서 취약한 암호화 패턴이 빈번하게 발견된다는 연구 결과를 분석합니다. GitHub Copilot과 FormAI 데이터셋을 통해 AI 모델이 보안상 취약한 암호화 알고리즘이나 무작위성 실패를 생성하는 경향을 입증합니다.
핵심 포인트
- GitHub Copilot 생성 코드의 약 40%가 CWE Top 25 취약점을 포함함
- FormAI 연구 결과, 생성된 C 프로그램의 51.24%가 형식 검증 시 취약점으로 분류됨
- AI는 학습 데이터의 영향으로 MD5, AES-ECB 등 취약한 암호화 패턴을 선호함
- 정적 분석 피드백을 통해 AI 생성 코드의 취약점을 최대 55.5%까지 해결 가능
BrassCoders는 Bandit의 암호화 규칙을 통해 취약한 암호화 (Weak cryptography)를 결정론적으로 탐지합니다. 이는 AI 어시스턴트가 일정 비율로 이를 생성하기 때문입니다. Pearce 등의 연구에 따르면 GitHub Copilot으로 생성된 프로그램의 약 40%가 CWE Top 25 전반에 걸쳐 취약한 것으로 나타났으며, FormAI 연구에서는 생성된 112,000개의 C 프로그램 중 51.24%가 형식 검증 (Formal verification) 결과 취약한 것으로 분류되었습니다. 취약한 암호화는 모델이 지난 10년간의 튜토리얼로부터 물려받은 패턴으로, MD5나 AES-ECB가 짧고 잘 작동한다는 이유로 이를 선택하는 경향을 보입니다.
위험한 점은 모델이 취약한 기본 요소 (Weak primitive)를 알고 있다는 사실 그 자체가 아닙니다. 취약한 기본 요소가 개발자가 테스트한 입력값에 대해서는 프롬프트(Prompt)를 충족하지만, 프롬프트가 전혀 설명하지 않은 공격자에 대해서만 보안 격차 (Security gap)가 발생한다는 점입니다.
AI가 취약한 암호화를 배포하는 속도
BrassCoders는 세 가지 독립적인 연구를 통해 취약한 암호화를 신뢰도 높은 AI 실패 모드 (Failure mode)로 취급합니다. Pearce 등 (Asleep at the Keyboard?, IEEE S&P 2022)은 MITRE의 CWE Top 25에서 추출한 89가지 시나리오에 대해 GitHub Copilot을 사용하여 1,689개의 프로그램을 생성했으며, 그 중 약 40%가 취약함을 발견했습니다. 특히 CWE-327 암호화 시나리오는 모델이 가장 취약하게 처리한 카테고리 중 하나였습니다.
현장 데이터도 이를 뒷받침합니다. Fu 등 (Security Weaknesses of Copilot-Generated Code in GitHub Projects, ACM TOSEM 2025)은 실제 공개 저장소(Public repositories)에 병합된 Copilot 코드 조각들을 연구했으며, 가장 빈번하게 발생하는 단일 취약점은 불충분하게 무작위적인 값의 사용인 CWE-330임을 발견했습니다. 이는 세션 토큰 (Session tokens), 논스 (Nonces), 키 (Keys)를 약화시키는 무작위성 실패 (Randomness failure)입니다. 동일한 연구에 따르면 정적 분석 (Static-analysis) 경고를 어시스턴트에게 다시 피드백함으로써 문제의 최대 55.5%를 해결할 수 있었다고 보고했습니다.
가장 큰 측정치는 형식 검증 (Formal verification)에서 나옵니다. FormAI 데이터셋 (Tihanyi et al. 2023)은 112,000개의 C 프로그램을 생성하고, 휴리스틱 린터 (Heuristic linter) 대신 ESBMC 경계 모델 검사기 (Bounded model checker)를 사용하여 레이블을 지정했습니다. 그 결과 51.24%가 최소 하나 이상의 취약점을 포함하고 있었으며, 레이블링된 클래스 중에는 암호화 오용 (Crypto misuse)이 포함되어 있었습니다. 레이블링이 형식적 증명 (Formal proof)을 기반으로 하기 때문에, 이 비율은 최소치(floor)라고 볼 수 있습니다.
Bandit이 탐지하는 항목
BrassCoders는 Bandit의 암호화 규칙을 묶어서 관리하며, 실행할 때마다 모든 일치 항목을 동일한 방식으로 보고합니다: MD5 및 SHA1 (B303), 안전하지 않은 암호 (Insecure ciphers) 및 ECB 모드 (B304 및 B305), 취약한 hashlib 알고리즘 (B324), 그리고 보안 문맥에서 사용된 표준 random 모듈 (B311) 등이 해당됩니다. 각 항목은 소스 코드 내의 구조적 패턴이며, 주변 코드에 대한 판단이 아닙니다.
MD5를 사용하는 비밀번호 해셔 (Password hasher)는 다음과 같은 형태의 결과물을 생성합니다:
issues:
- id: bandit-B303-001
severity: high
...
결과 파일 (Findings file)을 읽는 AI 어시스턴트는 소스 코드를 한 줄 읽기 전에 심각도 (Severity), 정확한 라인, 그리고 수정 방향을 파악합니다. 어시스턴트는 탐지된 내용을 확인하고 패치 (Patch)를 작성합니다. 발견 단계는 이미 완료된 상태입니다.
코퍼스(Corpus) 내의 MD5는 실제로 문제가 없습니다
BrassCoders는 공개된 코퍼스의 file_dedupe.py에 있는 MD5 호출을 탐지하지만, 이 탐지는 오탐 (False positive)입니다. 해당 파일은 중복을 감지하기 위해 파일 내용을 해싱하며, 이는 MD5의 속도가 장점이 되고 충돌 취약성 (Collision-weakness)이 중요하지 않은 비보안적 용도이기 때문입니다. 생성된 코드는 다음과 같습니다:
with open(path, "rb") as f:
digest = hashlib.md5(f.read()).hexdigest()
if digest in seen:
...
이것은 바로 문맥을 추론하는 스캐너(context-inferring scanner)가 반대 방향으로 잘못 판단하게 되는 전형적인 사례입니다. 변수명이 digest이거나 파일명이 *_dedupe.py일 때마다 MD5 탐지 결과를 강등(demote)시키는 규칙은, 우연히 해당 이름을 사용한 실제 비밀번호 해시(password hash)까지도 강등시키게 됩니다. BrassCoders는 그러한 트레이드오프(trade-off)를 거부합니다. BrassCoders는 md5 호출을 보고하며, 탐지 결과를 읽는 AI 어시스턴트가 이 사례가 보안에 민감한지 여부를 결정합니다.
그 역할 분담이 핵심입니다. BrassCoders는 패턴 보고자(pattern reporter)이며, YAML을 소비하는 AI는 문맥을 인식하는 분류 계층(context-aware triage layer)입니다. 오탐(false positive)은 기능이지 실패가 아닙니다. 왜냐하면 그 대안은 오탐 횟수를 낮게 유지하기 위해 실제 버그에 대해 침묵하는 스캐너이기 때문입니다.
검토(Review)만으로 취약한 암호화(Weak Crypto)를 놓치는 이유
BrassCoders는 결정론적 검사(deterministic check)를 인간의 검토 단계로 넘깁니다. 암호화 분야에서는 검토만으로는 신뢰할 수 없기 때문이며, 가장 강력한 증거는 통제된 연구 결과에 있습니다. Perry 등(Do Users Write More Insecure Code with AI Assistants?, ACM CCS 2023)은 참가자들에게 메시지를 암호화하고 서명하도록 요청하는 작업을 포함하여 보안에 민감한 과업을 수행하게 했습니다. AI 어시스턴트를 사용한 그룹은 대조군보다 훨씬 덜 안전한 코드를 작성했으며, 자신의 코드가 안전하다고 믿을 가능성도 더 높았습니다.
이러한 신뢰의 격차(confidence gap) 때문에 육안으로 이를 잡아내기가 어렵습니다. 개발자는 어시스턴트가 생성한 취약한 암호화 코드를 더 신뢰하게 되며, 따라서 더 적은 정밀도로 검토를 통과하게 됩니다. 검토 단계 아래에 있는 결정론적 스캐너는 그러한 편향(bias)을 공유하지 않습니다. 스캐너는 그것을 작성한 것이 인간이든, 모델이든, 혹은 동전 던지기 결과이든 상관없이 md5 호출을 플래그(flag)합니다.
해결책
BrassCoders는 각 암호화 탐지 결과와 함께 교정 노트(remediation note)를 발행하며, 해결책은 사용 사례에 따라 명확하게 정의되어 있습니다. 비밀번호 해싱(Password hashing)은 MD5와 SHA1에서 벗어나 느리고 솔트(salt)가 포함된 알고리즘으로 전환합니다:
from passlib.hash import argon2
hashed = argon2.hash(password)
...
대칭 암호화 (Symmetric encryption)는 동일한 평문 블록이 동일한 암호문 블록을 생성하여 구조를 유출하는 ECB 모드에서 벗어나, 인증된 모드 (authenticated mode)로 전환합니다:
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
aesgcm = AESGCM(key)
...
토큰 및 키 생성은 관찰된 출력으로부터 예측 가능한 Mersenne Twister를 사용하는 random 모듈에서 벗어나 secrets 모듈로 전환합니다:
import secrets
token = secrets.token_hex(32)
각 수정 사항은 모델이 보안 조치 노트 (remediation note)를 바탕으로 단 한 번의 실행으로 구현할 수 있는 잘 정의된 대체 방식입니다. 스캐너 (scanner)가 해결 공간을 좁히면, 어시스턴트 (assistant)가 주변 코드에 맞는 구체적인 패치 (patch)를 작성합니다.
pip install brasscoders
brasscoders --offline scan /path/to/your/project
스캐너는 실행할 때마다 해당 패턴을 찾아냅니다. AI 어시스턴트는 문맥을 분류 (triage)합니다. 사용자는 문제가 없는 암호화는 유지하고, 문제가 있는 암호화는 수정하기만 하면 됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기