AWS 기술 블로그
빗썸의 AWS Security Hub 및 AWS Step Functions을 통한 멀티 어카운트 보안 모니터링 및 자동화 시스템 구축
빗썸(Bithumb)은 대한민국을 대표하는 가상자산 거래소 중 하나로, 안정적이고 확장 가능한 서비스를 제공하기 위해 AWS(Amazon Web Services)의 다양한 인프라 서비스를 적극 활용하고 있습니다. 글로벌 수준의 보안성과 고가용성을 확보하기 위해 다양한 클라우드 기반 아키텍처를 채택하고 있으며, 이러한 기술적 기반 위에 수많은 사용자에게 빠르고 안정적인 거래 환경을 제공합니다.
빗썸은 단순한 암호화폐 거래소를 넘어 디지털 자산의 미래 가치를 선도하는 기업으로 자리매김하고 있으며, 블록체인 기술과 금융 서비스를 융합해 지속적인 혁신을 이어가고 있습니다.
빗썸은 AWS 클라우드 환경에서 AWS Control Tower와 AWS Security Hub를 중심으로 한 멀티 계정 보안 거버넌스 체계를 구축하여 운영하고 있습니다. 주로 다음과 같은 보안 서비스들을 Security Hub로 통합하여 모니터링하고 있습니다:
위협 탐지 및 모니터링
- Amazon GuardDuty: 지능형 위협 탐지를 통한 실시간 보안 모니터링
- AWS CloudTrail: API 호출 로깅 및 감사 추적을 통한 사용자 활동 모니터링
취약점 관리 및 패치
- Amazon Inspector: 애플리케이션 및 인프라 취약점 자동 평가
- AWS Systems Manager: 체계적인 패치 관리 프로세스 구축
컴플라이언스 관리
- AWS Config: AWS 리소스의 구성 변경 추적 및 컴플라이언스 준수 여부 상시 확인
AWS Security Hub 기반 멀티 계정의 보안 서비스 모니터링 아키텍처
이러한 아키텍처 구성은 아래와 같은 장점을 가지고 있습니다.
- 단일 창구(Single Pane of Glass): 모든 보안 정보의 중앙 집중 관리
- 표준화된 보안 정책: Security Hub 멀티 계정 관리를 통한 일관된 보안 기준 적용
- 멀티 계정 가시성: 전체 조직의 보안 상태 통합 모니터링
- 컴플라이언스 추적: 다양한 보안 표준에 대한 준수 현황 실시간 확인
AWS Security Hub와 기타 AWS 서비스들을 통해 발견된 문제점(finding)을 수집하고 조치하는 과정에서, 단순한 사항은 AWS Lambda로 해결이 가능합니다. 하지만 컴플라이언스 위반 사항과 같은 복잡한 케이스의 경우, 처리 과정이 복잡하여 추가적인 시간과 인력 투입이 필요했습니다.
해당 포스팅에서는 이를 개선하기 위해 빗썸에서 어떻게 “컴플라이언스 위반 사항에 대한 자동조치(Auto-Remediation)”를 AWS Step Functions로 구현했는지를 소개합니다.
자동조치 아키텍처
위와 같이 Security Hub에서 탐지 내역이 발생하게 되면 Amazon EventBridge를 통해 트리거되어 간단한 작업(e.g. Slack 알림, 로그 저장 등)의 경우, AWS Lambda를 통해 처리합니다. 하지만 위에서 언급한 “컴플라이언스 위반 사항에 대한 자동조치(Auto-Remediation)” 부분은 기존에 위반 발생 → 탐지 → 수동 분석 → 수동 조치의 긴 프로세스가 필요하고 이로인한 보안 담당자의 반복적인 수동 작업으로 인한 업무의 부담이 증가했습니다.
이에 자동조치하기 위한 서비스 별 분기 처리 구성, 조건에 맞는 조치 구성 등 각 작업 항목에 대한 디버깅이 가능하도록 워크플로우 구성이 필요합니다.
AWS에서는 이러한 복잡한 프로세스를 손쉽게 설계하고 자동화할 수 있도록 AWS Step Functions을 제공합니다. Step Functions을 활용하면 복잡한 비즈니스 로직과 14,000개 이상의 AWS 기능들을 유기적으로 연결하여 원하는 클라우드 보안 태세 관리(CSPM) 자동조치 워크플로를 손쉽게 구현할 수 있습니다.
AWS Step Functions을 통한 컴플라이언스 위반 사항에 대한 자동조치
자동조치(Auto-Remediation) 워크플로우는 Finding Log 검증과 AWS service SDK integrations in Step Functions를 통한 자동조치 두 가지 핵심 과정으로 구성됩니다.
단계별 프로세스는 아래와 같습니다.
- Step 1) 변수 선언: 워크플로우 실행에 필요한 입력값을 가공하여 하위 프로세스에서 사용할 변수로 저장합니다.
- Step 2) 서비스 분기: 조치가 필요한 서비스를 기준으로 처리 프로세스를 분기합니다. 각 서비스(Security Hub, Config 등)별로 처리해야 하는 데이터 구조와 검증 로직이 다르기 때문입니다.
- Step 3) 항목별 분기 및 Tag 검증:
- Security Hub: Control ID 기준으로 분기 (Account.1, S3.8 등)
- Config: Custom Rule 및 EC2 인스턴스 처리 분기
- 각 항목별로 서로 다른 Tag Key를 사용하여 예외 상황을 추적 관리합니다.
- 특정 Tag가 존재하면 자동조치를 스킵하고, 없으면 조치를 진행합니다.
- Step 4) AWS service SDK integrations in Step Functions를 통한 자동조치:
- 보안 연락처 정보 업데이트: PutAlternateContact
- S3 퍼블릭 액세스 차단: PutPublicAccessBlock
- EC2 인스턴스 종료: TerminateInstances
- Step 5) 결과 처리: 불필요한 알림 방지하여 업무 노이즈를 감소시킵니다.
- 성공 시: 별도 알림 없이 워크플로우 종료
- 실패 시: Slack을 통해 알림 발송
- 에러 발생 시: Catch 블록에서 에러 처리 후 Slack 알림
SecurityHub의 Finding이 생성될 시 워크플로우가 트리거되어 실행되며 입력 값은 AWS Security Finding Format(ASFF)의 JSON입니다.
해당 워크플로우와 비슷한 로직 구현을 위해 설정 참고 및 활용하고자 한다면 아래 가이드를 따라 참고하실 수 있습니다.
2025년 1월에 출시한 Step Functions JSONata를 적용하여 작성되었습니다.
- AWS 콘솔에서 AWS Step Functions 서비스에서 State machines → Create state machine을 클릭합니다.
- State machine name에 적절한 상태머신 이름을 입력하고 State machine type은 Standard로 선택합니다.
- 상태머신 편집기의 이름 오른쪽에 있는 토글 메뉴에서 Code를 선택하고 아래 코드를 복사하여 붙여넣습니다.
- [Design] 편집기를 통하여 각 상태의 구성을 확인하고 필요한 경우, 변경해야 하는 값을 알맞게 조정합니다.
- 사용하는 AWS SDK에 필요한 권한을 생성한 상태머신에 추가합니다.
JSONata 기반의 ASL(Amazon States Language) 코드:
{
"Comment": "보안 자동화 워크플로우",
"StartAt": "변수 선언",
"States": {
"변수 선언": {
"Type": "Pass",
"Assign": {
"productName": "{% $states.input.ProductName %}",
"tagKeys": "{% $exists($states.input.Resources[].Tags) ? $keys($states.input.Resources[].Tags) : [] %}",
"resourceId": "{% $states.input.(Resources[].Id)[0] %}",
"resourceType": "{% $states.input.(Resources[].Type)[0] %}"
},
"Next": "서비스 분기"
},
"서비스 분기": {
"Type": "Choice",
"Choices": [
{
"Condition": "{% $productName = \"Security Hub\" %}",
"Next": "SecurityHub 처리",
"Assign": {
"controlId": "{% $states.input.Compliance.SecurityControlId %}"
}
},
{
"Condition": "{% $productName = \"Config\" %}",
"Next": "Config 처리",
"Assign": {
"title": "{% $states.input.Title %}",
"resourceId": "{% $states.input.Resources{}.Id[0] %}",
"resourceType": "{% $states.input.Resources{}.Type[0] %}"
}
}
],
"Default": "Config 처리 종료",
"Assign": {
"ResultCode": 100,
"controlId": "None",
"ResultMessage": "Not Remediation Control."
}
},
"Config 처리 종료": {
"Type": "Pass",
"End": true
},
"SecuritHub 처리 종료": {
"Type": "Pass",
"End": true,
"Output": {
"ControlId": "{% $controlId %}",
"ResultCode": "{% $ResultCode %}",
"ResultMessage": "{% $ResultMessage %}"
}
},
"SecurityHub 처리": {
"Type": "Choice",
"Default": "SecuritHub 처리 종료",
"Choices": [
{
"Condition": "{% $controlId = \"Account.1\" %}",
"Next": "보안 연락처 설정",
"Comment": "Control Id : Account.1"
},
{
"Next": "S3 공개 액세스 차단",
"Condition": "{% $controlId = \"S3.8\" and $not(\"YOUR_CUSTOM_TAG\" in $tagKeys) %}",
"Comment": "Control ID : S3.8"
}
]
},
"보안 연락처 설정": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:account:putAlternateContact",
"Arguments": {
"AlternateContactType": "SECURITY",
"EmailAddress": "YOUR_SECURITY_ADMIN_EMAIL",
"Name": "YOUR_SECURITY_ADMIN_NAME",
"PhoneNumber": "YOUR_SECURITY_ADMIN_PHONE",
"Title": "YOUR_SECURITY_ADMIN_TITLE"
},
"Next": "Security Hub 처리 Slack 알람"
},
"Security Hub 처리 Slack 알람": {
"Type": "Task",
"Resource": "arn:aws:states:::http:invoke",
"Arguments": {
"ApiEndpoint": "YOUR_SECURITY_SLACK_ALERT_URL",
"Headers": {
"Content-type": "application/json"
},
"RequestBody": {
"color": "#FFFF00",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Remediation Test"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "{% 'Remediation Test Alarm Message Body\nResource : ' & $resourceId %}"
}
}
]
},
"Authentication": {
"ConnectionArn": "YOUR_EVENTBRIDGE_CONNECTION_ARN"
},
"Method": "POST"
},
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"BackoffRate": 2,
"IntervalSeconds": 1,
"MaxAttempts": 3,
"JitterStrategy": "FULL"
}
],
"End": true,
"Output": {
"ControlId": "{% $controlId %}",
"ResultCode": "{% $ResultCode %}",
"ResultMessage": "{% $ResultMessage %}"
}
},
"S3 공개 액세스 차단": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:s3:putPublicAccessBlock",
"Arguments": {
"Bucket": "{% $split($resourceId, ':')[-1] %}",
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": true,
"IgnorePublicAcls": true,
"BlockPublicPolicy": true,
"RestrictPublicBuckets": true
}
},
"Next": "Security Hub 처리 Slack 알람",
"Output": {
"ControlId": "{% $controlId %}",
"ResultCode": 200,
"ResultMessage": "Setting Block Public Access option complete."
},
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"Comment": "Catch All",
"Next": "SecuritHub 처리 종료",
"Output": {
"ControlId": "{% $controlId %}",
"ResultCode": 400,
"ResultMessage": "Remediation was failed."
}
}
]
},
"Config 처리": {
"Type": "Choice",
"Choices": [
{
"Next": "EC2 인스턴스 종료",
"Condition": "{% $title = \"YOUR_CONFIG_RULE_TITLE\" and $not(\"YOUR_CUSTOM_TAG\" in $tagKeys) %}",
"Comment": "Custom Rule"
}
],
"Default": "Config 처리 종료"
},
"EC2 인스턴스 종료": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:ec2:terminateInstances",
"Arguments": {
"InstanceIds": [
"{% $resourceId %}"
]
},
"Next": "Config 처리 Slack 알람"
},
"Config 처리 Slack 알람": {
"Type": "Task",
"Resource": "arn:aws:states:::http:invoke",
"Arguments": {},
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"BackoffRate": 2,
"IntervalSeconds": 1,
"MaxAttempts": 3,
"JitterStrategy": "FULL"
}
],
"End": true
}
},
"QueryLanguage": "JSONata"
}
AWS Step Functions을 도입한 이유
#1 기존의 워크플로우 디버깅 문제
기존 Lambda 기반 처리에서는 복잡한 워크플로우의 각 단계별 실행 상태를 추적하기 어려웠습니다. 또한 오류 발생 시 어느 단계에서 문제가 발생했는지 파악하는데 시간이 오래 걸렸습니다.
Step Functions는?
- 각 Step의 처리 성공/실패 여부를 시각적으로 확인 가능
- 실시간 실행 상태 모니터링을 통한 즉각적인 문제 파악
- 상세한 실행 로그와 입출력 데이터 추적 기능
- 워크플로우 전체의 실행 히스토리 관리
예시 1. 리소스에 요구되는 Tag가 있어 Auto-Remediation 통과한 케이스 디버깅
예시 2. 리소스에 요구되는 Tag가 없어 Auto-Remediation 수행된 케이스 디버깅
#2 기존의 코드 품질 일관성 문제
Lambda를 이용한 코드 개발 시 개발자의 역량 및 사용 가능한 플랫폼에 따라 코드 품질이 크게 달라졌습니다. 또한 메인 담당자의 부재나 인수인계 시 코드 이해도와 유지보수성에 차이가 발생했습니다.
Step Functions는?
- ASL(Amazon States Language) 기반의 워크플로우 정의를 통한 표준화된 구조
- JSON 기반의 상태 머신 정의로 누구나 이해하기 쉬운 구조
- 비즈니스 로직의 시각적 표현으로 인한 높은 가독성
- 아래 다이어그램과 같이 JSONata의 신규 적용으로 더욱 복잡성이 감소된 상태머신 구성 가능
AWS Step Functions의 상태머신 구성 다이어그램
결론
이번 포스팅에서는 빗썸의 AWS Security Hub 기반 멀티 계정 보안 모니터링 환경에서 컴플라이언스 위반 사항에 대한 자동조치(Auto-Remediation) 시스템을 AWS Step Functions으로 구현한 사례를 소개했습니다.
이를 통해 기존의 수동적인 보안 운영 방식에서 벗어나 완전 자동화된 컴플라이언스 관리 체계를 구축할 수 있었습니다. 특히 사전에 검토되지 않은 설정 중 위반사항이 있는 경우 자동으로 규정 준수 상태로 롤백되어 Incident 누락을 차단할 수 있습니다. 모든 Compliance에 대해 Remediation을 구현한다면, 미사용중인 Resource를 사용한다 하더라도 규정에 위반하는 설정을 적용한 배포가 불가합니다. 이를 통해 보안 담당자의 업무 부담을 크게 줄이고 대응 속도를 획기적으로 향상시켰습니다.
현재 구축된 시스템을 기반으로 향후 Security Hub와 Config뿐만 아니라 다양한 보안 서비스들의 탐지 내역에 대한 자동화를 추가로 확대해 나갈 예정입니다. 더 나아가 생성형 AI를 활용하여 복잡한 보안 이벤트의 패턴 분석과 지능적인 대응 전략 수립을 통해 보다 정교하고 효율적인 자동조치 시스템을 구현할 계획입니다.
빗썸의 해당 사례가 AWS 환경에서 보안 컴플라이언스 자동화를 고민하고 있는 다른 조직들에게 실질적인 도움이 되기를 바랍니다.