binchoo / binchoo-spring-study

스프링 연습하는 프로젝트 백업
0 stars 0 forks source link

aws/dragndrop-s3-upload 설계 #1

Open binchoo opened 2 years ago

binchoo commented 2 years ago

개요

깃허브 이슈 작성 화면에서 이미지 파일을 드래그 & 드롭으로 업로드 하는 사용성을 흉내내 보자.

설계

image

User 사용성

image

Web Browser 관점

image

Web Server 관점

image

Bucket 관점

binchoo commented 2 years ago

과제 식별

User → Browser

Browser → Web Server

연관 이슈

제목 내용
Cookies.get('XSRF-TOKEN') returns undefined 쿠키로 토큰을 내려줄 때, HttpOnly 쿠키라서 읽지 못 하는 현상.

Web Server → S3

(SDK 설정 예)

@Configuration
public class AmazonS3Config {

    @Bean
    AmazonS3 amazonS3() throws IOException {
        AmazonS3ClientBuilder clientBuilder = AmazonS3ClientBuilder.standard();
        clientBuilder.setCredentials(credentialsProvider());
        clientBuilder.setRegion("ap-northeast-1");
        return clientBuilder.build();
    }

    @Bean
    AWSCredentialsProvider credentialsProvider() throws IOException {
        return new PropertiesFileCredentialsProvider(
                new ClassPathResource("aws.properties").getFile().getAbsolutePath());
    }
}

연관 이슈

제목 내용
Authenticating Requests: Using the Authorization Header AWS에 인가된 AWS 요청 보내기
sig-v4-header-based-auth AWS 요청에 도장을 찍기위한 계산 과정

S3

https://awspolicygen.s3.amazonaws.com/policygen.html

image

binchoo commented 2 years ago

생각거리: 서버에서 파일 업로드 책임을 지고 싶지 않을 때

유저에게 파일 업로드 할 S3 주소를 알려주어라.

방법1. S3 Presigned URL을 생성해 내려주자.

클라이언트는 임시 싸인이 붙은 (TTL 있음) S3 자원 주소를 받는다. 이어서 클라이언트는 해당 주소로 PutObject 연산을 수행하면 된다.

단점: 인증 허점이 생긴다. Presigned URL이 다른 사람에게 공유되면 그 자는 스프링 시큐리티의 인증을 받지 않았는데도 파일을 업로드 할 수 있다. 이 단점은 유저 인증과 AWS 리소스 접근 권한 인증이 구분되어 있어서 발생한다. 즉, S3 접근 권한 인증이 유저 인증을 장담하지 않는다. 따라서 스프링 백엔드에서 추가적인 엔터티를 만들어 유저 인증과 S3 버킷 인증을 매핑해 보유하는 방식이 필요하다.

구현 복잡도를 낮추기 위해 서버리스 서비스를 끌어들일 수 있다.

방법2. 스프링 서버를 제거, 서버리스 아키텍처를 구성한다.

  1. 유저는 Cognito User Pools로 인증한다. JWT를 내려받는다.
  2. 유저는 이 토큰과 함께 API 게이트웨이의 엔드포인트를 호출한다.
  3. 대응하는 LambdaCognito Federated Identity Pools에게 토큰을 포워딩하고 AWS 크레덴셜을 얻는다.
  4. 다시 Lambda는 PutObject를 할 타겟 s3 오브젝트 주소를 생성하고 PutObject리퀘스트 명세를 생성한다. 이 명세를 AWS 크레덴셜로 서명하여 Sigv4 싸인을 만든다.
  5. 유저는 s3 오브젝트를 업로드 할 주소와 Sigv4 싸인을 얻는다. 덕분에 PutObject를 수행할 수 있다.

그러나, 방법2은 스프링을 쓰지 않으니 이 레포에서 채용할 수 없다.

방법3. Cognito Federated Identity Pools와 본인 스프링을 연동한다

  1. 유저는 스프링 시큐리티로 인증한다. JWT를 내려받는다.
  2. 유저는 이 토큰과 함께 스프링 MVC의 엔드포인트를 호출한다.
  3. 대응하는 핸들러는 Cognito Federated Identity Pools에게 토큰을 포워딩하고 AWS 크레덴셜을 얻는다.
  4. 서비스는 PutObject 타깃이 될 S3 오브젝트 주소를 생성하고 PutObject 리퀘스트 명세를 작성한다. 이 명세를 AWS 크레덴셜로 서명하여 Sigv4 싸인을 만든다.
  5. 유저는 s3 오브젝트를 업로드 할 주소와 Sigv4 싸인을 얻는다. 유저는 4에서 명세된 것과 동일한 PutObject 리퀘스트만을 수행할 수 있다.

방법 2, 3에서 제3자가 URL과 Sigv4 싸인을 탈취한 상태라도, 사전 명세된 PutObject 요청을 알기 어려워 요청을 모사하기 힘들다.

이보다 더 엄격한 방식으로 특정 IP만 s3에 접근하도록 하는 법이 있다.

방법4. CloudFront Signed URL을 내려주자 CloudFront는 S3 버킷의 앞 단에 세워 연동할 수 있다. CloudFront는 S3와 비슷한 개념의 Signed URL를 제공할 수 있다. 이 URL의 경우 요청 User의 IP까지도 제한을 걸 수 있다.