본문 바로가기
★ 프로젝트 + 트러블 슈팅 ★

Lambda@Edge / Secret Manager / CloudFront / S3 트러블 슈팅 (3)

by 리승우 2024. 1. 10.

이슈

Lambda@Edge는 환경변수를 지원하지않아, 보안상 노출하면 안되는 정보를 코드에 적는 상황이 발생하였다.

하지만 이건 보안상 좋지 않은 것으로 판단되어 다른 방법을 찾아봐야 할 것으로 판단되었다.

 

해결방안

아래와 같은 방법들을 찾아보았다.

 

1. aws secret manager를 통해 비밀값 세팅 및 읽어오기

2. s3에 비밀값을 세팅한 후, 해당 오브젝트를 읽어 비밀값 세팅 및 읽어오기 

 

보안상 더 유리한 1번을 택하였고, 결국 AWS의 secret manager를 쓰는 방향으로 진행하였다.

허나 이때, 매 Lambda@Edge 요청마다 secret manager를 거쳐 비밀값을 가져오며 사용하는 것은 성능상 저하를 가져올 것이기에, secret manager를 통해 얻는 값을 전역 변수에 캐싱하여 최대한 성능상 이점을 가져가는 방향으로 진행하고자 하였다.

 

작업내용

  1. AWS Secrets Manager 작성
    1. iv, key에 대한 값 작성

2. Lambda@Edge에 Secrets Manager 활용을 위해 path-dcode-node14에 권한 설정 및 추가 (SecretsManagerReadWrite)

3. Lambda@Edge 코드 작성

const AWS = require('aws-sdk');
const crypto = require('crypto');

const secretsManager = new AWS.SecretsManager();
let cachedSecrets = null; // 글로벌 변수로 비밀 정보 캐시

exports.handler = async (event) => {
    const request = event.Records[0].cf.request;
    
    if (!cachedSecrets) {
        try {
            const secretData = await secretsManager.getSecretValue({ SecretId: 'webtoon_img_path_decode_key' }).promise();
            cachedSecrets = JSON.parse(secretData.SecretString);
        } catch (error) {
            console.error('Secrets Manager 에러:', error);
            return request; 
        }
    }

    const pathPrefix = '/comic_data/';
    const startIndex = request.uri.indexOf(pathPrefix);
    if (startIndex === -1) {
        return request; // 'comic_data/'가 없다면 원본 요청을 반환
    }

    const encryptedToken = request.uri.substring(startIndex + pathPrefix.length);

    // 비밀 정보를 aesDecrypt 함수에 전달
    const decryptedToken = aesDecrypt(encryptedToken, cachedSecrets.iv, cachedSecrets.key);

    // 복호화된 토큰을 사용하여 새로운 URI 설정
    request.uri = pathPrefix + decryptedToken;

    return request;
};

function aesDecrypt(encryptedText, secret_iv, secret_key) {
    const iv = Buffer.from(secret_iv, 'base64');
    const key = Buffer.from(secret_key, 'base64');

    const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
    decipher.setAutoPadding(true);

    let decryptedText = decipher.update(Buffer.from(encryptedText, 'base64'), 'binary', 'utf-8');
    decryptedText += decipher.final('utf-8');

    return decryptedText;
}

 

댓글