인증서 해지
손상, 정책 변경 또는 관계 종료로 인해 인증서를 취소해야 하는 경우 mTLS 핸드셰이크 중에 해당 인증서를 거부하는 메커니즘이 필요합니다. CloudFront는 인증서 해지에 대한 두 가지 기본 접근 방식을 제공하며 계층화된 제어를 위해 이를 결합할 수 있습니다.
-
OCSP(온라인 인증서 상태 프로토콜) - CloudFront는 인증 기관의 OCSP 응답자를 실시간으로 쿼리하여 클라이언트 인증서가 취소되었는지 확인합니다. 트러스트 스토어에서 OCSP를 활성화하면 CloudFront는 TLS 핸드셰이크 중에 검증을 자동으로 처리합니다. OCSP 결과는 연결 함수에도 표시되므로 사용자 지정 의사 결정을 위한 취소 상태에 프로그래밍 방식으로 액세스할 수 있습니다.
-
CloudFront Functions 및 KeyValueStore - 취소된 인증서 일련 번호 목록을 CloudFront KeyValueStore에 유지합니다. 연결 함수는 TLS 핸드셰이크 중에 KeyValueStore를 쿼리하고 연결을 허용하거나 거부합니다. 이를 통해 해지 데이터, 업데이트 타이밍, 유예 기간 또는 IP 기반 예외와 같은 사용자 지정 로직을 완벽하게 제어할 수 있습니다.
| OCSP | CloudFront Functions + KeyValueStore | |
|---|---|---|
| 데이터 소스 | 인증 기관의 OCSP 대응 담당자 | 해지 목록 관리 |
| 업데이트 메커니즘 | CA에 대한 실시간 쿼리 | KeyValueStore에 업데이트 푸시 |
| 사용자 지정 로직 | 연결 함수를 통해 사용 가능 | 함수 코드에 내장 |
| 외부 종속성 | CA OCSP 응답자 가용성 필요 | 외부 종속성 없음 |
| 최적의 용도 | OCSP 대응 담당자를 유지하는 CA, 실시간 CA의 신뢰할 수 있는 상태 | 자체 관리형 해지, 사용자 지정 정책, OCSP가 지원되지 않는 CA |
두 접근 방식을 함께 사용할 수 있습니다. CA 인증 해지 확인을 위해 OCSP를 활성화한 다음 연결 함수를 사용하여 OCSP 결과 위에 추가 로직을 계층화합니다. 예를 들어 유예 기간 동안 신뢰할 수 있는 IP 범위에서 취소된 인증서를 허용합니다.
OCSP(온라인 인증서 상태 프로토콜)
OCSP는 인증 기관(CA)을 통해 인증서의 해지 상태를 직접 확인하는 실시간 프로토콜입니다. 인증서 서명 프로세스 중에 CA는 인증서에 OCSP 응답자 URL을 포함합니다. 클라이언트가 mTLS 핸드셰이크 중에 인증서를 제시하면 CloudFront는 OCSP 요청을 임베디드 응답자 URL로 전송하고 응답에 따라 작동합니다. 유효한 인증서가 진행되고 취소된 인증서가 종료됩니다.
CloudFront는 리프 인증서와 최대 3개의 중간 인증서 등 전체 인증서 체인을 각 OCSP 응답자 URL을 통해 검증합니다. 트러스트 스토어의 루트 인증서는 OCSP 검증에서 제외됩니다.
OCSP 활성화
트러스트 스토어에서 OCSP를 활성화합니다. 활성화되면 CloudFront는 권한 정보 액세스(AIA) 확장에 OCSP 응답자 URL이 포함된 모든 클라이언트 인증서에 대해 OCSP 검증을 자동으로 수행합니다. OCSP가 활성화되면 전체 클라이언트 인증서 체인에 OCSP URL이 있어야 합니다. 클라이언트 인증서 체인의 인증서에 OCSP URL이 포함되지 않은 경우 CloudFront는 연결을 설정하지 않습니다.
CloudFront는 엣지에서 OCSP 응답을 캐싱하여 왕복 시간을 줄이고 OCSP 응답자 가동 중지 시간을 방지합니다. OCSP 응답은 약 30분 동안 캐시되며 업데이트된 취소 상태가 반영되는 데 최대 30분이 걸릴 수 있습니다.
OCSP는 연결 함수를 생성합니다.
연결 함수가 동일한 배포에 구성된 경우 CloudFront는 OCSP 검증이 완료된 후 해당 함수를 간접적으로 호출합니다. 연결 객체에는 리프 및 중간 인증서의 OCSP 상태가 포함됩니다.
{ "clientCertificate": { "certificates": { "leaf": { "subject": "CN=client.example.com, O=Example Org", "issuer": "CN=Intermediate CA, O=Example Org", "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86", "validity": { "notBefore": "2024-01-01T00:00:00Z", "notAfter": "2025-01-01T00:00:00Z" }, "sha256Fingerprint": "AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90", "ocspEndpoint": "http://ocsp.example.org" }, "intermediates": [ { "subject": "CN=Intermediate CA, O=Example Org", "issuer": "CN=Root CA, O=Example Org", "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86", "validity": { "notBefore": "2020-01-01T00:00:00Z", "notAfter": "2030-01-01T00:00:00Z" }, "sha256Fingerprint": "12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF", "ocspEndpoint": "http://ocsp.example.org" } ] }, "revocationStatus": { "chainValidity": "Valid", // "Valid" | "Invalid" | "Unknown" "certificates": { "leaf": { "method": "OCSP", // "OCSP" "status": "Good", // "Good" | "Revoked" | "Unknown" | "Error" "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86" }, "intermediates": [ { "method": "OCSP", // "OCSP" "status": "Error", // "Good" | "Revoked" | "Unknown" | "Error" "errorType": "InternalError", // "InternalError" | "OCSP response verification failed: {Type}" "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86" } ] } } }, "clientIp":"127.0.0.1", "endpoint":"d123.cloudfront.net", "distributionId":"E1NXS4MQZH501R", "connectionId":"xdzQ6lJUDUt8b7OuqOD8lmzOC9HcMaXPmhH5ZdzLCZpKxqzfCPpR4A==" }
chainValidity 필드는 Valid, Invalid 또는 Unknown일 수 있습니다. 개별 인증서 status 값은 Good, Revoked, Unknown 또는 Error일 수 있습니다. 상태가 Error인 경우 errorType 필드에 InternalError 또는 OCSP response verification
failed: {Type}이 포함됩니다.
연결 함수는 신뢰할 수 있는 IP 범위에서 취소된 인증서를 허용하거나 OCSP가 추가 비즈니스 로직을 기반으로 ‘좋음’으로 보고하는 인증서를 거부하는 등 OCSP 결과를 재정의할 수 있습니다.
참고
errorType 필드는 상태가 Error인 경우에만 표시됩니다.
예: 신뢰할 수 있는 IP 예외를 사용한 사용자 지정 OCSP 처리
function connectionHandler(connection) { var revocationStatus = connection.clientCertificate.revocationStatus; var trustedIP = (connection.clientIp === "[IP_ADDRESS]"); if (revocationStatus.chainValidity === "Invalid") { if (trustedIP) { connection.allow(); } else { connection.deny(); } } else if (revocationStatus.certificates.leaf.status === "Error") { console.log(revocationStatus.certificates.leaf.errorType); connection.deny(); } else { connection.allow(); } }
OCSP 실패 동작
OCSP 응답자에 연결할 수 없거나 오류를 반환하거나 ‘알 수 없음’을 반환하여, CloudFront가 인증서의 해지 상태를 확인할 수 없는 경우 CloudFront는 기본적으로 연결을 거부합니다. 소프트 실패 동작을 구현하려면 연결 함수를 사용합니다. OCSP가 활성화되어 있고 클라이언트 인증서에 OCSP URL이 없는 경우 연결 함수가 실행되지 않습니다. OCSP 결과는 연결 객체에서 사용할 수 있으며 함수는 OCSP 상태가 확인되지 않은 경우 연결을 허용할 수 있습니다.
async function connectionHandler(connection) { var revocationStatus = connection.clientCertificate.revocationStatus; if (revocationStatus.certificates.leaf.status === "Error" || revocationStatus.certificates.leaf.status === "Unknown") { // OCSP responder unreachable — allow connection (soft-fail) connection.logCustomData(`OCSP_SOFT_FAIL:${revocationStatus.certificates.leaf.errorType}`); return connection.allow(); } if (revocationStatus.chainValidity === "Invalid") { return connection.deny(); } return connection.allow(); }
CloudFront Functions 및 KeyValueStore를 사용한 인증서 해지
CloudFront 연결 함수를 KeyValueStore와 함께 사용하여 외부 종속성 없이 인증서 해지 확인을 구현할 수 있습니다. KeyValueStore에 해지된 인증서 일련 번호 목록을 유지하고, 연결 함수는 TLS 핸드셰이크 중에 각 클라이언트 인증서를 이 목록과 비교하여 확인합니다.
인증서 해지 프로세스는 다음과 같이 작동합니다.
-
해지된 인증서 일련 번호를 CloudFront KeyValueStore에 저장합니다.
-
클라이언트가 인증서를 제시하면 연결 함수가 간접 호출됩니다.
-
함수는 KeyValueStore에 대해 인증서의 일련 번호를 확인합니다.
-
스토어에서 일련 번호를 찾으면 인증서가 해지됩니다.
-
함수는 해지된 인증서에 대한 연결을 거부합니다.
이 접근 방식은 CloudFront의 글로벌 엣지 네트워크에서 해지 확인을 실시간에 가깝게 제공합니다.
이 접근 방식을 구현하려면 다음이 필요합니다.
-
뷰어 mTLS로 구성된 배포
-
해지된 인증서 일련 번호가 포함된 KeyValueStore
-
KeyValueStore를 쿼리하여 인증서 상태를 확인하는 연결 함수
클라이언트가 연결되면 CloudFront는 트러스트 스토어에 대해 인증서를 검증한 다음 연결 함수를 실행합니다. 함수는 KeyValueStore에 대해 인증서 일련 번호를 확인하고 연결을 허용하거나 거부합니다.
1단계: 해지된 인증서에 대한 KeyValueStore 생성
해지된 인증서 일련 번호를 JSON 형식으로 준비합니다.
{ "data": [ { "key": "ABC123DEF456", "value": "" }, { "key": "789XYZ012GHI", "value": "" } ] }
이 JSON 파일을 S3 버킷에 업로드한 다음 KeyValueStore를 생성합니다.
aws s3 cp revoked-serials.json s3://your-bucket-name/revoked-serials.json aws cloudfront create-key-value-store \ --name revoked-serials-kvs \ --import-source '{ "SourceType": "S3", "SourceARN": "arn:aws:s3:::your-bucket-name/revoked-serials.json" }'
KeyValueStore가 프로비저닝을 완료할 때까지 기다립니다. 다음을 통해 상태를 확인합니다.
aws cloudfront get-key-value-store --name "revoked-serials-kvs"
2단계: 해지 연결 함수 생성
KeyValueStore에 대해 인증서 일련 번호를 확인하는 연결 함수를 생성합니다.
aws cloudfront create-connection-function \ --name "revocation-control" \ --connection-function-config file://connection-function-config.json \ --connection-function-code file://connection-function-code.txt
구성 파일은 KeyValueStore 연결을 지정합니다.
{ "Runtime": "cloudfront-js-2.0", "Comment": "A function that implements revocation control via KVS", "KeyValueStoreAssociations": { "Quantity": 1, "Items": [ { "KeyValueStoreArn": "arn:aws:cloudfront::account-id:key-value-store/kvs-id" } ] } }
샘플 연결 함수 코드:
import cf from 'cloudfront'; async function connectionHandler(connection) { const kvsHandle = cf.kvs(); // Get client serial number from client certificate const clientSerialNumber = connection.clientCertificate.certificates.leaf.serialNumber; // Check KVS to see if serial number exists as a key // Remove : from the clientSerialNumber if KVS entries dont have it const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber.replaceAll(":", "")); // Deny connection if serial number exists in KVS if (serialNumberExistsInKvs) { console.log("Connection denied — certificate revoked"); connection.logCustomData("Connection denied — certificate revoked"); return connection.deny(); } // Allow connections that don't exist in KVS console.log("Connection allowed"); return connection.allow(); }
3단계: 해지 함수 테스트
CloudFront 콘솔을 사용하여 샘플 인증서로 연결 함수를 테스트합니다. 콘솔에서 연결 함수로 이동하여 테스트 탭을 사용합니다.
-
테스트 인터페이스에 PEM 형식의 샘플 인증서를 붙여 넣습니다.
-
선택적으로 IP 기반 로직을 테스트하기 위한 클라이언트 IP 주소를 지정합니다.
-
함수 테스트를 선택하여 실행 결과를 확인합니다.
-
실행 로그를 검토하여 함수 로직을 확인합니다.
유효한 인증서와 해지된 인증서를 모두 사용해 테스트하여 함수가 두 시나리오를 올바르게 처리하는지 확인합니다.
4단계: 배포에 함수 연결
연결 함수를 게시한 후 mTLS 지원 배포와 연결하여 인증서 해지 확인을 활성화합니다. 배포 설정으로 이동하여 ‘뷰어 상호 인증(mTLS)’ 섹션으로 스크롤한 후 연결 함수를 선택하고 변경 사항을 저장합니다.
고급 해지 전략
OCSP를 연결 함수 로직과 결합
CA 인증 해지 확인을 위해 OCSP를 활성화하고 사용자 지정 정책을 위해 연결 함수를 위에 계층화할 수 있습니다. 연결 함수는 OCSP 결과를 수신하고 추가 로직을 적용할 수 있습니다.
-
유예 기간 - 인증서 교체 중에 정의된 기간 동안 내부 네트워크에서 취소된 인증서를 허용합니다.
-
긴급 액세스 - OCSP가 취소된 상태를 보고하는 경우에도 특정 IPs로부터의 연결을 허용합니다.
-
사용자 지정 거부 로직 - OCSP가 KeyValueStore의 자체 해지 데이터를 기반으로 ‘좋음’으로 보고하는 인증서를 차단합니다.
예 OCSP + KVS 통합 해지
import cf from 'cloudfront'; async function connectionHandler(connection) { var kvsHandle = cf.kvs(); var revocationStatus = connection.clientCertificate.revocationStatus; var serialNumber = connection.clientCertificate.certificates.leaf.serialNumber; // Check your own revocation list first (immediate revocation, no cache delay) var inKvs = await kvsHandle.exists(serialNumber.replaceAll(":", "")); if (inKvs) { connection.logCustomData("KVS_REVOKED:" + serialNumber); return connection.deny(); } // Then check OCSP result if (revocationStatus.chainValidity === 'Valid' && revocationStatus.certificates.leaf.status === "Revoked") { // OCSP says revoked — allow grace period from trusted IPs if (connection.clientIp.startsWith("10.0.")) { connection.logCustomData("GRACE_PERIOD:" + serialNumber + ":" + connection.clientIp); return connection.allow(); } connection.logCustomData("OCSP_REVOKED:" + serialNumber); return connection.deny(); } connection.allow(); }
이 패턴은 KVS(캐시 지연 없음)를 통한 즉각적인 취소와 OCSP를 통한 CA 인증 해지를 제공하며, 그 사이에 사용자 지정 예외 처리를 제공합니다.