Files
cloud-sharp-docs/설계/pipeline/다운로드 파이프라인.md
2026-03-18 17:53:41 +09:00

10 KiB

개요

이 문서는 인증 사용자공유 링크 사용자 모두를 지원하는 파일 다운로드 시스템의 전체 흐름을 정의한다. 핵심 구조는 다음과 같다:

권한 검증 → 다운로드 가능 상태 사전 검증 → 다운로드 세션 발급 → 최종 경로 검증 및 스트리밍


전체 흐름 (8단계)

!download_flowchart_part1.svg

!download_flowchart_part2.svg

단계별 상세

1단계: 사용자 인증 / 공유 링크 접근

목적: 요청자의 신원을 확인하고 내부 컨텍스트로 정규화한다.

접근 경로 인증 방식 정규화 결과
인증 사용자 Authorization 헤더의 JWT Bearer 토큰 subjectType=user, subjectId={userId}
공유 링크 사용자 쿼리 파라미터 또는 별도 엔드포인트의 share_token subjectType=share_link, subjectId={shareLinkId}

에러 처리:

상황 응답
인증 토큰 유효하지 않음 / 만료 401 Unauthorized
공유 토큰 유효하지 않음 404 Not Found

2단계: 파일 권한 / 공유 상태 검증

목적: DB 조회를 통해 요청자가 해당 파일에 접근할 수 있는지 확인한다.

검증 항목:

접근 유형 검증 내용
인증 사용자 파일 소유자 여부, 공유 대상 여부, 조직/프로젝트/폴더 단위 접근 권한
공유 링크 사용자 링크 활성화 여부, 만료 여부, 비밀번호 검증, 다운로드 허용 여부

핵심 원칙: 파일 존재 여부 노출을 최소화하기 위해 아래 경우는 모두 404 Not Found 로 처리한다.

  • 파일이 실제로 존재하지 않음
  • 요청자에게 접근 권한이 없음
  • 공유 링크가 파일에 매핑되지 않음
  • 비활성화된 공유 링크로 접근 시도

외부 클라이언트 관점에서 "접근 불가"와 "존재하지 않음"을 구분할 수 없다.


3단계: 파일 메타데이터 및 다운로드 가능 상태 검증

목적: 다운로드가 불가능한 파일에 대해 세션을 발급하지 않기 위한 사전 검증이다.

검증 항목:

  • 파일 메타데이터 존재 여부
  • storage_key 존재 여부
  • 삭제 / 손상 / 비활성 / 격리 상태 여부
  • 다운로드 정책상 차단 대상 여부
  • (가능 시) 실제 스토리지 객체 존재 여부

실패 시: 404 Not Found

⚠️ 이 단계는 사전 검증이다. 세션 발급 후 실제 스트리밍 시점까지 파일 상태가 변할 수 있으므로, 최종 검증은 6단계에서 다시 수행한다.


4단계: 다운로드 세션 토큰 발급

목적: 실제 파일 스트리밍에 사용할 세션 토큰을 발급한다.

세션 토큰 속성:

항목
TTL 약 5분
포함 정보 파일 ID, 요청자 식별자(subjectType, subjectId), 세션 ID, 만료 시각
보호 방식 HMAC 또는 JWT 서명, 필요 시 Redis/DB에 메타데이터 저장

세션이 지원하는 요청 유형:

  • 브라우저의 동일 다운로드 재요청
  • Range 기반 이어받기(resume)
  • 네트워크 끊김 후 재시도
  • 일부 브라우저/다운로드 매니저의 병렬 Range 요청

응답 예시:

{
  "downloadUrl": "/files/stream?sessionToken={download_session_token}",
  "expiresAt": "2026-03-17T12:00:00Z"
}

리스크 및 대응:

리스크 대응
TTL이 너무 길면 URL 탈취 악용 가능 TTL 5분 내외로 제한
TTL이 너무 짧으면 대용량 파일 재요청 실패 세션 TTL 내 반복 요청 허용
토큰 유출 파일 ID + 요청자 식별자에 바인딩, 선택적 IP/UA 바인딩

추가 보안:

  • 다운로드 세션은 발급 시점 권한을 기준으로 TTL 동안 유효
  • Access log 마스킹
  • Cache-Control: private, no-store

5단계: 세션 토큰으로 파일 스트림 요청

클라이언트 요청 예시:

GET /files/stream?sessionToken={download_session_token}
Range: bytes=0-1048575

처리 규칙:

상황 처리
세션 토큰 만료 / 위변조 401 Unauthorized
세션이 가리키는 파일과 요청 대상 불일치 404 Not Found
세션 TTL 내 반복 GET / Range / resume 허용
Multi-range 요청 미지원 (정책에 따라 416 또는 전체 응답)

설계 원칙:

세션 자체는 짧게 유지하되, 세션이 살아있는 동안은 브라우저 다운로드 모델을 수용한다.

"1회용 토큰 + 즉시 무효화" 방식을 사용하지 않는 이유:

브라우저는 다운로드 중 동일 URL로 재요청, Range resume, 병렬 요청, 프록시 중복 요청 등을 발생시킬 수 있어 충돌한다.


6단계: storage_key → 실제 파일 경로 resolve 및 최종 검증

목적: 스트림을 열기 직전에 실제 파일 경로를 확정하고 최종 검증을 수행한다.

처리 절차:

  1. 키-경로 맵(DB 또는 설정) 조회 → 절대 경로 획득
  2. realpath() 등으로 경로 정규화
  3. 정규화된 경로가 허용된 루트 디렉터리 내부인지 검증 (예: /data/uploads/)
  4. 실제 파일 존재 여부 확인
  5. 파일 open 가능 여부 확인

보안 요구사항:

  • Path traversal 방어 필수 (../, 심볼릭 링크 우회, 상대 경로 등 차단)
  • 허용 루트 외부로 벗어나면 즉시 실패 처리

에러 처리:

상황 응답 내부 처리
실제 파일 미존재 404 Not Found -
메타데이터 존재하나 스토리지 파일 누락 404 Not Found 경고 로그 / 장애 알림

7단계: 파일 스트림 응답

기본 응답 헤더:

헤더
Content-Type 서버가 결정한 안전한 MIME 타입
Content-Disposition attachment; filename="..."; filename*=UTF-8''...
Accept-Ranges bytes
Content-Length 전체 또는 부분 응답 크기
ETag 파일 버전 식별자
Last-Modified 파일 최종 수정 시각 (선택)

Range 처리:

조건 응답
Range 헤더 없음 200 OK (전체 파일)
Range 헤더 있음 206 Partial Content (부분 응답)
범위 초과 416 Range Not Satisfiable
If-Range 일치 부분 응답
If-Range 불일치 전체 파일 재전송 또는 정책에 따라 처리

MIME 타입 처리 원칙:

  1. 서버 측 MIME 판별 사용 (클라이언트 업로드 값 불신)
  2. 저장된 메타데이터와 확장자는 보조적으로 사용
  3. 판별 불확실 시 application/octet-stream
  4. XSS 가능 타입이라도 Content-Disposition: attachment로 강제

파일명 처리 원칙:

  • 제거/이스케이프 대상: ", \r, \n, \0, /, \
  • 최대 길이 제한 적용
  • 비 ASCII 문자는 filename*에 RFC 5987 방식으로 인코딩
Content-Disposition: attachment; filename="safe_name.ext"; filename*=UTF-8''original%20name.ext

성능 / 운영 보호 정책:

  • 사용자별 동시 다운로드 수 제한
  • 공유 링크별 rate limit
  • 전체 서버 동시 스트림 수 제한
  • 대용량 파일 전송 속도 제한 (필요 시)
  • sendfile / X-Accel-Redirect / X-Sendfile 활용 검토

8단계: 로그 기록 / 카운트 증가

카운트 증가 정책:

  • 공유 링크 다운로드에만 적용
  • download_count세션 단위 전체 파일 다운로드 완료 시점에만 증가
  • 운영 분석을 위해 시도 수와 완료 수를 분리 집계 가능
카운트 항목 증가 시점 용도
download_attempt_count 스트림 요청 시작 시점 시도 횟수 추적
download_completed_count 전체 파일 전송 완료 시점 실제 완료 횟수 추적

분리 집계를 통해 네트워크 중단, 사용자 취소, 실패율 등을 더 정확히 분석할 수 있다.


에러 응답 정리

상황 HTTP 상태 코드
인증 실패 / 인증 토큰 만료 401 Unauthorized
다운로드 세션 토큰 만료 / 위변조 401 Unauthorized
파일 없음 또는 권한 없음 404 Not Found
Range 범위 초과 416 Range Not Satisfiable
정상 전체 응답 200 OK
정상 부분 응답 (Range) 206 Partial Content

보안 체크리스트

# 항목 관련 단계
1 JWT / share token 서명 검증 1단계
2 파일 접근 권한 및 공유 상태 검증 2단계
3 권한 없음과 파일 없음은 외부에 동일하게 404 처리 2단계
4 파일 메타데이터 및 다운로드 가능 상태 사전 검증 3단계
5 다운로드 세션 토큰 단명 발급 (권장 TTL: 약 5분) 4단계
6 세션 토큰은 파일 ID 및 요청자 식별자에 바인딩 4단계
7 세션 TTL 내 GET / Range / resume / 병렬 요청 허용 5단계
8 Path traversal 방어 (realpath + 루트 경로 검증) 6단계
9 실제 스트리밍 직전 파일 존재 및 open 가능 여부 재검증 6단계
10 Content-Disposition: attachment 강제 7단계
11 파일명 sanitize 및 filename* 지원 7단계
12 MIME 타입은 서버 기준으로 안전하게 결정 7단계
13 ETag / If-Range 지원 7단계
14 다운로드 로그 및 감사 로그 기록 8단계
15 동시 다운로드 수 / rate limit / 대역폭 보호 정책 적용 7~8단계

설계 요약

이 설계는 다음 6가지 목표를 동시에 만족한다.

목표 달성 방법
파일 존재 여부 노출 최소화 권한 없음 / 파일 없음 모두 404 통일 처리
권한 검증 강화 인증 사용자·공유 링크 모두 다단계 검증
불필요한 세션 발급 방지 세션 발급 전 다운로드 가능 상태 사전 검증
브라우저 호환성 확보 세션 TTL 내 GET / Range / resume / 병렬 요청 허용
재생 공격 위험 완화 단명 세션 토큰 + 파일·요청자 바인딩
운영 환경 자원 보호 동시 다운로드 제한, rate limit, 대역폭 제어