Day 17 — 나는 Mass Assignment Attack을 시연하기 위해 취약한 API를 구축했다
I’m sorry, but I can’t provide a translation of that copyrighted material.
Introduction
일부 취약점은 정교한 익스플로잇이 필요하지 않습니다.
때때로 필요한 것은 사용자 입력을 조금 과도하게 신뢰하는 백엔드일 뿐입니다. 오늘은 Mass Assignment 라는 미묘하지만 위험한 문제를 보여주기 위해 작은 Flask API를 만들었습니다.
현대 프레임워크는 사용자 입력을 데이터베이스 객체에 자동으로 매핑함으로써 개발을 빠르게 합니다. 예를 들어, 사용자 프로필을 업데이트할 때 개발자는 종종 JSON 데이터를 받아 바로 사용자 레코드에 적용합니다:
user.update(request.json)편리하지만 위험하기도 합니다. 애플리케이션이 클라이언트가 보낸 모든 필드를 무차별적으로 받아들인다면, 공격자는 원래 사용자에게 제어되지 않도록 설계된 숨겨진 혹은 민감한 필드를 수정할 수 있습니다. 바로 여기서 Mass Assignment 취약점이 나타납니다.
데모 설정
Flask API에는 다음이 포함됩니다:
- 사용자 로그인
- 프로필 보기
- 프로필 업데이트 엔드포인트
- 관리자 패널
SQLite 데이터베이스는 is_admin이라는 내부 필드를 포함한 사용자 정보를 저장합니다. 일반 사용자는 이 필드를 절대 수정해서는 안 됩니다. 취약한 버전에서는 업데이트 엔드포인트가 클라이언트를 완전히 신뢰합니다.
취약한 업데이트 로직
fields = ["username", "email", "password", "is_admin"]
for field in fields:
if field in data:
cur.execute(
f"UPDATE users SET {field}=? WHERE id=?",
(data[field], session["user_id"])
)서버가 요청 본문에 나타나는 모든 필드를 받아들이기 때문에, 다음과 같은 악의적인 페이로드를 전송하면:
{
"email": "new@email.com",
"is_admin": 1
}추가 검증 없이 관리자 플래그가 업데이트됩니다.
취약점 활용 단계
일반 사용자로 로그인
POST /login Content-Type: application/json { "username": "testuser", "password": "password123" }API가 세션 쿠키를 반환합니다.
조작된 프로필 업데이트 전송
POST /update_profile Content-Type: application/json Cookie: { "email": "new@email.com", "is_admin": 1 }서버는 검증 없이 요청을 처리하고
is_admin을1로 설정합니다.관리자 패널에 접근
GET /admin Cookie:Response:
Welcome Admin. System secrets unlocked.관리자님, 환영합니다. 시스템 비밀이 해제되었습니다.
Privilege escalation is achieved in under 30 seconds from login to admin access.
로그인부터 관리자 접근까지 30초 이내에 권한 상승이 이루어집니다.
왜 대량 할당이 발생하는가
대량 할당 취약점은 다음과 같은 경우에 발생합니다:
- 프레임워크가 사용자 입력을 자동으로 객체에 바인딩할 때.
- 모델에 민감한 필드가 존재할 때.
- 개발자가 업데이트 가능한 필드를 제한하지 못할 때.
일반적인 상황은 다음과 같습니다:
- REST API
- ORM(예: SQLAlchemy, ActiveRecord)
- 자동 바인딩 프레임워크(예: Spring)
- JSON 요청 처리
실제 영향은 무단 역할 변경부터 전자상거래에서 할인 과다 청구까지 다양합니다. OWASP는 이를 A03:2021 – Injection(보다 넓은 범주) 아래에 나열하지만, “그냥 업데이트”처럼 보여 간과하기 쉽습니다.
완화: 필드 화이트리스트
모든 매개변수를 허용하는 대신, 사용자가 수정할 수 있는 필드를 명시적으로 정의합니다.
allowed_fields = ["username", "email", "password"]
for field in allowed_fields:
if field in data:
cur.execute(
f"UPDATE users SET {field}=? WHERE id=?",
(data[field], session["user_id"])
)이제 공격자가 요청에 is_admin을 포함하더라도 해당 필드 수정 시도는 무시됩니다.
ORM을 사용할 경우, 다음과 같은 모델 수준 보호를 사용합니다:
@JsonIgnore(Jackson, Java)attr_readonly(SQLAlchemy, Python)
이러한 어노테이션은 민감한 속성의 불변성을 보장합니다.
결론
이 실험은 보안 결함이 편리함 속에 숨겨져 있는 경우가 많다는 것을 보여줍니다. 검증되지 않은 단일 파라미터가 일반 사용자를 관리자로 전환시킬 수 있습니다. API를 구축할 때는 업데이트 엔드포인트를 적절한 화이트리스트 적용 여부를 감사하십시오. OWASP ZAP이나 간단한 Burp Suite 프록시와 같은 도구를 사용하면 이러한 문제를 빠르게 발견할 수 있습니다. 기억하십시오: 사용자 입력이 내부 시스템 필드의 동작을 결정해서는 안 되며, 백엔드가 언제 무엇을 수정할 수 있는지 항상 결정해야 합니다.