AI가 생성한 Python API에서의 대량 할당 (Mass Assignment) 문제
요약
AI가 생성한 FastAPI 코드에서 발생할 수 있는 대량 할당(Mass Assignment) 보안 취약점을 분석합니다. AI가 권한 부여 로직을 생략하고 요청 스키마를 데이터베이스 모델에 직접 매핑하면서 발생하는 관리자 권한 상승 위험을 경고합니다.
핵심 포인트
- AI는 프롬프트 충족을 위해 가장 단순한 매핑 방식을 선택하여 보안 결함을 유발함
- Pydantic 모델을 DB 업데이트에 직접 사용할 경우 권한 없는 필드 수정 가능
- OWASP API3:2023(객체 속성 수준 권한 부여 결함) 사례에 해당함
- 필드 화이트리스트를 명시하여 허용된 필드만 업데이트하도록 제어해야 함
당신의 AI 어시스턴트가 방금 작성한 FastAPI 엔드포인트는 UserUpdate 모델을 수락하고 .update(**request.dict())를 호출합니다. 해당 모델의 모든 필드는 데이터베이스 행(row)으로 바로 전달됩니다. 여기에는 is_admin도 포함됩니다.
이것이 바로 대량 할당 (Mass Assignment)이며, AI가 생성한 API가 보안 열쇠를 넘겨주는 가장 조용한 방식 중 하나입니다. 코드는 컴파일되고, 테스트는 통과하며, JSON 에디터를 가진 사용자는 당신이 배포하기 전에 스스로를 관리자(admin)로 승격시킬 수 있습니다.
과도하게 노출되는 가장 단순한 FastAPI 패턴
BrassCoders는 AI가 생성한 Python API에서 이러한 패턴을 반복적으로 발견합니다. 즉, 사용자에게 보여지는 요청 스키마 (request schema)와 데이터베이스 업데이트 맵 (update map) 역할을 동시에 수행하는 Pydantic 모델입니다.
class UserUpdate(BaseModel):
name: str
email: str
...
request.dict().items()를 순회하며 모델의 모든 필드를 User ORM 객체에 기록합니다. is_admin, role, account_tier — 이 모든 것들이 포함됩니다. 엔드포인트는 어떤 호출자가 어떤 필드를 설정할 수 있는지 확인하지 않습니다. 인증된 사용자가 자신을 수정하고 있는지 아니면 다른 사람을 수정하고 있는지도 확인하지 않습니다. 그저 매핑하고 커밋(commit)할 뿐입니다.
이것은 저항이 가장 적은 경로이며, 권한 부여 명세 (authorization spec) 없이 사용자 업데이트 엔드포인트를 연결해 달라는 요청을 받았을 때 AI 어시스턴트가 취하는 경로입니다.
왜 AI 어시스턴트는 이런 방식으로 작성하는가
BrassCoders가 FastAPI 프로젝트에서 이러한 과도한 노출 패턴을 찾아내는 이유는, AI 어시스턴트가 "이 사용자를 업데이트하라"는 요구를 가장 작은 단위의 매핑 — 요청 본문(request body)에서 데이터베이스 행으로의 매핑 — 으로 충족시키며, 권한 부여 (authorization) 문제를 완전히 건너뛰기 때문입니다. 이것이 근본 원인입니다.
FastAPI 업데이트 엔드포인트를 생성하는 AI 어시스턴트의 목표는 단 하나입니다: 프롬프트(prompt)를 충족하는 것. 명시적인 필드 화이트리스트 (allowlisting) 작성은 프롬프트가 지정하지 않은 결정, 즉 "어떤 필드를 호출자가 설정할 수 있고 어떤 필드가 금지되어 있는가?"라는 결정을 요구합니다. 이것은 권한 부여 (authorization) 문제입니다. 권한 부여 문제를 해결하려면 데이터 모델의 권한 계층 구조를 알아야 합니다. AI 어시스턴트는 이를 알지 못합니다. 당신은 알고 있지만, 당신이 말하지 않았다면 어시스턴트는 사용 가능한 가장 단순한 구현으로 그 공백을 채운 것입니다.
Pydantic v2에서는 .dict()가 .model_dump()로 이름이 변경되었지만, 취약점은 두 방식 모두에 존재합니다. 패턴은 request.model_dump()와 같이 약간 다를 뿐, 작동 방식은 동일합니다. 필드 이름은 여전히 데이터베이스로 전달됩니다.
관리자 권한 상승 경로 (The Admin Escalation Path)
BrassCoders는 AI 분류 계층(triage layer)이 이러한 유형의 실패를 식별하는 데 필요한 컨텍스트를 제공하도록 설계되었습니다. OWASP API Security Top 10 2023에서는 이를 API3:2023 — 객체 속성 수준 권한 부여 결함 (Broken Object Property Level Authorization) — 으로 명명하며, 이는 읽기 측 과다 노출(호출자가 봐서는 안 되는 필드를 반환)과 쓰기 측 과다 노출(호출자가 설정해서는 안 되는 필드를 수락)을 모두 포함합니다. 대량 할당 (Mass assignment)은 쓰기 측 사례에 해당합니다.
OWASP API Security 사이트의 API3:2023 항목에 전체 분류 체계가 나와 있습니다. 권한 상승 경로는 짧습니다. 사용자가 다음과 같이 전송합니다:
{
"name": "Alice",
"email": "alice@example.com",
...
엔드포인트에는 필드 수준의 권한 부여 확인(field-level authorization check)이 없습니다. ORM은 모든 속성을 설정합니다. 커밋(commit)은 성공합니다. 이제 Alice는 관리자입니다. 오류가 나타나지 않고, 로그 기록에 권한 상승이 남지 않으며, 검증기(validator)가 페이로드(payload)를 거부하지도 않습니다. 유일한 신호는 is_admin 컬럼이 변경된 데이터베이스 행뿐이며, 이는 직접 확인하지 않는 한 알아차리기 어렵습니다.
FastAPI와 Pydantic은 필드가 올바른 타입인지 검증합니다. 하지만 호출자가 해당 필드를 설정할 권한이 있는지는 검증하지 않습니다. 이 차이가 여기서 매우 중요합니다.
BrassCoders가 탐지할 수 있는 것
BrassCoders에는 .update(**request.dict()) 또는 .update(**request.model_dump())에 특이적으로 반응하는 결정론적 스캐너 규칙(deterministic scanner rule)이 없습니다. 스캐너의 범위는 BrassCoders가 명확히 밝히고 있는 부분입니다. 즉, BrassCoders는 컨텍스트에서 추론한 패턴이 아니라, 12개의 스캐너가 결정론적으로 탐지한 내용만을 보고합니다.
스캐너가 AI 생성 API 코드에서 실제로 탐지하는 내용은 다음과 같습니다: 라우트 핸들러 (route handlers) 내의 하드코딩된 비밀 정보(secrets) 및 API 키, 취약한 JWT 구현 (algorithm='none' 변종 포함), f-string 쿼리 생성으로 인한 SQL 인젝션 (SQL injection), 그리고 POST 엔드포인트의 속도 제한 (rate limiting) 누락입니다. 이러한 항목들은 BrassCoders의 YAML 출력에서 결정론적인 탐지 결과 (deterministic findings)로 나타납니다.
대량 할당 (mass assignment) 패턴은 이와 다릅니다. 이는 코드 수준의 패턴이 아니라 구조적인 권한 부여 (authorization) 문제이며, 정규 표현식 (regex)이나 추상 구문 트리 (AST) 규칙만으로는 오탐 (false-positive) 위험 없이 정확히 짚어낼 수 없습니다. FastAPI 라우트 내의 모든 setattr 루프를 플래그(flag)로 표시하는 단순한 규칙을 적용하면, 상위 단계에서 적절한 권한 확인을 거치는 정당한 동적 필드 업데이트까지 탐지하게 됩니다. 이 둘을 구분하기 위해서는 컨텍스트 (context)가 필요합니다.
그 컨텍스트를 제공하는 것이 바로 AI 분류 (triage) 레이어입니다. Claude Code가 FastAPI 프로젝트에 대한 BrassCoders YAML 보고서를 읽을 때, 모델은 파일 수준의 전체 스캔 결과, 즉 UserUpdate 모델이 어떤 필드를 포함하고 있는지, 엔드포인트가 어디에 위치하는지, 주변에서 Bandit과 Pylint가 무엇을 지적했는지 등을 모두 파악합니다. 이러한 컨텍스트가 있다면 권한 관련 필드 옆에 위치한 .update(**request.dict())를 발견하는 것은 매우 간단한 관찰이며, 이는 스캐너가 아닌 AI 어시스턴트가 분류 과정에서 수행해야 할 작업입니다.
이것이 BrassCoders가 구축된 역할 분담의 핵심입니다. 스캐너는 결정론적인 패턴 보고자입니다. 반면, .brass YAML을 읽는 여러분의 Claude Code 또는 Cursor 세션과 같은 AI 분류 레이어는 탐지된 결과와 실제 권한 모델 (authorization model)을 연결하는 컨텍스트 인식 (context-aware) 레이어입니다.
해결책: 명시적 필드 포함 (Explicit Field Inclusion)
BrassCoders는 모델이 어떤 필드를 노출하는지에 대한 완전한 컨텍스트를 AI 어시스턴트에게 제공하는 YAML 탐지 결과를 생성합니다. 해결 방법은 필드 루프를 명시적인 허용 목록 (allowlist)으로 교체하는 것입니다. 두 가지 깔끔한 접근 방식이 있습니다.
옵션 1: 명시적 필드 업데이트
@router.put("/users/{user_id}")
async def update_user(
user_id: int,
...
옵션 2: 호출자 노출 필드와 관리자 노출 필드를 위한 별도 스키마 (schemas) 사용
class UserUpdatePublic(BaseModel):
name: str
email: str
...
관리자용 변형(admin variant)을 관리자 전용 의존성(dependency) 뒤로 라우팅하세요. 공개용 변형(public variant)에는 권한 필드가 포함되지 않으므로, 이를 대상으로 한 대량 할당 (Mass Assignment) 공격은 권한 상승 경로를 생성하지 않습니다. 업데이트 경로에서의 Pydantic 스키마 (Pydantic schemas in update routes)에 대한 FastAPI 문서는 부분 업데이트를 위한 exclude_unset 패턴을 다루고 있으며, 이 해결책과 함께 읽어볼 가치가 있습니다.
두 방식 모두 AI 어시스턴트가 건너뛴 권한 부여 (Authorization) 문제를 해결하도록 강제합니다. 일반 호출자가 설정할 수 있는 필드는 무엇인가? 어떤 필드가 관리자 권한을 필요로 하는가? 명시적인 필드 목록은 코드상에서 가시화된 정답입니다. 라우트를 읽는 감사자(Auditor)는 이를 즉시 확인할 수 있습니다. 코드 리뷰어는 누락된 필드를 찾아낼 수 있습니다. BrassCoders의 출력을 읽는 AI 분류(triage) 레이어는 허용 목록(allowlist)이 스캔된 데이터 모델과 일치하는지 검증할 수 있습니다.
이 패턴은 매우 흔하여 OWASP는 2023년 API 보안 실패 항목 중 인젝션 (Injection)이나 깨진 인증 (Broken Authentication)보다 앞서 3위로 선정했습니다. 해결 방법은 간단합니다. 단순한 코드와 올바른 코드 사이의 간극은 AI 어시스턴트가 인지하지 못한 채 건너뛴 권한 부여 (Authorization) 문제입니다.
BrassCoders는 macOS, Linux, Windows (WSL2)에서 실행됩니다. 다음 명령어로 설치하세요:
pip install brasscoders
brasscoders scan /path/to/your/api
OSS 코어는 Apache 2.0 라이선스이며 무료입니다. BrassCoders 유료 버전 (BrassCoders Paid)은 개발자당 월 $12에 AI 기반 강화 기능을 추가합니다. 5,000만(50M) 토큰이 포함되며, brasscoders portal을 통해 언제든 취소할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기