AI 코딩 보안 2026.05.12 · 조회 8904

Mass Assignment 취약점: AI 코딩 CRUD가 만든 관리자 권한 상승 [2026]

AI 도구가 만든 사용자 API에서 is_admin·role 같은 숨은 필드가 PATCH 한 줄에 그대로 열린다. OWASP API3:2023 BOPLA의 본질과, Django·Express·FastAPI·Next.js별 차단 코드를 정리했다.

S
보안 전문 기업 SENTRIX 레드팀
해킹대회 수상 · 보안 실무 10년 · 실전 진단 경험을 바탕으로 작성합니다.
웹사이트를 운영하고 있다면
보안 헤더·인증서·공개 노출부터 무료로 점검하세요. 결과 링크는 이메일로 받습니다. 로그인 이후 권한·업무 로직은 별도 검증이 필요합니다.

다음 화면에서 이메일과 점검 권한을 확인합니다. 회원가입·카드 없이 무료 요약을 받고, 상세 리포트는 선택 구매합니다.

Mass Assignment는 클라이언트가 보낸 필드를 서버가 그대로 모델에 매핑해, 의도하지 않은 컬럼까지 수정되는 결함이다.
AI 코딩 도구는 짧은 코드를 선호해서 DRF에 fields = "__all__", Express에 Object.assign(user, req.body), FastAPI에 model.dict() 통째 저장을 자주 만든다. is_admin·role·balance 같은 컬럼이 PATCH 요청에 그대로 들린다.
OWASP API Security Top 10 2023의 API3:2023, Broken Object Property Level Authorization(BOPLA)이 정확히 이 결함이며, 2019년판 API6에서 명시된 "Mass Assignment"가 흡수된 항목이다(출처: OWASP API Security Top 10 2023 공식 문서).

한눈에 보는 Mass Assignment

AI가 만든 CRUD에서 왜 자주 나오나요?

AI 도구는 "전체 필드를 한 번에 받는" 짧은 코드를 선호합니다. DRF의 fields = "__all__", Express의 { ...req.body } 패턴이 권장처럼 보여서 검토 없이 운영에 박힙니다.

PATCH 한 줄로 어떻게 관리자가 되나요?

PATCH /api/users/me에 {"is_admin": true}를 추가로 보내면, 서버가 화이트리스트 없이 그대로 저장합니다. 프런트엔드에 노출된 필드만 받는 게 아니라 모든 컬럼이 열려 있기 때문입니다.

IDOR(BOLA)과 뭐가 다른가요?

BOLA는 "남의 객체에 접근"하는 결함이고, Mass Assignment(BOPLA)는 "본인 객체에서 만질 수 없어야 할 속성"을 만지는 결함입니다. 두 개가 동시에 터지면 임의 계정을 관리자로 만들 수 있습니다.

ORM이 알아서 막아 주지 않나요?

막아 주지 않습니다. Django/Express/FastAPI/Prisma 모두 기본 동작은 "받은 필드 그대로 저장"입니다. 차단은 시리얼라이저·Pydantic·Zod 스키마 레벨에서 명시해야 합니다.

실제로 어떻게 터지나, PATCH 한 줄 시연

가장 흔한 패턴은 사용자 프로필 수정 API다. 화면에는 닉네임만 있지만, 백엔드가 화이트리스트 없이 받으면 추가 필드가 그대로 들어간다.

# 정상 요청 (프런트 화면 기준)
PATCH /api/users/me
{"nickname": "alice"}

# 공격 요청 — 한 줄 추가
PATCH /api/users/me
{"nickname": "alice", "is_admin": true, "credit": 999999}
핵심 원리: 서버가 request.body를 그대로 모델에 매핑하면, 화면에 없는 컬럼도 다 열린다. 프런트 폼에 없으니 안전하다는 가정이 가장 흔한 오해다.

AI가 자주 만드는 위험 패턴 5가지

스택AI가 자주 짜는 코드위험 컬럼 예시
Django REST Frameworkclass UserSerializer(ModelSerializer): class Meta: fields = "__all__"is_staff, is_superuser, user_permissions
Express + MongooseUser.findByIdAndUpdate(id, req.body)role, balance, verified
FastAPI + SQLAlchemyfor k,v in data.dict().items(): setattr(user,k,v)is_admin, plan_tier, stripe_customer_id
Next.js Server Actionawait prisma.user.update({data: formData})role, emailVerified, plan
Ruby on Rails@user.update(params[:user]) (strong_params 미사용)admin, points, organization_id

30초 자가 점검, 새고 있는지 확인

운영 중인 API에 다음 3가지를 시도해 본다. 하나라도 응답·DB에 반영되면 새고 있다.

# 1. 본인 계정에 관리자 플래그 시도
curl -X PATCH https://your.app/api/users/me \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"is_admin":true,"role":"admin","is_staff":true}'

# 2. 잔액·플랜 등 비즈니스 컬럼 시도
curl -X PATCH https://your.app/api/users/me \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"credit":99999999,"plan":"enterprise","verified":true}'

# 3. 소유권 이전 시도 (IDOR + Mass Assignment 결합)
curl -X PATCH https://your.app/api/posts/123 \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"author_id":"<<공격자 ID>>"}'
위 3가지는 인증이 필요해 외부 자동 점검으로 잡기 어렵다. 그러나 진입 단계인 .env·관리자 페이지·민감 파일·헤더 17종은 CodeScan 30초 무료 스캔으로 한 번에 잡힌다. 사내 SAST가 없는 팀이라면 우선 외부 노출부터 끊는 게 비용 대비 효과가 가장 크다.

스택별 차단 코드, 복사해서 바로 적용

Django REST Framework

class UserUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        # fields="__all__" 금지. 클라이언트가 만질 수 있는 것만 명시
        fields = ["nickname", "avatar_url", "bio"]
        # read_only_fields 는 보조 방어선
        read_only_fields = ["is_staff", "is_superuser", "date_joined"]

Express + Mongoose

// 화이트리스트 헬퍼
const pick = (obj, keys) => keys.reduce((a,k)=> (k in obj ? {...a,[k]:obj[k]} : a), {});

router.patch("/users/me", auth, async (req, res) => {
  const allowed = pick(req.body, ["nickname", "avatar_url", "bio"]);
  const user = await User.findByIdAndUpdate(req.user.id, allowed, {new:true});
  res.json(user);
});

FastAPI + Pydantic

class UserUpdate(BaseModel):
    nickname: str | None = None
    avatar_url: str | None = None
    bio: str | None = None
    model_config = ConfigDict(extra="forbid")  # 정의 안 된 필드는 400

@app.patch("/users/me")
def update_me(payload: UserUpdate, user=Depends(current_user)):
    for k, v in payload.model_dump(exclude_unset=True).items():
        setattr(user, k, v)
    db.commit()
    return user

Next.js Server Action + Prisma

const schema = z.object({
  nickname: z.string().min(1).max(40),
  bio: z.string().max(500).optional(),
});

export async function updateProfile(formData: FormData) {
  const parsed = schema.parse(Object.fromEntries(formData));
  return prisma.user.update({
    where: { id: session.user.id },
    data: parsed,  // role·emailVerified 는 스키마에 없으니 들어올 수 없음
  });
}

실제 보안 사고 사례

Mass Assignment 는 GitHub·HackerOne·정부 보고서에서 반복되는 결함이다.

  • GitHub 사고 (2012), Rails strong_params 도입 이전, 한 연구자가 PATCH 로 자신의 SSH 공개키를 Rails 코어 organization 에 등록해 임의 코드 푸시가 가능했다(출처: github.blog "Public Key Security Vulnerability and Mitigation").
  • HackerOne 디스클로저, 사용자 프로필 PATCH 에 is_admin·role 필드를 추가해 관리자 권한을 얻은 보고가 매년 수십 건 공개된다(출처: HackerOne Hacktivity, "mass assignment" 태그).
  • OWASP API Security Top 10 2023, API3 BOPLA 를 "공격자가 객체 속성을 변경해 권한 상승하거나 민감 데이터에 접근하는 결함"으로 정의하며, 가장 흔히 발견되는 결함 중 하나로 분류한다(출처: owasp.org/API-Security/editions/2023).

비슷한 결함과 비교

결함OWASP본질대표 증상
Mass Assignment (BOPLA)API3:2023속성 단위 권한 누락PATCH 에 is_admin:true 추가하면 통과
BOLA (IDOR)API1:2023객체 단위 권한 누락GET /users/999 로 남의 정보 열람
Broken Function Level AuthAPI5:2023엔드포인트 단위 권한 누락일반 사용자가 /admin/* 접근
Excessive Data ExposureAPI3:2019응답 과다 노출GET 응답에 비밀번호 해시 포함

발견했다면, 사고 대응 4단계

  1. 로그 역추적, 최근 90일 PATCH/PUT 로그에서 is_admin·role·balance 등 위험 키워드를 검색해 영향 받은 계정 식별.
  2. 권한 원복, 비정상 권한이 부여된 계정의 role·플래그를 일괄 원복.
  3. 화이트리스트 강제, 위 스택별 차단 코드를 즉시 배포. fields="__all__"·req.body spread 를 전체 코드베이스에서 grep 해 제거.
  4. 회귀 테스트 추가, "본인 PATCH 에 is_admin:true 보내면 권한이 안 바뀐다"를 통합 테스트로 박아 두기. AI 가 다시 짜도 막힌다.

오늘 시작할 5가지 액션

  1. grep -rn 'fields\s*=\s*"__all__"\|req\.body)\s*$\|Object\.assign(.*req\.body' src/ 로 위험 패턴 검색
  2. 위 스택별 코드 중 본인 스택 1개를 복사해 화이트리스트 시리얼라이저 작성
  3. 본인 계정에 {"is_admin":true,"role":"admin"} PATCH 직접 시도 → 응답·DB 양쪽 확인
  4. 관련 글: AI 가 만든 JWT 인증의 흔한 실수, Supabase RLS 우회 사례, API 키 유출 30초 점검, 권한 결함은 보통 함께 터진다
  5. 외부 노출은 CodeScan 30초 무료 스캔으로, 인증 후 결함은 사내 회귀 테스트로 이중 방어

CodeScan 이 잡아 주는 것

CodeScan 은 해킹대회 수상·레드팀 운영 경력의 보안 전문 기업 SENTRIX 가 운영하는 보안 점검 SaaS 다.
URL 하나만 넣으면 외부 노출 자산·환경변수·민감 파일·헤더 17종을 자동 점검한다.

  • 무료 스캔: .env·관리자 페이지·민감 파일·헤더 17종 등 외부 노출 1차 점검
  • 정기 스캔: 코드 변경마다 자동 점검 + 이상 시 알람
  • 사내 SAST 연동(엔터프라이즈): fields="__all__"·req.body spread 패턴까지 자동 시그니처화

다음에 해야 할 한 가지

Mass Assignment 는 "화면에 없는 컬럼이 열려 있다"는 단순한 사실에서 시작된다. AI 가 짜 준 CRUD 를 한 번도 검토하지 않았다면, 본인 계정 PATCH 에 is_admin:true 를 추가해 보는 것이 가장 빠른 확인이다.

👉 실무자라면 지금 CodeScan 30초 무료 스캔으로 외부 노출부터 끊고, 본 글의 스택별 코드 1개를 오늘 배포하라.

👉 임원/CISO 라면 SENTRIX 30분 1:1 API 보안 진단 상담으로 BOPLA 포함 OWASP API Top 10 전체를 1회 점검받는 것을 권장한다.

내 사이트도 점검해보세요

URL 입력, 결과 링크는 메일로 받는 무료 점검. 회원가입·카드 불필요.

AI 코딩 보안 체크리스트 받기

AI 코딩 보안 체크리스트(PDF)를 무료로 받아보세요.

코드 자체가 걱정이라면 : 소스코드 보안 분석(SAST)이 ZIP 업로드만으로 취약점 위치와 수정 코드를 찾아드립니다.
이런 글을 계속 받아보려면 뉴스레터 구독 · RSS 피드