유미의 기록들

[최종 프로젝트] Signed URL 적용 본문

대외활동 기록/내일배움캠프

[최종 프로젝트] Signed URL 적용

지유미 2024. 11. 13. 23:19
728x90
반응형

💡 배경

유료로 제공되는 강의 영상이 모든 사용자에게 무제한으로 노출되는 것을 방지하기 위해, 수강 유저에게만 제한적으로 접근권한을 주고자 한다. 기존의 구현 방식은 배포된 CloudFront 도메인과 S3경로만으로도 외부에서 접근 가능 했기 때문에 무단으로 강의 영상이 유출될 위험이 있었다. 따라서 제한된 시간동안만 유효하고 필요한 권한만 제공하는 SignedURL을 적용하려고 한다.

 

 

📝 Signed URL

어떠한 요청을 수행하는 데 필요한 제한된 권한과 시간을 제공하는 URL

 

Signed URL에는 쿼리스트링에 인증정보, 만료 날짜 및 시간 같은 추가 정보가 포함되서 콘텐츠에 대한 액세스를 세부적으로 제어할 수 있다

 

 

🚀 CloudFront에 SignedURL 적용하기

 

서명된 URL과 서명된 쿠키를 사용하여 프라이빗 콘텐츠 제공 - Amazon CloudFront

이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

docs.aws.amazon.com

1. Signed URLs 인증 절차에 사용할 `공개키`와 `개인키` RSA key pair를 만든다.
2.`공개키`를 AWS CloudFront keygroups에 등록하고 해당 CloudFront에 접근제한을 설정하고 keygroups을 연결한다.
3. 애플리케이션에서 `개인키`를 사용하여 인증정보가 쿼리에 담겨있는 Signed URLs을 생성한다.
4. 해당 Signed URLs로 접근하면 url 쿼리에 등록된 개인키가 CloudFront에 등록된 공개키와 복호화를 성공하여 콘텐츠에 접근이 가능하다.

 

CloudFront에서 SignedURLs 를 사용하려면 2가지 방법이 있다

  1. 루트사용자가 aws에서 key pair 생성하는 방식
  2. 키 그룹을 생성하는 방법

AWS에서는 키 그룹을 생성하는 방법을 권장한다

 

1. RSA key pair 생성

RSA 알고리즘을 사용하면 `공개키`와 `개인키` 두 가지 키를 생성한다. 

 

RSA 암호화 알고리즘

공개키 암호 시스템 중 하나이며, 암호화 뿐만 아니라 전자 서명이 가능한 최초의 알고리즘

 

공개키 암호화 방식

데이터를 안전하게 전달하기 위해 사용하는 기술이다. 쉽게 말하면, 데이터를 암호화해서 전달함으로써 중간에 누군가가 가로채도 데이터를 읽을 수 없고 특정한 사람만 데이터를 읽을 수 있도록 하는 방식이다

내가 친구에게 메시지를 보낸다고 가정했을때 

  • 친구에게 전달받은 공개키를 사용해서 메시지를 암호화한다
  • 친구만 자신의 개인키로 메시지를 복호화할 수 있다
  • 누군가가 중간에 메시지를 가로채도, 공개키만으로는 내용을 확인할 수 없음

 

전자 서명

원본 데이터가 자신의 것이라는 의미로 이 메시지를 내가 보냈고, 위조되지 않았음을 증명하기 위해 디지털 데이터에 서명을 하는 것이다

 

 

내가 친구에게 메시지를 보낸다고 가정했을때 

  • 나의 개인키를 사용하여 메시지에 서명을 생성한다 (개인키는 나만 들고 있으므로 나만 생성 가능)
  • 서명이 포함된 메시지를 친구에게 보낸다
  • 친구는 나의 공개키를 사용해서 서명을 검증한다
  • 서명이 유효하면 나로부터 왔음을 확인하고, 중간에 변조되지 않았음을 보장할 수 있음

 

개인키 (Private Key)

- 비밀로 유지해야 하는 키

- 자신이 데이터를 암호화 해독하거나 자신의 데이터에 서명할 때 사용

- 예) 애플리케이션은 자신의 개인키를 사용해 URL에 서명하여 Signed URL을 생성한다.

 

공개키 (Public Key)

- 다른사람과 공유할 수 있는 키

- 다른 사람이 보낸 데이터를 암호화하거나 서명을 검증할 때 사용

예) CloudFront는 애플리케이션의 공개키를 등록해 요청의 서명을 검증한다

 

여기서는 암호화는 하지 않고, 서명 검증을 통해 SignedURL이 올바르게 생성되었고, 변조 되지 않았음을 확인하는 과정을 사용한다

 

이제는 서명 검증을 하기 위한 필요한 Key pair를 생성해보려고 한다.

OpenSSL을 사용하여 RSA 키 페어를 생성하고 private_key.pem이라는 파일에 저장한다

 

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

Minimum system requirements: Windows XP or later 32MB RAM 200MHz CPU 30MB hard drive space Recommended system requirements: Windows XP or later 128MB RAM 500MHz CPU 300MB hard drive space October 22, 2024 - OpenSSL 3.4 is available. Users should currently

slproweb.com

 

*OpenSSL : 암호화와 보안 프로토콜을 구현하는 오픈소스 라이브러리

openssl genrsa -out private_key.pem 2048

 

이렇게 만들어진 파일은 공개키와 개인키를 모두 포함한다. `private_key.pem`이라는 파일에서 `public_key.pem`이라는 파일로 공개키를 추출한다

openssl rsa -pubout -in private_key.pem -out public_key.pem

 

 

2. AWS CloudFront에 공개키 연결

CloudFront 공개키 등록

`CloudFront` > `Public Key` > `Create public key` 에서 공개키를 등록한다

Name을 지정하고, Key 입력하는 곳에 public_key.pem 파일을 넣으면 된다.

 

CloudFront에 Key groups 등록

`CloudFront` > `Key groups` > `Create Key group` 에서 키 그룹을 생성할 수 있다

위에서 만든 공개키를 선택하고 키 그룹을 생성한다

 

CloudFront id에 Key groups 연결

`CloudFront` > `Distributions` > `CloudFront id 선택` > `behavior` > `edit` 으로 이동 후 위에서 만든 키 그룹을 등록한다

 

 

이제 CloudFront에 접근 제한 설정을 했다. 저번에는 CloudFront에 접근했던 URL은 더 이상 접근을 할 수 없게 되고, Signed URLs을 통해서만 접근이 가능하다

 

3. Signed URLs 생성

이제 `Signed URLs` 를 코드상에서 생성해주면 된다. AWS 공식문서를 보면 언어별로 코드를 제공해주고 있다. 나는 SpringBoot를 사용하고 있으므로 Java 코드를 참고하였다.

 

Java를 사용한 URL 서명 생성 - Amazon CloudFront

이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

docs.aws.amazon.com

 

영상을 조회할 때, SignedURL을 생성하는 코드

 

LectureVideoController - getLectureVideo 메소드

 //signedURL 생성
String signedUrl = null;

try {
    signedUrl = cloudFrontService.generateSignedUrl(lectureVideo.getFileName(), 60);
} catch (Exception e) {
    throw new ApiException(ErrorStatus._INVALID_URL_FORMAT); //SignedURL 생성 실패
}

 

CloudFrontService

@Service
@RequiredArgsConstructor
public class CloudFrontService {

    @Value("${cloud.aws.cloudfront.cloudFrontUrl}")
    private String CLOUD_FRONT_URL;

    @Value("${cloud.aws.cloudfront.keyPairId}")
    private String KEY_PAIR_ID;

    @Value("${cloud.aws.cloudfront.privateKeyPath}")
    private String PRIVATE_KEY_PATH;

    private final CloudFrontClient cloudFrontClient;

    public String generateSignedUrl(String resourcePath, long expirationMinutes) throws Exception {
        CloudFrontUtilities cloudFrontUtilities = CloudFrontUtilities.create();
        Instant expirationDate = Instant.now().plus(expirationMinutes, ChronoUnit.MINUTES);
        resourcePath = URLEncoder.encode(resourcePath, StandardCharsets.UTF_8);

        CannedSignerRequest request = CannedSignerRequest.builder()
                .resourceUrl(CLOUD_FRONT_URL+resourcePath)
                .privateKey(new java.io.File(PRIVATE_KEY_PATH).toPath())
                .keyPairId(KEY_PAIR_ID)
                .expirationDate(expirationDate)
                .build();

        SignedUrl signedUrl = cloudFrontUtilities.getSignedUrlWithCannedPolicy(request);

        return signedUrl.url();
    }
 }
728x90
반응형
Comments