You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
이미지 등록 v2(presigned)는 클라이언트가 S3 에 직접 PUT 하므로, v1 의 ProductImage.of(bytes) 서버측 바이트 검증을 우회한다. 현재 confirm/폴링의 업로드 확인은 imageStorage.exists()(HEAD) 로 "올라왔는지"만 보고 내용·크기는 검증하지 않는다. 따라서 image/jpeg 로 위장한 비이미지 바이트나 수 GB 대용량 파일도 pending 등록까지 갈 수 있다(스토리지 비용 증가·잠재 DoS).
PR #658 의 CodeRabbit 리뷰(ImagePresignService.verifyUploaded, S3ImageStorage.presignUpload)에서 지적됐고, "발급 + 전달 보장" 과는 독립된 관심사(업로드 남용 방어)라 별도 이슈로 분리했다.
제안
PendingUpload 에 발급 시점의 기대 content-type·최대 허용 크기를 저장한다.
confirm/폴링의 업로드 확인 단계에서 headObject 응답의 contentLength·contentType 을 검증한다. 현재 exists() 가 이미 HEAD 를 치므로, 그 응답에서 메타데이터를 함께 얻어 상한 초과·미스매치면 거부한다.
상한 초과·비이미지면 ImageUploadException 으로 400 거부하고 raw 는 회수(deleteRawsQuietly) + lifecycle 백업에 맡긴다.
(선택) 이미지 매직바이트 검증 — 바이트 다운로드가 필요해 비용 트레이드오프를 검토한 뒤 결정.
참고
content-type 은 presigned 서명에 박혀 S3 가 헤더 일치를 어느 정도 강제하나, 실제 바이트 내용·크기는 무방비다.
presigned PUT(단일 PUT)은 POST Policy 의 content-length-range 같은 크기 제한을 걸 수 없다 — 크기 강제가 필수면 presigned POST 전환도 대안.
배경
이미지 등록 v2(presigned)는 클라이언트가 S3 에 직접 PUT 하므로, v1 의
ProductImage.of(bytes)서버측 바이트 검증을 우회한다. 현재 confirm/폴링의 업로드 확인은imageStorage.exists()(HEAD) 로 "올라왔는지"만 보고 내용·크기는 검증하지 않는다. 따라서image/jpeg로 위장한 비이미지 바이트나 수 GB 대용량 파일도 pending 등록까지 갈 수 있다(스토리지 비용 증가·잠재 DoS).PR #658 의 CodeRabbit 리뷰(
ImagePresignService.verifyUploaded,S3ImageStorage.presignUpload)에서 지적됐고, "발급 + 전달 보장" 과는 독립된 관심사(업로드 남용 방어)라 별도 이슈로 분리했다.제안
PendingUpload에 발급 시점의 기대 content-type·최대 허용 크기를 저장한다.headObject응답의contentLength·contentType을 검증한다. 현재exists()가 이미 HEAD 를 치므로, 그 응답에서 메타데이터를 함께 얻어 상한 초과·미스매치면 거부한다.ImageUploadException으로 400 거부하고 raw 는 회수(deleteRawsQuietly) + lifecycle 백업에 맡긴다.참고
content-length-range같은 크기 제한을 걸 수 없다 — 크기 강제가 필수면 presigned POST 전환도 대안.