상세 컨텐츠

본문 제목

[쀼] S3에 업로드한 사진 : 이미지 핫링크 방지 과정 (2)

🔥Activites/[프로젝트] 진행하며 생각을 했다

by :부셔져버린개발자 2025. 2. 10. 12:52

본문

핫링크가 무엇이고 왜 방지하려고 하는가 

링킹은 다른 호스팅된 서버에서 파일을 다운로드하지 않고 링크하는 행위이다
해당 파일을 링킹하여 사용자의 서버에 호스팅하면서 출처를 제공하지 않는다
 
쀼에 설정이 필요한 이유 
쀼는 추억이 담긴 개인 사진을 업로드할 수 있는 프로젝트이기 때문에 무엇보다 이미지에 대한 보안이 필요하다 

 

어떻게 방지할 수 있는가

 
1. CloudFront Signed URL + 캐시 유효 시간 설정 혹은 CloudFront Signed Cookie 
혹은 CloudFront WAF에서 Referer 헤더 검증 
2. 오리진에서 직접 접근 차단 (CloudFront만 거치도록 제한)

 
3번은 저번 글에서 S3에 CloudFront만 접근할 수 있도록 하였다 
2번은 1번을 진행한다면 개선이 되는 부분이기 때문에 1번을 진행하려 한다 
(브라우저 개발자 도구에서 Referer를 조작할 수 있고, VPN이나 프록시를 사용하면 우회 가능하다 따라서 1번을 사용하기로 했다)


CloudFront Signed Cookie 선택 이유 

CloudFront Signed Cookie 방식으로 방지하기로 하였다 

 
방안1) CloudFront Signed URL 적용 
Signed URL은 만료 시간, 서명 값 등의 요소 때문에 동적으로 생성되지만, 만료 기간 동안에는 동일한 URL을 제공할 수 있다. 즉, 유효 기간 내에서는 클라이언트 입장에서는 정적 URL처럼 동작하게 된다. 

예를 들어, Signed URL을 생성할 때 만료 시간을 1시간으로 설정하면, 그 1시간 동안은 같은 Signed URL을 사용할 수 있으므로, Next.js의 `next/image`가 이를 정적 URL처럼 처리할 수 있다. 다만, 만료 시간이 지나면 URL이 무효화되고, 새로운 Signed URL을 생성해야 하므로 그 시점부터는 URL이 다시 동적으로 변한다.
 
과정 
1. 업로드 : 사용자가 이미지를 업로드하면, 백엔드(Spring Boot)가 S3에 업로드한 후, 파일 경로(또는 키)를 DB에 저장하고
정보를 클라이언트에 반환한다. 

2. 조회 : 클라이언트는 이미지가 필요할 때, 저장된 ID를 이용해 CloudFront의 서명된 URL을 받아온다
 
 
고려해야할점
핫링크를 방지하는 데 효과적이지만, URL을 계속 서버로부터 받아오기 때문에 서버에 추가적인 부하를 줄 수 있는 단점이 있다. 
그렇다고해서 DB에 Signed URL을 저장한 후 조회하는 방식도 갱신 주기 마다 갱신을 하던지 이런 만료 처리를 해야 한다. 
 
 
 
방안2) CloudFront Signed Cookie 사용

클라이언트 로그인 후, Signed Cookie를 받아서 모든 이미지 요청시 자동으로 인증하도록 한다 
CloudFront에서 Signed Cookie가 없으면 차단한다 
 
 
과정
로그인이 성공하면 CloudFront Signed Cookie를 만들어서, CloudFront 를 통해 이미지 접근시 헤더에 쿠키를 포함하여 요청을 보낸다 
 
장점
추가적인 요청이 필요없다 
 
 
공식문서 : Signed URLs vs Signed Cookies
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-choosing-signed-urls-cookies.html

Decide to use signed URLs or signed cookies - Amazon CloudFront

Decide to use signed URLs or signed cookies CloudFront signed URLs and signed cookies provide the same basic functionality: they allow you to control who can access your content. If you want to serve private content through CloudFront and you're trying to

docs.aws.amazon.com

 


CloudFront Signed Cookie 적용 과정

1) AWS SDK for JAVA  v2로 변경 

AmazonS3Client > S3Client
AWS SDK v2는 서명된 쿠키를 생성하는 명시적인 방법을 제공하지 않음
(CloudFront Signed Cookie 설정 과정 참고)

 
1. S3
Maven 저장소
https://mvnrepository.com/artifact/software.amazon.awssdk/s3
 
공식 문서 (예제)
https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/java_s3_code_examples.html

Amazon S3 examples using SDK for Java 2.x - AWS SDK for Java 2.x

Amazon S3 examples using SDK for Java 2.x The following code examples show you how to perform actions and implement common scenarios by using the AWS SDK for Java 2.x with Amazon S3. Basics are code examples that show you how to perform the essential opera

docs.aws.amazon.com

 
 
2. CloudFront
Maven 저장소
https://mvnrepository.com/artifact/software.amazon.awssdk/cloudfront
 
공식문서 (예제 - 연결)
https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/java_cloudfront_code_examples.html

CloudFront examples using SDK for Java 2.x - AWS SDK for Java 2.x

CloudFront examples using SDK for Java 2.x The following code examples show you how to perform actions and implement common scenarios by using the AWS SDK for Java 2.x with CloudFront. Actions are code excerpts from larger programs and must be run in conte

docs.aws.amazon.com

 

2) CloudFront 동작 수정

 
생성된 키 페어는 Public Key와 Private Key로 제공된다.
Private Key는 서버에서 보관하며, Public Key는 CloudFront에 업로드하여 서명을 검증할 때 사용된다. 
 
 
공식문서 :서명된 URL과 서명된 쿠키를 생성할 수 있는 서명자를 지정하세요.
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs

Specify signers that can create signed URLs and signed cookies - Amazon CloudFront

Save the private key for your CloudFront key pair in a secure location, and set permissions on the file so that only the desired administrators can read it. If someone gets your private key, they can generate valid signed URLs and signed cookies and downlo

docs.aws.amazon.com

 
 
1. 퍼블릭 키/개인키 생성하기

키 쌍의 요구사항은 다음과 같다

1) openssl 활용하여 개인키 + 퍼블릭키 생성

# 개인 키 생성 (private.pem)
openssl genrsa -out private_key.pem 2048

# 퍼블릭 키 생성 (public.pem)
openssl rsa -pubout -in private_key.pem -out public_key.pem
더보기

윈도우 OpenSSL 설치하기 

https://chris1108.tistory.com/36

 
 
개인키는 보관하고 퍼블릭키를 이제 CloudFront에 업로드해야 한다 
 
2. CloudFront에 퍼블릭 키 업로드 
CloudFront > 퍼블릭 키 > 생성
 

 
3. 키 그룹에 퍼블릭 키 추가하기 
이 키 그룹은 CloudFront에서 서명된 URL이나 쿠키의 서명을 검증할 때 사용된다. 

 
4. 배포 > 동작편집 : 뷰어 액세스 제한 풀고 키 그룹 추가하기 

 

3) CloudFront Signed Cookie 생성 (CloudFrontService 코드 작성)

공식문서 (예제 - Set Signed Cookies Using a Custom Policy)
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html

Set signed cookies using a custom policy - Amazon CloudFront

IP addresses in IPv6 format, such as 2001:0db8:85a3::8a2e:0370:7334, are not supported.

docs.aws.amazon.com

 
- CloudFront-Signature, CloudFront-Policy, CloudFront-Key-Pair-Id 3가지 쿠키가 필요하다

CloudFront-Signature, CloudFront-Policy, CloudFront-Key-Pair-Id 3가지 쿠키가 필요하다

 
- Secure : Set-Cookie 요청을 보내기 전에 뷰어가 쿠키를 암호화해야 한다. 쿠키 속성이 중간자 공격으로부터 보호되도록 HTTPS 연결을 통해 헤더를 보내는 것이 좋다. 
- HttpOnly : HTTP 또는 HTTPS 요청으로만 쿠키 보내도록 해야 한다. 
 
 
3-1) CloudFront-Signature, CloudFront-Policy, CloudFront-Key-Pair-Id 3가지 쿠키를 생성하는 방법에 대해 알아보자 

(1) CloudFront-Policy 생성
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html#private-content-custom-policy-signature-cookies

Set signed cookies using a custom policy - Amazon CloudFront

IP addresses in IPv6 format, such as 2001:0db8:85a3::8a2e:0370:7334, are not supported.

docs.aws.amazon.com

 
 
(2) CloudFront-Signature 생성
 
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html#private-content-custom-policy-signature-cookies

Set signed cookies using a custom policy - Amazon CloudFront

IP addresses in IPv6 format, such as 2001:0db8:85a3::8a2e:0370:7334, are not supported.

docs.aws.amazon.com

 
(3) CloudFront-Key-Pair-Id 생성
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html

Specify signers that can create signed URLs and signed cookies - Amazon CloudFront

Save the private key for your CloudFront key pair in a secure location, and set permissions on the file so that only the desired administrators can read it. If someone gets your private key, they can generate valid signed URLs and signed cookies and downlo

docs.aws.amazon.com

 
2번 CloudFront 설정에서
publickey를 올려서 key pair를 만들었는데 그 ID이다
알파벳대문자여러개로 구성되어 있다 


Spring Boot 코드 수정 

1. 로그인 후 마지막에 Signed Cookie를 생성하여 응답에 추가할 수 있도록 하였다 

 
2. CloudFrontService를 생성했다 
 
- CloudFront에서 뷰어 액세스 제한을 적용하기 위해 아래와 같은 쿠키를 설정하였다 

  • CloudFront-Policy: 리소스에 대한 접근 조건을 설정하는 서명된 정책.
  • CloudFront-Signature: 서명된 정책을 검증하기 위한 서명 값.
  • CloudFront-Key-Pair-Id: 서명된 정책을 생성하는 데 사용된 키 페어 ID

 
3. Docker-Compose.yml 수정 
 
도커 외부에서 PRIVATE_KEY_PATH에 있는 
docker - compose에서 volumes로 도커 컨테이너와 호스트 간에 마운트하고,
environment로 PRIVATE_KEY_PATH를 설정해 두었다 
해당 위치에 private_key.pem 을 두었다 

volumes:
      - ./디렉토리:/app
environment:
      - PRIVATE_KEY_PATH=/app/private_key.pem  # PEM 파일의 위치
더보기

제대로 마운트가 되었는지 확인해보자 

 
 


확인해보기

직접 접근하는 경우 : 접근 안됨

 
 
쀼에서 접근하는 경우 : 접근됨
접근 되는 줄 알았다 ... 그런데 안된다 (캐시 무효화 한다음에 다시 확인해보자)

 
 

3편에서 계속...

728x90

관련글 더보기