[Lambda@Edge] Signed Cookie를 사용한 인증 구현

2021. 4. 8. 09:24DevOps/AWS

 

 

 

 

 

Overview

 

AWS(Amazon Web Service)를 사용해서 동영상을 제공하는 서비스를 개발할 때, 동영상은 정적 파일이므로 S3 Bucket을 사용하여 저장하고, 세계 각지에 있는 사용자들에게 이를 빠르게 제공할 수 있도록 Cloud Front를 사용하는 것이 일반적입니다. 필요하다면 Route53 등의 도메인 네임 서비스를 통해 해당 Cloud Front 주소를 알맞은 도메인과 연결하여 제공합니다. 

 

 

모든 사용자들이 접근할 수 있는 동영상이라면 보통 이 정도 선에서 구현이 가능하지만 온라인 강의 등, 멤버십 등을 통해 제한적인 사용자, 즉 권한이 있는 사용자들에게만 동영상을 제공하고, 권한이 없는 사용자에게는 동영상을 재생할 수 없도록 하기 위해서는 추가적인 보안 처리가 필요합니다. 이번 포스팅에서는 Lambda@Edge를 사용해서 권한 있는 사용자들에게만 동영상을 제공하는 방법에 대해 살펴보려고 합니다.

 

 

 

 

HLS Format

 

HLS란 HTTP Live Format의 약자로, 동영상 스트리밍 재생을 가능하게 해주는 format입니다. HTTP 프로토콜을 사용해서 스트리밍 영상을 구현할 수 있기 때문에 CDN(Content Delivery Network)등의 웹 서비스를 위한 여러 네트워크와 캐시 등을 그대로 사용할 수 있기 때문에 대용량의 비디오를 재생하기 위한 format으로 각광받고 있습니다. 

 

 

파일을 전부 다운로드 받아야 재생이 가능했던 mp4포맷과는 다르게 m3u8 형식의 index파일을 먼저 다운로드하고, 그 인덱스 파일에 기록된 대로 원하는 지점의 파일들의 조각을 받아올 수 있기 때문에 라이브 스트리밍, 대용량 동영상의 재생이 용이하다는 장점이 있습니다.

 

 

 

https://medium.com/@cafielo/understanding-hls-for-ios-developer-cc7788658bb8

 

 

HLS포맷을 통해 동영상을 재생하는 과정은 다음과 같습니다.

 

  1. m3u8 파일을 다운로드한다. (이 파일은 일종의 인덱스 파일이며, 실제로 재생될 동영상의 Segments들에 대한 정보를 모두 기록하고 있습니다. 이 파일을 다운로드하지 않으면 HLS 포맷의 파일을 재생할 수 없습니다.)
  2. m3u8에 적혀진대로, 순차적으로 다음 차례의 동영상 Segments들을 HTTP를 통해 요청하여 재생한다.

 

 

Process

 

한 번의 요청으로 모든 동영상 파일을 다 불러올 수 있는 것이 아니라, 한 번의 요청으로 하나의 동영상 Segments만을 가져오는 것이기 때문에 Cookie를 사용한 인증 방식이 적합합니다. 

 

 

Lambda@Edge는 CloudFront에서 발생한 이벤트를 트리거로 하여 실행되는 함수이기 때문에 첫 m3u8 요청 때, 적절한 인증 과정을 거쳐서 인증정보를 담아 Set Cookie를 해주고, 이후의 동영상 Segment 요청에는 쿠키를 가진 요청만 유효하다고 판단하여 인증된 사용자에게만 동영상 Segment를 보내도록 구현할 수 있습니다.

 

 

전체적인 인증 Process는 다음과 같습니다.

 

  1. 사용자가 CloudFront에 m3u8파일을 요청 (요청 시에 쿼리 파라미터로 JWT 토큰을 보냄)
  2. CloudFront에서 이 파일을 받아오고 사용자에게 전달하기 전에 'Viewer Response' 이벤트 트리거하여 Lambda함수 실행
  3. Lambda함수에서 사용자가 요청한 Request의 쿼리파라미터에서 JWT토큰 추출해서 유효성 검증
  4. 유효한 JWT토큰이라면 리턴할 Response객체에 Set-Cookie 헤더 정보를 추가로 입력해서 리턴
  5. 유효하지 않은 JWT토큰이거나 토큰이 없으면 그냥 리턴
  6. 응답받은 m3u8 파일을 기준으로 동영상 Segment 요청할 때 Set-Cookie를 통해 Cookie가 헤더에 있는 요청이면 유효하다고 판단해서 동영상 리턴
  7. 헤더에 Cookie가 없는 요청이면 403 에러코드 리턴. 동영상 재생 불가

 

 

 

CloudFront에서 발생하는 이벤트는 크게 4가지로 나뉜다.

 

 

Implementation

 

S3 bucket에 대한 CloudFront Distribution 생성

먼저 CloudFrond Distribution을 생성해야 합니다. AWS Cloudfront management console에 들어가서 Create Distribution을 통해 서비스를 제공하기 원하는 S3 Bucket과 Cloud Distribution을 연결해 줍니다.

 

 

 

 

CloudFront Distribution에 Public Key생성 및 CF와 연결

CloudFront Dsitribution이 제대로 생성되었다면, 동영상 Segment를 Cookie가 있는 요청에만 허용할 수 있도록 설정해주어야 합니다. (그전에 S3 Bucket의 퍼블릭 액세스가 차단되어 있는지를 먼저 확인해야 합니다. AWS에서는 S3 Bucket에 대한 액세스를 금지하고 CF를 통해서 콘텐츠에 접근하는 것을 권장하고 있습니다. )

 

 

 

CloudFront의 좌측 메뉴에서 Public Key를 생성하고, Key Group에 추가합니다. Public Key는 openssl을 사용하여 생성하면 됩니다. (private_key는 안전한 곳에 보관하세요). Public Key를 생성하는 이유는 인증된 사용자에게 Set-Cookie를 할 때, 이 Public Key로 서명된 값을 Set 해주기 위함입니다.

 

 

 

 

 

 

CloudFront Distribution에 Behaviors 설정

다음은 이렇게 설정된 Key Group을 가지고 CloudFront에 보안 설정을 해주어야 합니다. CloudFront의 Behavior탭에 들어가면 다음과 같이 Behavior를 설정할 수 있는 화면이 나옵니다. Default 패턴의 Behavior는 쿠키를 통해서만 접근할 수 있도록 설정할 것이고, /videos/*/hls/master.m3u8패턴은 쿠키를 통하지 않고 퍼블릭 접근이 가능하도록 설정할 것입니다.(여기서 Lambda@edge를 통과시켜 Set-Cookie를 해주어야 하기 때문)

 

 

 

 

Edit을 누르면 Behavior를 수정할 수 있는데, 여기에 "Restrict Viewer Access (Use Signed URLs or Signed Cookies)"를 체크하고 "Trusted Key Groups"에 아까 생성한 Key Group을 추가하고 저장합니다. 이 과정이 끝나면 서명된 쿠키가 없이는 해당 S3 Bucket에 접근하지 못합니다.

 

 

기존의 Default 패턴에 대해서는 Signed Cookie를 설정하고, master.m3u8 파일이 있는 패턴은 따로 Behavior를 만들어서 Cookie 인증을 거치지 않고 접근할 수 있도록 해주어야 합니다.

 

 

Lambda 함수 생성 & Behavior 추가

Lambda함수를 하나 생성하고, 코드를 작성합니다. Lambda함수는 여러 가지 이벤트들을 트리거로 받을 수 있지만, 여기서는 CloudFront에서 발생하는 이벤트를 받기 때문에 정해진 Format의 이벤트를 받아, 정해진 Format으로 리턴해야 합니다. 이벤트 객체에 대해서는 AWS공식문서에 잘 나와있습니다. 

 

 

제가 작성한 예제는 다음과 같습니다. 

'use strict';
const AWS = require('aws-sdk');
const qs = require('querystring');

exports.handler = (event, context, callback) => {
    // Get Datas
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;
    const params = qs.parse(request.querystring);
    
    // If Request Header does not contain JWT Token, then return
    if(!params.token) {
        callback(null, request);
    }
    
    // JWT validation
    // 여기서 JWT Validation 수행

    
    // get signed information
    const cloudFront = new AWS.CloudFront.Signer(
	  // public_key_id
	  // private_key
    );
     
    const policy = {
        Statement: [
            {
                Resource: 'https://*********.cloudfront.net/*',
                Condition: {
                  DateLessThan: {
                    'AWS:EpochTime':
                      Math.floor(new Date().getTime() / 1000) + 60 * 60 * 10,
                  },
                },
            },
        ],
    };
    
    const cookie = cloudFront.getSignedCookie({ policy: JSON.stringify(policy) });
    response.headers['set-cookie'] = [
        {
            key: 'Set-Cookie',
            value: `CloudFront-Key-Pair-Id=${cookie['CloudFront-Key-Pair-Id']}; httpOnly; secure`
        },
        {
            key: 'Set-Cookie',
            value: `CloudFront-Policy=${cookie['CloudFront-Policy']}; httpOnly; secure`
        },
        {
            key: 'Set-Cookie',
            value: `CloudFront-Signature=${cookie['CloudFront-Signature']};httpOnly; secure`
        }
    ]
    callback(null, response);

};

const private_key = 'generated private key'
const jwt_secret = 'jwt_secret';

이렇게 Lamdba함수를 생성한 뒤에 이를 Lambda@edge에 배포합니다. (배포할 때에 아까 만들어 두었던 master.m3u8이 있는 경로에 배포하면 됩니다.) 이렇게 설정한 뒤에 토큰을 가지고 해당 master.m3u8에 접근하면 이렇게 성공적으로 cookie가 세팅되고, 동영상이 제대로 재생되는 것을 알 수 있으며, 토큰을 넣어주지 않고 접근하면 cookie가 세팅되지 않아 동영상 재생이 되지 않는 것을 확인할 수 있습니다.

 

 

 

 

 

 

Reference

medium.com/@cafielo/understanding-hls-for-ios-developer-cc7788658bb8

 

Understanding HLS for iOS developer

What is HLS

medium.com

docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html

 

Serving private content with signed URLs and signed cookies - Amazon CloudFront

Serving private content with signed URLs and signed cookies Many companies that distribute content over the internet want to restrict access to documents, business data, media streams, or content that is intended for selected users, for example, users who

docs.aws.amazon.com

 

반응형

'DevOps > AWS' 카테고리의 다른 글

[ELB] Connection Draining  (0) 2021.07.28
[SQS] Long Polling & Short Polling  (0) 2021.07.27
[Global Accelerator] Introduction & Usecases  (1) 2021.07.25
[CloudWatch] Alarm & Events Usecases  (0) 2021.07.22