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
FileStorageService.delete(key) 는 있었지만 부르는 곳이 없었습니다. 첨부 개별 삭제와 건강기록 삭제(cascade 는 행만 지운다) 양쪽에서 파일이 디스크에 남았습니다. 진단서·처방전이 "지웠다" 고 생각한 뒤에도 서버에 남는 상태였습니다.
2. 로그인한 아무나 정적 경로로 남의 첨부를 받을 수 있었다
업로드 루트 전체가 /files/** 로 정적 서빙되고 있었습니다. 보안 설정이 프로필 이미지만 공개로 두고 나머지는 인증을 요구했지만, 인증을 거치는 다운로드 경로는 소유권까지 확인하는 반면 정적 경로는 로그인만 보면 통과시켰습니다. 주소를 알게 된 다른 회원이 남의 진단서를 받아갈 수 있었습니다.
정적 서빙을 profile-images 하위로만 좁혔습니다.
남은 것 — S3 전환은 결정이 필요합니다
지금 상태로도 보안 항목은 대체로 충족합니다(확장자·시그니처·크기·스캔 지점·소유권). S3 가 주는 것은 다른 축입니다.
S3 로 얻는 것
지금 상태의 문제
앱 컨테이너를 교체해도 파일이 유지된다
배포할 때마다 ./uploads 가 컨테이너와 함께 사라진다 (볼륨을 붙이지 않으면)
서명 URL 로 만료가 생긴다
다운로드가 앱을 거치므로 앱 메모리·대역을 쓴다
디스크 용량이 인스턴스에 묶이지 않는다
1GB 프리티어 인스턴스에 쌓인다
첫 줄이 당장 문제입니다. 운영 배포가 시작되면(#94) 첨부가 배포마다 날아갑니다. 지금 docker run 에 볼륨 마운트가 있는지 확인하고, 없으면 S3 전환 전에 볼륨이라도 붙여야 합니다.
어느 쪽으로 갈지 알려주시면 이어가겠습니다.
A. 볼륨 먼저 — docker run -v /opt/carecode/uploads:/app/uploads. 작고 즉시 가능
B. S3 전환 — FileStorageService 구현체 추가 + 서명 URL. 자격증명·버킷 정책 결정 필요
Activity
RosieOh commentedon Aug 4, 2026
부분 완료 — 남은 범위만 유지합니다
#57 에서 완료
FileStorageService추상화 — S3 로 교체 가능한 구조남은 범위 (이 이슈에서 계속)
RosieOh commentedon Aug 10, 2026
부분 완료 상태입니다. 검증은 되어 있고, 스캔과 저장소 분리가 남았습니다.
이미 되어 있는 것 (
LocalFileStorageService):ALLOWED_EXTENSIONS)ALLOWED_CONTENT_TYPES)app.storage.max-file-size-bytes, 기본 10MB)남은 것:
둘 다 코드보다 인프라·비용 결정이 먼저입니다. 그래서 열어 둡니다.
fix: 로그인한 아무나 정적 경로로 남의 건강기록 첨부를 받을 수 있었다
RosieOh commentedon Oct 23, 2026
체크리스트를 코드와 대조했습니다 — 6개 중 3개는 이미 되어 있습니다
LocalFileStorageService.ALLOWED_EXTENSIONS(jpg·jpeg·png·gif·webp·heic·pdf),ALLOWED_CONTENT_TYPES,app.storage.max-file-size-bytes(기본 10MB)FileSignatureValidator+verifySignature(). 확장자와 내용이 다르면 거부FileScanner인터페이스,ClamAvFileScanner(clamd),NoOpFileScanner.app.storage.scan.enabled로 켠다. 켜면 검사기에 닿지 못할 때 업로드를 받지 않는다(503)LocalFileStorageService만 있다. 결정이 필요하다 — 아래HealthRecordAttachmentServiceTest(소유권·교차 접근·404 동형), 이번에 삭제·정적 서빙 추가대조하면서 나온 것 둘 (#152)
1. 첨부를 지워도 저장된 파일이 남았다
FileStorageService.delete(key)는 있었지만 부르는 곳이 없었습니다. 첨부 개별 삭제와 건강기록 삭제(cascade 는 행만 지운다) 양쪽에서 파일이 디스크에 남았습니다. 진단서·처방전이 "지웠다" 고 생각한 뒤에도 서버에 남는 상태였습니다.2. 로그인한 아무나 정적 경로로 남의 첨부를 받을 수 있었다
업로드 루트 전체가
/files/**로 정적 서빙되고 있었습니다. 보안 설정이 프로필 이미지만 공개로 두고 나머지는 인증을 요구했지만, 인증을 거치는 다운로드 경로는 소유권까지 확인하는 반면 정적 경로는 로그인만 보면 통과시켰습니다. 주소를 알게 된 다른 회원이 남의 진단서를 받아갈 수 있었습니다.정적 서빙을
profile-images하위로만 좁혔습니다.남은 것 — S3 전환은 결정이 필요합니다
지금 상태로도 보안 항목은 대체로 충족합니다(확장자·시그니처·크기·스캔 지점·소유권). S3 가 주는 것은 다른 축입니다.
./uploads가 컨테이너와 함께 사라진다 (볼륨을 붙이지 않으면)첫 줄이 당장 문제입니다. 운영 배포가 시작되면(#94) 첨부가 배포마다 날아갑니다. 지금
docker run에 볼륨 마운트가 있는지 확인하고, 없으면 S3 전환 전에 볼륨이라도 붙여야 합니다.어느 쪽으로 갈지 알려주시면 이어가겠습니다.
docker run -v /opt/carecode/uploads:/app/uploads. 작고 즉시 가능FileStorageService구현체 추가 + 서명 URL. 자격증명·버킷 정책 결정 필요fix: 배포마다 업로드된 첨부가 사라졌다
feat: S3 호환 저장소를 더한다 (S3·MinIO 같은 코드) (#155)
RosieOh commentedon Oct 24, 2026
현재 상태 — 6개 중 5개 완료
core/storage/UploadPolicycore/storage/FileSignatureValidatorFileScanner+ClamAvFileScanner/NoOpFileScannerS3FileStorageServiceTest9개 + 기존 업로드 테스트검증은 전부
UploadPolicy로 모았다. 저장소 구현을 새로 추가해도 검사를 건너뛸 수 없다 —LocalFileStorageService와S3FileStorageService가 같은 정책을 거친다.서명 URL 을 넣지 않은 이유
건강기록 첨부는 민감정보다. 서명 URL 은 주소를 가진 사람이면 누구나 만료 전까지 열 수 있고,
그 주소는 로그·히스토리·공유로 새어 나간다. 대신 소유권을 확인하는 애플리케이션 경로를 거친다:
S3FileStorageService.store()가 돌려주는url도 공개 주소가 아니라 저장 키다.정적 서빙은 프로필 이미지(
/files/profile-images/**)로만 좁혀 뒀고, 업로드 루트 전체는 닫혀 있다.이 선택은 대역폭을 앱이 쓴다는 비용이 있다. 첨부가 커지면 서명 URL + 짧은 만료로 바꿀 수 있지만,
그때도 발급 시점에 소유권을 확인해야 한다. 지금 구조가 그 확인을 이미 갖고 있다.
남은 것
독립적인 만료(예: 90일 뒤 콜드 스토리지)는 필요해지면 그때 정한다.
남은 두 항목은 "지금 해야 할 일" 이 아니라 선택지다. 더 할 일이 생기면 그때 범위를 다시 적는다.
RosieOh commentedon Oct 24, 2026
라벨 정정: 가이드상
type:security에는 P2 를 붙이지 않는다(보안 결함은 P0/P1). 이 이슈가 다루던 보안 결함은 해소됐고 남은 두 항목(서명 URL·만료 정책)은 결함이 아니라 선택지이므로type:feature+priority:P2로 둔다.