최근 Cloudflare R2 업로드 버킷 구성을 했는데 채팅 썸네일 이미지가 깨졌습니다. 그래서 조사를 해보니 제 구성에 대한 빠른 해결책을 찾았고, 이 게시글을 발견했습니다: Cloudflare R2 Image URL Display Issue: Detailed Explanation and Fix. 어쨌든 다른 S3 업로드 버킷 구성을 살펴보니 이 버그가 Cloudflare에 특화된 문제가 아니라는 것을 알게 되었습니다.
설명
외부 S3 또는 호환되는 오브젝트 스토리지가 업로드로 구성되었을 때, 채팅 썸네일 이미지는 CDN을 우회하고 버킷 URL에서 직접 로드됩니다.
보안 강화된 외부 S3 호환 버킷(예: Cloudflare R2)의 경우, 채팅 썸네일이 깨져서 표시되지 않습니다.
근본적인 문제는 채팅 직렬화기(serializer)가 썸네일에 s3_cdn_url 설정을 적용하는 데 실패한다는 것입니다. 구성된 CDN을 통해 이미지를 라우팅하는 대신, 브라우저 페이로드에 원본 내부 S3 버킷 URL이 그대로 노출되고 있습니다.
재현 방법
이 문제는 Meta 및 S3 업로드 버킷을 사용하는 다른 사이트에서도 재현됩니다:
- 채팅이나 채널에 이미지를 게시합니다.
- 콘솔에서 썸네일 이미지 URL을 검사합니다.
- 이미지를 클릭하여 더 큰 원본 이미지를 열고 URL을 검사합니다.
- 썸네일 URL과 비교합니다.
아래는 Meta 채팅의 예시입니다.
썸네일 URL: 버킷에서 직접
https://cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/meta/optimized/4X/4/7/9/479815360e0e6e0cd9f4ba565891776e84aea532_2_375x500.jpeg
원본 URL: CDN을 통해
https://global.discourse-cdn.com/meta/original/4X/4/7/9/479815360e0e6e0cd9f4ba565891776e84aea532.jpeg
콘솔에서 썸네일의 <img... HTML에는 data-large-src = CloudFront CDN URL, src = AWS 버킷 URL이 포함되어 있습니다.
영향:
- 기본적으로 보안이 강화되어 원본 버킷 엔드포인트에 대한 인증되지 않은 액세스를 차단하는 Cloudflare R2와 같은 S3 호환 스토리지의 경우, 채팅 썸네일 이미지(최적화)가 깨집니다.
- 원본 버킷 엔드포인트에 대한 액세스를 허용하는 AWS 및 기타 S3 호환 오브젝트 스토리지 버킷의 경우, 채팅이 CDN을 완전히 우회하므로 대역폭이 누출됩니다. 이로 인해 모든 채팅 썸네일 트래픽에 대해 직접적인 S3 아웃바운드(equivalent: egress) 수수료가 부과됩니다.
- 인프라 정보 누출: 원본 백엔드 스토리지 URL(내부 버킷 이름 및 때로는 계정 ID 포함)이 클라이언트 JSON 페이로드에 노출되고 있습니다.
PR:
이 문제를 수정하기 위한 PR을 여기에 올렸습니다:
Sam이 채팅 작성기(composer) 미리보기에 getURLWithCDN을 추가한 것으로 보이지만, 채팅 스트림까지 반영되지 않는 것 같습니다.
작성기 수정이 프로토콜 불일치(// 대 https://)로 인해 getURLWithCDN이 충돌하는 일부 S3 구성에서도 실패했을 수 있다고 생각합니다. 어쨌든 위의 PR은 Sam의 작업을 확장하여 스트림에 래퍼를 추가하고 프로토콜 비의존적(agnostic)으로 만드는 것입니다.
임시 우회 방법:
이것이 Cloudflare만의 문제가 아니라 더 큰 문제임을 깨닫기 전에, 가벼운 테마 컴포넌트를 만들었습니다. 이는 브라우저가 다운로드를 시도하기 전에 Chat DOM의 원본 S3 도메인을 가로채 적절한 CDN 도메인으로 교체합니다. 이를 통해 트래픽이 올바르게 라우팅되고 대역폭 누출을 막을 수 있습니다. 이를 S3 호환 오브젝트 스토리지 모두에서 작동하도록 조정했습니다. 설정은 두 가지뿐입니다 - 원본 S3 버킷 URL과 S3 CDN URL.
(여기서 GitHub 원박스가 왜 깨지는지 모르겠습니다) 이제 수정되었습니다.
