| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- simplejpaRepository
- JDBC
- transactional
- Layered Architecture
- Transaction
- springboot
- JPA
- 실무
- hexagonal architecture
- Spring Data JPA
- Spring
- Hexagonal
- Adapter
- Today
- Total
Ezcho
오디오 업로드 데모 API, 이렇게 뚫린다 — 게이트웨이 개발 전략 본문
AI 모델 서빙을 위한 B2B, B2C 서비스를 개발하고 있었다.
내가 개발한 Gateway는 개별 모델 컨테이너로 Input을 전달하는 역할을 한다. 처음에는 API Key 기반 인증과 접근 제어를 구현하는 데 집중했다.
그런데 클라이언트 요청으로 데모 페이지를 추가 개발하게 되었다.
파일 업로드를 포함한 데모 페이지를 공개하자마자 예상보다 많은 공격 시도가 발생했다. SQL 인젝션, XSS, 파일 업로드 조작 등 다양한 패턴이 로그에 기록되었다.
초기에는 단순히 로그를 추적하다가 DB에 Injection Query 데이터가 일부 쓰이는 정도로 끝났지만, 이를 계기로 단순한 요청 라우팅만으로는 부족하다는 점을 알게 되었다. 업로드 파일과 요청 자체를 검증하고 차단하는 로직을 Gateway에 포함시켜야 한다는 사실을 확인했다.
AI 서비스를 외부에 데모로 공개할 때 흔히 쓰는 방식이 있다.
웹 페이지에 <input type="file">를 두고, 사용자가 오디오 파일을 업로드 → Gateway를 통해 모델 서버로 전달 → 판정 결과 반환.
겉보기엔 단순하고 안전해 보이지만, 실제로는 여러 보안 취약점이 숨어 있다.
오디오 업로드의 공격 벡터
1. 파일명 기반 XSS
"><script>alert(1)</script>.wav
관리 페이지나 로그에서 파일명을 그대로 출력할 경우 브라우저에서
스크립트가 실행될 수 있습니다.
2. 오디오 메타데이터 삽입
MP3 ID3 태그, WAV INFO Chunk 등에 악성 스크립트를 넣을 수 있습니다.
예를 들어 Artist 태그에 <img src=x onerror=alert(1)>를 삽입하고,
관리 페이지에서 그대로 출력하면 Stored XSS가 발생합니다.
3. Content-Type 스니핑
사용자가 실제로는 HTML 파일을 .wav 확장자로 업로드했는데, 서버가
응답을 text/html로 내보내면 브라우저는 이를 오디오가 아닌 HTML/JS로
실행합니다.
4. ZIP 폭탄, 경로 탈출
ZIP 파일 내부에 ../../etc/passwd 같은 파일명을 넣어 두면 압축 해제 시
서버 자원에 접근할 수 있습니다.
또는 작은 ZIP 파일 안에 TB 단위로 풀리는 zip bomb을 넣어 서비스 거부
공격을 유발할 수 있습니다.
5. 디코더 취약점
조작된 오디오 파일을 파싱할 때 사용하는 라이브러리(librosa, ffmpeg,
libsndfile 등)에 알려진 취약점이 있다면, 서버 크래시나 원격 코드
실행(RCE)이 발생할 수 있습니다.
왜 게이트웨이에서 막아야 할까?
모델 컨테이너마다 보안 로직을 추가하면 유지보수가 어렵습니다.
게이트웨이에서 한 번만 필터링과 세척(sanitize)을 하면 모든 모델 서비스가
동시에 보호됩니다.
모델 컨테이너는 오직 추론만 담당하도록 단순화할 수 있습니다.
게이트웨이 보안 전략
- 확장자는 신뢰하지 않는다 → 파일 내용을 검사한다.
- 원본 파일은 그대로 모델에 전달하지 않는다 → ffmpeg로 재인코딩한다.
- 파일명과 메타데이터는 제거한다 → UUID 기반 파일명,
-map_metadata -1. - 민감한 헤더는 전달하지 않는다 → Authorization, Cookie 등은 제거.
- 응답에는 보안 헤더를 강제한다 →
X-Content-Type-Options: nosniff,Content-Disposition: attachment.
여기서 내가 준수하지 않았던 원칙은 2번, 1번이다.
오디오 업로드기능에 대한 개발경험이 부족했던 이유였다.
게이트웨이 코드 예시
업로드 세척 함수
def sanitize_filestorage_to_wav(fs: FileStorage) -> tuple[str, str]:
# 파일을 임시 저장 → 매직넘버 검사 → ffprobe 길이 확인
# ffmpeg로 16kHz mono WAV 재인코딩 (메타데이터 제거)
...
return safe_wav, "input.wav"
게이트웨이 라우트
@gw_bp.route("/<model>/<path:sub>", methods=["GET","POST"])
def proxy(model, sub):
if request.files:
safe_files = {}
for field, fs in request.files.items():
safe_path, safe_name = sanitize_filestorage_to_wav(fs)
safe_files[field] = (safe_name, open(safe_path, "rb"), "audio/wav")
return proxy_request(model=model, sub=sub, files_data=safe_files)
...
응답 헤더 하드닝
SAFE_RESP_HEADERS = {
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "no-referrer",
"Cross-Origin-Resource-Policy": "same-origin",
"Cross-Origin-Opener-Policy": "same-origin",
}
오디오 다운로드나 서빙 시 반드시 부착하여 브라우저가 HTML/JS로 잘못
해석하지 못하게 합니다.
추가 방어선
- 업로드 크기 및 길이 제한 (예: ≤30MB, ≤60초)\
- 공개 데모 트래픽은 JWT/데모 토큰으로 보호\
- 임시 디렉토리는 격리하고
noexec,nodev,nosuid마운트 옵션 적용\ - IP별 업로드 속도 제한으로 서비스 남용 방지
결론
오디오 업로드는 겉보기엔 안전해 보여도, 실제로는 XSS, DoS, RCE 등 다양한
공격 벡터를 제공합니다.
게이트웨이 레벨에서 단일 보안 레이어를 두면 모든 모델 서비스가 보호되고,
유지보수도 단순해집니다.
재인코딩 후 전달하는 방식은 가장 단순하면서도 효과적인 방어 패턴이므로
반드시 고려해야 합니다.
'BE' 카테고리의 다른 글
| [Spring] Hexagonal vs Layerd Architecture (1) (1) | 2025.04.16 |
|---|---|
| [Spring] SpringBoot (1) | 2024.01.02 |