Site Icon THEREiS최신 글 게시판 · 005번 읽는 중

제 목 :

보안을 위한 시큐어 인프라, 시큐어 코딩 #1 - 인프라편

올린이
[야훔]
날짜
태그

요즘 보안 관련 사고가 연이어 터지고 있다. 해킹을 당했다는 소식이 끊이지 않고, 매년 보안에 1,200억 원씩 쓰는 은행마저 뚫리는 일이 벌어진다. 심지어 AI에게 “보안 점검을 위해 이 사이트에 침투할 수 있는 툴을 만들어 줘”라고 요청해서 만든 툴이 실제 공격에 악용되기도 하는 것 같다.

개인정보해킹사례 뉴스 기사 검색결과

보안이 이렇게 중요한 시대인데, 정작 보안은 자꾸 뚫리고 내 정보는 공공재가 되어 버린 아이러니 속에 살고 있다.

나는 웹 개발자이자 애플리케이션 개발자이고, 클라이언트가 원하면 이것저것 다 만드는 사람이다. 그러다 보니 보안 이슈로 클라이언트와 함께 난리를 수습한 경험도 여러 번 있고, 그 덕에 보안에는 나름 민감한 편이라고 생각한다.

그래서 이번에는 보안 이야기를 두 편에 나눠서 정리해 보려고 한다. 첫 번째는 인프라, 두 번째는 코딩이다.

구조적으로 아무도 못 들어오게

요즘 내가 주로 맡는 일은 인프라와 보일러플레이트 구축이다. 그래서 프로젝트를 시작할 때마다 어떻게 하면 구조적으로 아무도 들어오지 못하게 할 수 있을까를 고민한다. 최근 프로젝트에서는 이런 구조를 생각했다. EC2에는 Bastion 서버 하나만 두고, Bastion에 접근할 수 있는 사람을 제한한다. 나머지 서버는 쿠버네티스(EKS)로 구성해서 EC2에 직접 접근할 방법 자체를 없애거나, EC2로 만들더라도 AWS 명령으로 생성해 Bastion을 통해서만 접근할 수 있도록 막는 방식이다.

보안인프라 구조

여기서 핵심은 “막는다”보다 “문 자체를 없앤다”에 가깝다. EKS 워커 노드에는 SSH 키를 아예 넣지 않고, 관리는 Bastion에서 프라이빗 EKS API로 kubectl을 호출하는 것만 허용한다. EC2를 써야 한다면 퍼블릭 IP 없이 생성하고, 보안그룹의 인바운드 소스를 IP 대역이 아니라 Bastion의 보안그룹으로 지정한다. 이렇게 하면 Bastion을 거치지 않은 접속은 애초에 성립하지 않는다.

외부 사용자의 트래픽도 같은 원칙으로 다룬다. 서버가 직접 80(HTTP)이나 443(HTTPS)을 받는 게 아니라, 서버는 모두 Private Subnet에 두고 외부 요청은 앞단의 로드밸런서(ALB)만 받게 한다. 여유가 있다면 그 앞에 WAF까지 붙인다. DB 역시 Private Subnet 안에서만, 그것도 애플리케이션 서버에서만 접근할 수 있게 해서 외부와 철저히 격리한다.

ALB와 WAF로 외부 접근을 제한하는 인프라 구조

IAM도 중요하지

사실 Bastion을 아무리 잘 잠가도, AWS 콘솔에 로그인할 수 있거나 액세스 키를 가진 사람은 Bastion을 거칠 필요가 없다. 보안그룹을 열어 버리거나, 스냅샷을 복사하거나, 새 인스턴스를 띄우면 그만이다. 실제 클라우드 보안 사고도 SSH를 뚫고 들어오는 경우보다 유출된 액세스 키나 잘못 설정된 권한에서 시작되는 경우가 훨씬 많다.

그래서 “인프라에 접근할 수 있는 사람은 소수만”이라는 원칙은 Bastion뿐 아니라 IAM까지 넓혀야 한다. 루트 계정은 쓰지 않고, 모든 사용자에게 MFA를 걸고, 오래 살아 있는 액세스 키 대신 SSO나 임시 자격 증명을 쓰고, ec2:RunInstances나 보안그룹 수정 같은 위험한 권한은 정말 필요한 사람에게만 준다. 그리고 그 인원을 철저히 관리하고.

Bastion을 없애는 법. 문이 하나라는 건, 그 문만 뚫으면 된다는 뜻이다

Bastion이 유일한 진입점이 되는 순간, 공격자 입장에서는 가장 매력적인 표적이 된다. 그래서 요즘은 AWS SSM Session Manager로 Bastion의 22번 포트조차 열지 않는 방식도 많이 쓴다. 인바운드 포트가 0개가 되고, 누가 언제 접속해서 무엇을 했는지 기록도 자동으로 남는다. 결국 “Bastion조차 없앤다”가 이 구조의 다음 단계인 셈이다. 예산이 넉넉하다면 별도의 접근제어 솔루션을 붙이는 것도 같은 맥락이다.

완벽하게 막을 수는 없다

1,200억을 쓴 은행도 뚫린다는 건, 결국 완벽한 차단은 없다는 이야기이기도 하다. 그렇다면 막는 것만큼 중요한 게 뚫렸을 때 빨리 알아채는 것이다. CloudTrail로 모든 API 호출을 남기고, GuardDuty로 이상 징후를 탐지하고, VPC Flow Logs로 네트워크 흐름을 기록해 두면, 사고가 나더라도 언제 어디로 들어왔는지 추적할 수 있다. 내부 서버의 아웃바운드 트래픽도 필요한 곳으로만 제한해 두면, 설령 누군가 들어오더라도 데이터를 밖으로 빼내기가 어려워진다.

정리하면

한마디로, 인프라에 접근할 수 있는 사람은 아주 소수로 제한하고, 나머지 서버에는 그 소수조차 접근하기 매우 어렵거나 아예 불가능해야 한다. 그리고 그 “접근”에는 SSH뿐 아니라 AWS 계정과 권한까지 포함되어야 하고, 그래도 뚫릴 수 있다는 전제로 기록과 탐지를 함께 갖춰야 한다.

  • 문은 최소한으로: 서버는 Private Subnet에, 외부 트래픽은 ALB와 WAF만
  • 사람도 최소한으로: Bastion(또는 SSM)과 IAM 권한 모두 소수에게만
  • 뚫렸을 때를 대비해: CloudTrail, GuardDuty, Flow Logs로 기록하고 탐지
  • 불편하되 길은 하나: 우회할 이유를 만들지 않기

접근이 불편해야 해킹도 불편해질 테니까.

하지만 인프라를 아무리 단단하게 잠가도, 결국 외부에 열어 둔 443 포트 너머에는 우리가 짠 코드가 있다. SQL 인젝션 한 줄, 검증하지 않은 입력값 하나면 이 모든 구조를 정문으로 통과해 들어온다. 다음 편에서는 그 코드 이야기, 시큐어 코딩을 다뤄 보려고 한다.

그럼 안녕!

안녕

[ 꼬리말 ]승인 후 게시됩니다

꼬리말을 불러오는 중...