클라우드

S3 사전 서명 URL, 서명이 보증하지 않는 것

사전 서명 URL은 만료 전까지 몇 번이든 쓸 수 있고, 만든 쪽의 자격 증명보다 오래 살지 못한다.
누가 보내는지, 무엇이 올라오는지는 서명과 상관없이 서비스가 따로 확인해야 한다.

이 글의 목차서버를 거치지 않고 올리는 이유7
  1. 서버를 거치지 않고 올리는 이유
  2. 서명에 담기는 것
  3. 서명은 만든 쪽의 권한을 빌려 준다
  4. 적어 둔 만료 시간보다 일찍 끝나기도 한다
  5. 만료 전까지는 몇 번이든 쓸 수 있다
  6. 무엇이 올라오는지는 서명이 정하지 않는다
  7. 서버에 남겨 둘 것

프로필 사진 업로드 같은 기능을 처음 만들 때 파일을 S3에 올리는 방법을 찾다 보면 ‘사전 서명 URL’⁠이라는 이름을 자주 만난다.
서버가 이 URL을 만들어 브라우저에 건네면, 브라우저가 그 주소로 S3에 파일을 바로 올리는 방식이다.

만들어 보면 생각보다 간단하다.
그런데 이 주소가 무엇을 허락하고 무엇은 확인하지 않는지 모르고 쓰면 업로드 기능에 빈틈이 생긴다.

서버를 거치지 않고 올리는 이유

가장 단순한 업로드는 브라우저가 파일을 서버로 보내고, 서버가 다시 S3로 보내는 방식이다.
이러면 파일이 클수록 서버의 네트워크 대역폭과 메모리가 그만큼 쓰인다.

AWS의 사전 서명 URL 지침은 클라이언트가 S3와 직접 주고받게 해서 S3의 확장성을 그대로 쓰는 것을 이 방식의 핵심 이유로 든다.
서버는 바이트를 나르지 않고 주소만 만들어 주면 된다.

업로드 방식에 따른 파일 경로 사전 서명 URL을 쓰면 서버는 주소만 만들어 주고, 파일 바이트는 브라우저에서 S3로 바로 간다. 개념 도해 업로드 방식에 따른 파일 경로 사전 서명 URL로 올리는 순서 브라우저 AWS 자격 증명 없음 서버 서명할 자격 증명 보유 S3 버킷 객체 저장 ① 업로드 요청 ② 사전 서명 URL ③ 파일 업로드 · 서버를 거치지 않음 업로드 한 번에 서버가 주고받는 데이터 서버를 거치는 업로드 브라우저 → 서버 → S3 사전 서명 URL 브라우저 → S3 직접 파일 받기 · 파일 크기 S3로 다시 보내기 · 파일 크기 요청과 URL만 · 파일 크기와 무관 서버를 지나는 데이터 양 사전 서명 URL을 쓰면 서버는 주소만 만들어 주고, 파일 바이트는 브라우저에서 S3로 바로 간다. SOSHIN · DEV NOTES
서버를 지나는 것은 ①②의 짧은 요청과 응답뿐이고, 아래 두 막대의 길이 차이가 서버가 덜 나르는 양이다.

문제는 권한이다.
S3는 원래 액세스 키 같은 AWS 자격 증명으로 요청한 사람을 확인한다.
내 서비스의 사용자에게는 그런 자격 증명이 없고, 그렇다고 버킷을 누구나 쓰게 열어 둘 수도 없다.

사전 서명 URL⁠은 버킷 정책을 바꾸지 않고도 특정 객체에 정해진 시간 동안만 접근을 허락한다.
객체는 S3에 저장된 파일 하나이고, 객체 키는 버킷 안에서 그 파일을 가리키는 이름이다.

서명에 담기는 것

이 URL을 만들 때는 서명할 자격 증명과 함께 버킷, 객체 키, HTTP 메서드, 만료 시간을 정한다.
HTTP 메서드는 요청의 종류이고, 업로드라면 PUT이다.

서명은 이 요청 값들을 모두 묶어 계산한 값이고, 요청에서 비밀인 것은 그 서명 하나뿐이다.
값을 하나라도 바꾸면 서명이 맞지 않아 S3가 거절한다.

이 목록에는 빠진 것이 있다.
누가 보내는지, 몇 번 쓰이는지, 무엇이 담기는지는 서명에 들어가지 않는다.
이 세 가지는 서비스가 따로 챙겨야 한다.

요청 값 변경에 따른 서명 대조 결과 S3는 받은 요청의 값으로 서명을 다시 계산해 맞춰 보므로, 서명에 넣은 값이 바뀌면 거절하고 보낸 사람과 파일 내용은 따지지 않는다. 개념 도해 요청 값 변경에 따른 서명 대조 결과 서명을 만들고 맞춰 보는 순서 ① 서명 계산 서버 · 자격 증명의 비밀 키로 값을 묶음 ② URL 전달 요청 값과 서명이 함께 실림 ③ 다시 계산해 비교 S3 · 받은 요청의 값으로 서명에 넣는 값 PUT 버킷 객체 키 만료 시간 서명에 없는 값 보낸 사람 파일 내용 받은 요청별 대조 결과 서명 대조 S3 응답 같은 URL · 다른 사람이 보냄 일치 허용 같은 URL · 다른 파일 내용 일치 허용 객체 키를 바꿔 보냄 불일치 거부 PUT 대신 GET으로 보냄 불일치 거부 S3는 받은 요청의 값으로 서명을 다시 계산해 맞춰 보므로, 서명에 넣은 값이 바뀌면 거절하고 보낸 사람과 파일 내용은 따지지 않는다. SOSHIN · DEV NOTES
아래 네 요청은 모두 같은 URL에서 출발했고, 위의 '⁠서명에 넣는 값'⁠을 건드린 두 요청만 거부된다.

서명은 만든 쪽의 권한을 빌려 준다

사전 서명 URL이 쓰는 자격 증명은 그 URL을 만든 주체의 것이다.
받는 쪽은 AWS 자격 증명이 없어도 되고, 만든 쪽이 가진 권한 안에서만 요청할 수 있다.

그리고 서명은 받는 쪽이 누구인지 따지지 않는다.
사전 서명 URL은 가진 사람이면 누구나 쓸 수 있는 토큰(bearer token)⁠이다.
주소를 손에 넣은 사람은 누구든 같은 요청을 보낼 수 있다.

그래서 로그인 확인은 URL을 내주기 전에 서비스가 끝내야 한다.
이 사용자가 정말 이 파일을 올려도 되는지 확인한 다음에만 주소를 만들어 준다.

적어 둔 만료 시간보다 일찍 끝나기도 한다

만료 시간은 만들 때 정한다.
콘솔에서 고르는 범위는 1분에서 12시간이고, 명령줄 도구(CLI)⁠나 언어별 개발 도구(SDK)⁠로는 최대 7일이다.

그런데 서명에 쓴 자격 증명이 임시 자격 증명이면 이야기가 달라진다.
URL은 서명한 자격 증명보다 오래 유효할 수 없어서, 정한 만료 시간과 자격 증명의 만료 중 먼저 오는 쪽에서 끝난다.

서버에 붙이는 IAM 역할이 바로 이런 임시 자격 증명을 쓴다.
IAM 역할은 키를 따로 보관하지 않고 AWS 권한을 빌려 쓰게 해 주는 신원이다.

컨테이너를 실행해 주는 ECS에서는 태스크의 역할 자격 증명이 보통 1시간에서 6시간마다 바뀐다.
태스크 역할이 무엇인지는 태스크 역할과 실행 역할 글에서 다뤘다.

개발할 때와 배포한 뒤의 결과가 달라지는 이유가 여기 있다.
오래 가는 액세스 키로 서명하면 잘 되다가, 역할로 서명하면 적어 둔 시간보다 일찍 끊긴다.

URL에 적힌 시간보다 먼저 끝날 수 있다

URL은 6시간, 발급 자격 증명은 2시간 뒤 만료하는 예시다.

URL의 기한

6시간 뒤

자격 증명의 기한

2시간 뒤

임시 자격 증명으로 만든 사전 서명 URL은 URL 기한과 자격 증명의 기한 중 먼저 끝나는 쪽을 넘길 수 없다.

가상의 기한이며 다른 정책 거부·자격 증명 취소는 없는 예시다. 만료 전에 시작한 전송과 새 요청은 구별한다.

만료는 요청이 들어온 시점에 확인한다.
그래서 만료 직전에 시작한 큰 다운로드는 도중에 만료 시각이 지나도 계속된다.
다만 연결이 끊긴 뒤 다시 시작하면 그때는 거절된다.

만료 전까지는 몇 번이든 쓸 수 있다

한 번 쓰면 사라지는 주소로 생각하기 쉽지만 사전 서명 URL은 만료 시각까지 여러 번 쓸 수 있다.
업로드용 주소라면 같은 키로 다시 올리는 것도 막히지 않는다.

같은 키의 객체가 이미 있으면 S3는 기존 객체를 새로 올린 것으로 바꾼다.
누군가 같은 주소로 다시 올리면 검사를 마친 파일도 다른 내용으로 덮인다.

만료 전 URL 재사용에 따른 객체 교체 한 번 검증한 객체도 URL 만료 전에 같은 키로 다시 올라오면 내용이 바뀔 수 있다. 개념 도해 만료 전 URL 재사용에 따른 객체 교체 URL 사용 경로 ① URL 발급 객체 키 · 만료 시각 ② 첫 업로드 같은 객체 키 만료 전 ③ 같은 URL 재사용 같은 객체 키 ④ 만료 도달 URL 사용 종료 요청 거부 같은 키의 객체 상태 저장 내용 시간 첫 업로드 URL 재사용 URL 만료 객체 없음 첫 업로드 내용 재업로드 내용 · 현재 객체 덮어쓰기 객체 유지 한 번 검증한 객체도 URL 만료 전에 같은 키로 다시 올라오면 내용이 바뀔 수 있다. SOSHIN · DEV NOTES
만료 전까지는 같은 주소를 몇 번이든 다시 쓸 수 있다.

주소가 퍼지는 길도 있다.
서명은 URL의 쿼리 문자열에 실려 다니므로, 클라이언트나 중간의 프록시가 URL을 로그에 남기면 서명도 함께 남는다.
그래서 AWS는 사전 서명 URL을 잠깐 쓰는 접근 자격 증명처럼 다루라고 권한다.

만료 시간을 짧게 잡으면 새어 나간 주소가 쓰일 수 있는 시간도 짧아진다.
AWS는 조건이 맞으면 60초짜리 서명도 현실적으로 쓸 수 있다고 본다.

무엇이 올라오는지는 서명이 정하지 않는다

서명이 기본으로 정하는 것은 어느 키에 어떤 방식으로 요청할 수 있느냐까지다.
파일의 체크섬 값을 요청에 함께 실어 무결성을 확인할 수도 있지만, 그것은 파일이 전송 중에 바뀌지 않았는지를 볼 뿐 내용이 안전한지는 알려 주지 않는다.

브라우저 화면에서 확장자를 검사하고 사진 크기를 줄여도, 누군가는 그 화면을 거치지 않고 같은 주소로 다른 파일을 보낼 수 있다.
화면의 검사는 사용자를 돕는 장치일 뿐이다.

조건을 더 걸 수는 있다.
S3는 POST 방식의 사전 서명도 지원하는데, 이때는 미리 서명한 정책에 맞는 업로드만 받는다.
그래도 파일 내용이 안전한지는 올라온 뒤에 확인해야 한다.

서명이 정하는 것서명이 정하지 않는 것서비스가 따로 할 일
버킷, 키, 요청 방식누가 보내는지로그인 확인 뒤에만 발급
URL의 만료 시각실제로 끝나는 시각서명한 자격 증명의 수명 확인
서명에 넣은 요청 값파일 내용의 안전성올라온 뒤 내용 검사

올라온 뒤의 확인에는 S3 이벤트 알림을 많이 쓴다.
객체가 새로 만들어지면 S3가 메시지 큐인 SQS나 코드를 실행해 주는 Lambda 같은 곳으로 알림을 보낸다.

알림은 보통 몇 초 안에 오지만 1분 넘게 걸릴 때도 있다.
한 번 이상 전달되도록 설계돼서 같은 알림이 두 번 오기도 하므로, 검사 처리는 같은 파일을 두 번 받아도 문제없게 만든다.

서버에 남겨 둘 것

업로드를 S3로 직접 보내면 서버는 가벼워지지만, 서버가 모르는 일이 늘어난다.
파일이 실제로 올라왔는지, 언제 올라왔는지, 무엇이 담겼는지를 서버가 저절로 알지 못한다.

그래서 실무에서는 주소를 발급한 기록을 남긴다.
누구에게 어느 키로 언제까지 유효한 주소를 주었는지 남겨 두면, 이상한 파일이 올라왔을 때 거꾸로 따라갈 수 있다.

키는 업로드마다 새로 만든다.
사용자가 고른 파일 이름을 그대로 키로 쓰면 다른 사람의 파일과 겹치거나 덮어쓸 수 있기 때문이다.

올라온 파일은 먼저 검사용 위치에 두고, 검사를 통과한 것만 서비스가 쓰는 위치로 옮긴다.
같은 버킷에 다시 쓰는 처리라면 그 쓰기가 또 알림을 일으켜, 처리가 자기 자신을 다시 부를 수 있다.

결과를 쓰는 위치에 따른 알림 반복 결과를 알림이 걸린 같은 버킷 경로에 쓰면 그 쓰기가 알림을 다시 일으키므로, 결과는 다른 버킷이나 알림 조건 밖의 경로에 쓴다. 개념 도해 결과를 쓰는 위치에 따른 알림 반복 같은 버킷에 결과를 쓸 때 브라우저 사전 서명 URL ① 업로드 버킷 모든 새 객체에 알림 ② 알림 ③ 검사 처리 알림마다 실행 ④ 같은 버킷에 결과 쓰기 · 새 객체 검사 처리 실행 횟수 1회 2회 3회 … 결과 쓰기가 알림 조건 밖에 놓일 때 버킷 분리 결과는 다른 버킷 검사용 버킷 알림 설정 ② 알림 ③ 검사 처리 알림마다 실행 ④ 쓰기 서비스용 버킷 알림 설정 없음 실행 1회 알림 경로 한정 같은 버킷 · 경로 나눔 들어오는 경로 알림 조건과 일치 ② 알림 ③ 검사 처리 알림마다 실행 ④ 쓰기 서비스용 경로 알림 조건 밖 실행 1회 결과를 알림이 걸린 같은 버킷 경로에 쓰면 그 쓰기가 알림을 다시 일으키므로, 결과는 다른 버킷이나 알림 조건 밖의 경로에 쓴다. SOSHIN · DEV NOTES
결과 쓰기가 알림 조건 안에 놓이면 검사 처리가 다시 불리고, 다른 버킷이나 조건 밖 경로에 놓이면 한 번으로 끝난다.

그래서 버킷을 나누거나, 알림을 들어오는 파일이 놓이는 경로에만 걸어 둔다.

소신을 상징하는 까마귀 목판화 일러스트
작성자소신 · Soshin

웹과 서버를 만들고, 개발하며 배운 것들을 글로 남깁니다.

좌우로 움직여 보세요