• logo

      SeolMyeongTang

  • 학부생이 바라본 클라우드 보안

    2023년 9월 04일

    보안은 중요합니다. 어떤 시스템을 기획할 때 마감일에 맞춰 구현하는 것이 최우선이지만 내가 쓴 코드나 구조가 보안적으로 안전한지에 대해 항상 고민하려고 노력합니다. 모든 해킹을 막겠다는 생각보다는 제가 생각한 구조로 구현함으로써 잠재적으로 일어날 수 있는 위협이 무엇인지 생각하고 이에 대해 예방하려고 합니다. 예방할 수 없는 경우라면 메모를 남겨서 추후 조치할 수 있도록 합니다. 인프라 구성을 하면서 제가 생각한 클라우드 보안이 어떤 것이 있는지 궁금해서 이번 글을 정리하게 되었습니다.


    저는 클라우드도 그렇고 보안도 그렇고 어느 한 쪽을 잘 안다고 말할 전문가가 아닙니다. 제가 이때까지 습득한 지식을 바탕으로 쓴 글이며, 이 글 제목 앞부분에 ‘학부생이 바라본’을 붙인 이유입니다.


    제가 생각하는 클라우드 보안은 클라우드 환경에서 서비스를 신뢰성 있게 운영하고 데이터를 안전하게 관리하는 기술 혹은 상태라 생각합니다. 클라우드 보안의 범위가 넓다고 생각하여 제 입맛대로 다음과 같이 정리해 보았습니다.

    • 클라우드 보안의 목표
      • 신뢰성있는 서비스 운영
      • 안전한 데이터 관리
    • 클라우드 보안 목표 구현 방법
      • 네트워크(요청 검사, 망분리, 방화벽, WAF, 포트 관리, CORS)
      • 데이터(이중화, 백업)
      • 시스템(안전한 인프라 구조도, 모니터링 및 로깅, 로드밸런서, 스케일링, 롤백)
      • CSP(IAM, MFA)
      • 그 외
        • 백엔드 보안
        • 보안 패치

    이외에도 신경 써야 할 보안 요소들도 있지만 위를 기준으로 생각해 보겠습니다.

    클라우드 보안을 위해 첫 번째로 살펴볼 항목은 네트워크입니다. 클라우드 보안과 네트워크는 밀접한 관련이 있습니다. 사용자와 클라우드 사이의 통신은 네트워크를 통해 이루어지기에 네트워크 보안은 클라우드 보안에 많은 부분을 차지합니다. 뒤에 나올 시스템 항목 또한 네트워크 보안과 여러 부분이 연관됩니다.

    기본적으로 사용자 요청은 신뢰하지 않는다고 생각하여 이를 감시할 필요성이 있습니다. 사용자 요청이 DB와 통신하는 WAS에 도달하기 전에 API Gateway와 같은 중간 감시자를 두어 요청 검사 및 제어를 합니다. API Gateway가 하는 역할을 다음과 같이 정리해 보았습니다.

    1. 요청 감지 및 제어
    2. 데이터 암호화
    3. 적절한 요청 분산
    4. 캐싱 처리

    WAS(Web Application Service)는 DB와 통신하면서 데이터를 저장하거나 가져와서 사용자가 필요한 데이터 형식에 맞게 변환하는 서버를 말합니다. Spring Boot, NestJS와 같은 웹 프레임워크나 server-side 언어(ex. PHP, Golang, C…)로 만든 백엔드 서버로 생각하셔도 좋습니다.


    API Gateway를 통해 HTTPS 통신만 허용하여 데이터가 보안 프로토콜 위에서 작동하게 한다든지 특정 ip가 단기간에 많은 요청을 보낸다면 그 요청은 차단한다든지 요청을 분산하여 WAS 처리 부담을 덜어준다든지 사용자의 요청을 모니터링하고 어떤 요청이 많이 오는지 조사하여 해당 요청에 대비한다든지 API Gateway를 사용함으로써 얻는 이점들이 많습니다.

    Client - API Gateway - WAS - DB 통신 과정
    Client - API Gateway - WAS - DB 통신 과정

    즉, API Gateway를 통해 정상적인 요청만 걸러 WAS에 전달해주는 역할을 합니다.


    WAS도 자체적인 웹 서버가 있어 다른 서비스들과 통신이 가능합니다. Spring Boot의 경우 Apache Tomcat, NodeJS라면 express 웹 서버를 가집니다.


    사용자와 서버 간 통신은 WAS와 통신하는 것이 아닌 API Gateway와 같은 데이터 흐름을 관리하는 gateway를 통해 통신하는 것이 안전합니다. 직접적으로 WAS와 통신한다면 임의의 사용자가 SQL Injection과 같은 웹 해킹 기법, 오픈소스 라이브러리 취약점을 이용한 공격을 여과 없이 받아들여 최악의 경우 DB 데이터를 건드릴 수 있는 위험이 있습니다. 또한 DDoS 공격으로 WAS를 다운시켜 서비스 장애를 일으킬 수 있습니다. 보통 WAS로 요청이 들어올 경우 추적이 어려워 언제 어디서 불특정 다수의 공격에 노출 되었다는 불안감이 있습니다.


    WAS에 로깅 시스템을 갖추었다면 요청이 들어와도 어느 정도 추적과 분석이 가능하겠지만, 여전히 화장실 문을 열어둔 것과 같이 불안합니다.


    이를 막기 위해 애초부터 외부 사용자와 WAS 통신을 차단해 버리는 것이 좋습니다. WAS와의 통신은 API Gateway만 통신하도록 구성하여 WAS는 API Gateway가 걸러낸 요청만 받도록 합니다. 포트 제한을 하여 특정 프로토콜 통신만 받도록 하는 것도 좋은 방법입니다. 최소한의 포트만 관리하여 애초에 필요없는 경로(포트)의 싹을 잘라버립니다. 예를 들어, HTTP 통신에 필요한 80(HTTP), 443(HTTPS) 포트만 허용하는 식입니다. AWS의 경우에는 보안 그룹(Security Groups)으로 특정 포트에 대해서만 통신을 허용할 수 있습니다. 포트 제한은 쉽고 효과적인 보안 방법이지만 관리자가 실수로 포트를 열거나 해커들의 다른 외부 접속 시도로부터 노출될 수 있습니다. 이를 위해 외부와 원천적으로 WAS 통신을 막는 방법인 망분리가 있습니다.

    포트를 통한 해커의 공격
    포트를 통한 해커의 공격

    SSH 접속도 WAS와 직접적으로 연결하는 것이 아닌 API Gateway를 거쳐 연결할 수 있도록 구성할 수 있습니다. SSH 보안을 하더라도 직접적으로 WAS와 연결하면 해커들의 개구멍이 될 수 있겠죠?


    외부(인터넷)와 통신할 수 있는 네트워크인 외부망, 내부에서만 통신할 수 있는 네트워크인 내부망을 구성합니다. 그리고 WAS를 내부망에 두어 구조적으로 외부에서 직접적으로 WAS와 통신하는 방법을 막아버립니다. AWS와 같은 CSP(Cloud Service Provider, 클라우드 서비스 제공업체)의 경우 VPC(Virtual Private Cloud) 기능을 활용하여 public subnet, private subnet으로 외부망, 내부망을 구축할 수 있습니다.


    군대로 예시로 들어보자면, 군부대 안에서만 쓰는 국방망(인트라넷)이 내부망이고 사지방의 인터넷망이 외부망입니다. 국방망을 쓰는 군대 컴퓨터에 인터넷망 랜선을 꽂으면… 간부님이 안 좋아하실 겁니다.


    외부망에는 API Gateway를 두고 내부망에는 WAS 서버를 두는 식으로 WAS를 외부로부터 지킬 수 있습니다. WAS 뿐만 아니라 DB나 MQ(메시지큐)와 같이 외부와 직접적으로 통신할 필요가 없는 서비스들은 내부망에 구축하는 것이 안전합니다. 즉, WAS와의 통신은 외부망의 API Gateway만 통신하도록 하여 다른 외부 공격을 원천 차단해 버립니다.

    외부망(public subnet)과 내부망(private subnet)
    외부망(public subnet)과 내부망(private subnet)

    망분리까지 고려했지만 아직 생각해 볼 상황들이 남아있습니다. API Gateway로부터 요청이 들어왔지만, WAS 서버와 DB가 못 버틸 정도의 규모이거나 API Gateway가 미처 걸러내지 못한 악성 요청 상황이 있을 수 있습니다. 이 경우 다음과 같은 위험이 있을 수 있습니다.

    • 많은 트래픽으로 인한 문제
      1. API Gateway 자원 부족으로 응답 지연 혹은 프로세스 종료
      2. WAS 자원 부족으로 응답 지연 혹은 프로세스 종료
      3. DB 자원 부족으로 응답 지연 혹은 프로세스 종료
      4. 2, 3번으로 인한 데이터 소실 혹은 꼬임
      5. 수 많은 트래픽으로 인한 서버 비용 문제
    • API Gateway가 미처 걸러내지 못한 악성 요청(ex. DDoS, SQL Injection)
      1. DB 데이터 탈취 및 변조
      2. 비정상적인 응답 유도

    요청(request)과 트래픽(traffic) 용어를 구분하여 설명했습니다. 요청은 사용자가 서버로 보내는 네트워크 통신을 말하는 것이고 트래픽은 요청, 응답을 포함하는 용어로 표현했습니다.


    트래픽이 많은 경우는 언제일까요? 대규모 서비스라 사용자의 요청이 많을 수도 있고, 대규모 서비스는 아니지만 수강 신청과 같은 특정 이벤트로 인한 단기간에 요청이 많을 수도 있고, DDoS 공격으로 비정상적인 요청이 들어오는 경우를 생각할 수 있습니다. OS나 API Gateway, WAS, DB 서비스들이 많은 요청을 처리하고 응답을 하느라 CPU, RAM, disk 자원이 부족해질 수 있습니다. 이로 인해 응답이 상당히 느려지거나, 최악의 경우 서비스가 죽는 일이 발생할 수 있습니다. 트래픽이 많다면 로드밸런싱을 통한 요청 분산, 서비스 스케일링, 이중화로 인한 고가용성 유지로 트래픽을 소화할 수 있습니다. 이 부분은 뒤에 나오는 ‘시스템’ 항목에서 다루도록 하겠습니다.

    순수하게 서비스 이용자가 많아 트래픽이 발생하는 상황은 빼고 DDoS나 SQL Injection 공격처럼 불특정 다수가 악의적인 요청을 보내면 어떻게 대처하면 좋을까요? 요청이 API Gateway로 오기 전 IDS(Intrusion Detection System, 침입 탐지 시스템), IPS(Intrusion Prevention System, 침입 방지 시스템), WAF(Web Application Firewall, 웹 어플리케이션 방화벽) 거치게 하여 들어온 요청이 보안적으로 안전한지 검사하는 과정을 추가합니다.

    IDS와 IPS는 OSI 7 계층 기준으로 4~7 계층을 모니터링 하고 악성 요청과 정책 위반한 요청을 ‘탐지’하고 ‘방지’합니다. WAF는 HTTP 요청을 ‘탐지’하고 SQL Injection, XSS, WebShell 등의 공격을 ‘방지’합니다. IDS와 IPS는 보통 하나의 시스템으로 묶어 관리하고 WAF보다 낮은 계층을 조사할 수 있어 넓은 범위의 공격을 막을 수 있습니다. SYN flood, UDP/ICMP flood와 같이 WAF에서 걸러낼 수 없는 3~4 계층의 DDoS 공격은 IDS/IPS에서 막을 수 있습니다.


    방화벽은 종류가 다양하며 firewall와 WAF는 다른 시스템입니다. 제가 언급한 firewall은 주로 IDS/IPS 앞 단에 위치해서 OSI 3~4 계층을 탐지합니다. WAF는 IDS/IPS 뒤 단에 위치하며 HTTP(OSI 7 계층) 요청을 탐지합니다.


    WAF는 구축한 환경에 따라 다르지만 OWASP(Open Web Application Security Project)에서 발표한 10개의 웹 어플리케이션 보안 취약점을 방지하기 위한 규칙(CRS, Core Rule Set)을 적용한 방화벽 시스템입니다. 여러 가지 방법으로 WAF를 구축할 수 있지만 저의 경우, NGINX에 CRS가 적용된 ModSecurity를 구축했습니다.

    네트워크 보안 장비로 IDS/IPS와 WAF를 구축할 수 있지만 소프트웨어로도 구축할 수 있습니다. IDS/IPS의 오픈소스 소프트웨어로는 Snort, Suricata가 있고 WAF는 앞서 말한 ModSecurity가 있습니다. WAF는 아니지만 fail2ban와 같은 로그 모니터링 프로그램을 이용하여 무차별 대입 공격을 막아주는 프로그램이 있으니 참고하면 좋을 거 같습니다. 다음에 기회가 된다면 IDS/IPS와 WAF 시스템 구축기를 정리하는 시간을 가져보겠습니다.

    API Gateway 앞에 Firewall, IDS/IPS, WAF를 두어 비정상적인 요청을 감지한다
    API Gateway 앞에 Firewall, IDS/IPS, WAF를 두어 비정상적인 요청을 감지한다

    IDS/IPS와 WAF를 모두 사용하면 더욱 효과적으로 DDoS와 같은 악성 요청들을 막을 수 있습니다. 하지만 서비스를 외부 공격으로부터 100% 안전하게 막을 수 있는 것은 아니니 모니터링 시스템을 구축하고 비정상적인 상황이 나타나면 이에 맞는 대응이 중요합니다.

    IDS/IPS와 WAF로 악성 요청을 걸러낼 수 있지만 순수하게 사용자가 많아 트래픽이 많은 경우, 자원 부족으로 서비스가 죽어 데이터가 꼬이거나 유실될 수 있고, 너무 느리거나 비정상적인 응답이 올 수 있습니다. 이렇게 되면 신뢰성 있는 서비스를 보장할 수 없습니다. 이를 보완하기 위해 개발자는 사용자의 요청을 적절히 분산시켜 서버 부하를 줄이다든지, 이중화나 클러스터링 작업으로 서비스가 죽어도 예비 서비스가 바로 가동되어 처리한다든지, 배포를 진행했는데 예상치 못한 취약점이나 버그로 이전 상태로 돌려야 한다는 것과 같은 대규모 트래픽을 위한 인프라를 구축해야 하는 경우가 있습니다.

    대규모 트래픽을 다루는 기술은 어렵습니다. 여러 서버를 둠으로써 나타나는 동시성이나 통신 문제, 시스템 및 네트워크 로깅 문제, 이중화로 인한 동기화 문제, 수많은 자원의 배포 및 모니터링 문제 정말 고려해야 할 상황이 많습니다. 하지만 Kubernetes, ArgoCD, Istio, Jaeger, Prometheus 등 대규모 서비스를 안정적으로 운영하기 위한 오픈소스 소프트웨어들이 많아 이를 활용하여 보다 쉽게 위 문제점들을 해결할 수 있습니다.


    CNCF(Cloud Native Computing Foundation)에서는 클라우드 환경에서의 서비스 구축, 배포, 모니터링, 스케일링, 롤백 등을 위한 프로젝트를 운영 및 개발합니다. CNCF 프로젝트를 살펴보면 컨테이너, 서버리스, 분산 처리, 배포, 모니터링, 로깅 등 대규모 서비스(cloud native스러운)에 적합한 프로젝트들을 찾아볼 수 있습니다.


    요청 분산, 스케일링, 이중화, 롤백, 모니터링, 로깅에 대한 예시를 들면서 설명하면 좋겠지만 간단한 언급만 하고 이 부분도 다음에 정리해보도록 하겠습니다.

    간단하게 그려본 안전한 인프라 구조도 예시
    간단하게 그려본 안전한 인프라 구조도 예시

    아무리 좋은 인프라 구조를 설계했다 한들 API Gateway가 죽으면 말짱 도루묵입니다. 그럼 API Gateway의 고가용성 확보는 어떻게 할까요? 가상 IP를 통해 로드밸런싱을 할 수 있는 Keepalived를 사용한 방법, Kubernetes의 Replicaset 리소스를 이용한 방법, AWS의 ELB 서비스(여러 가용 영역에 걸쳐 배포하는 방법으로 고가용성을 확보합니다)를 이용하면 API Gateway의 고가용성을 확보할 수 있습니다.


    그 다음의 클라우드 보안으로 CSP의 보안이 있습니다. AWS, GCP, Oracle Cloud, Naver Cloud 등의 CSP는 계정마다 클라우드 서비스(ex. AWS의 EC2, RDS)에 대한 접근을 관리할 수 있습니다. 만약 해커가 계정 정보나 access key, secret key를 탈취하고 마음대로 클라우드 서비스를 접근할 수 있다거나 실수로 개발자가 RDS 서비스와 같은 중요 서비스를 삭제한다면… 상상만 해도 정말 끔찍합니다. 미연의 사고를 방지하기 위해 IAM(Identity and Access Management)을 설정하여 계정 접근 권한을 지정하는 것이 좋습니다.

    예를 들어, 필요한 경우가 아니라면 직접적인 결제를 할 수 있는 root 계정은 최대한 로그인하지 않고 admin 계정으로 로그인합니다. 개발자 계정도 백엔드팀, 인프라팀으로 나누고 팀마다 접근 가능한 클라우드 서비스를 제한하는 것이 바람직합니다.

    IAM 예시
    IAM 예시

    IAM 설정 뿐만 아니라 MFA(Multi-Factor Authentication) 또한 활용하여 이중 로그인을 꼭 설정하도록 합시다. MFA는 OTP나 보안 카드처럼 추가적인 인증을 요구하는 인증 방법으로 계정을 더욱 안전하게 지켜줍니다. MFA 설정은 간단하고 과금도 되지 않으니 꼭 MFA 설정을 해주도록 합시다.

    그 외에도 인프라, 시스템 관점이 아닌 개발자의 관점인 시큐어 코딩이 있습니다. 서버에 직접적인 영향을 끼치는 백엔드로 살펴보면 사용자 요청 검사, 파일 업로드 크기 제한, prepared statement 사용, 적절한 exception 처리, 불필요한 API 삭제 같은 방법들이 있습니다.

    TYPESCRIPT
    import { IsNotEmpty, IsOptional, IsString, Matches } from "class-validator";
    
    @Matches(/^(http|https):\/\/([\w\-]+\.)+[\w\-]+(\/[\w\-./?%&=]*)?([^\s]*)$/)
    @IsOptional()
    imageUrl: string | null;
    
    @IsString()
    @IsNotEmpty()
    name: string;
    
    // 요청이 적절하지 않은 경우 400에러 반환
    

    주기적으로 커널, 라이브러리 보안 패치를 적용하여 최신 상태를 유지하는 것도 안전한 서비스를 유지하는 좋은 방법입니다. 저는 라이브러리를 선택할 때 LTS인지 최근까지도 꾸준한 업데이트가 있는지 여러 개발자가 쓰고 있는지에 대해 확인하고 사용 여부를 결정합니다.


    아이러니하게 예전 버전의 소프트웨어가 안전하고 최신 소프트웨어에만 취약한 경우도 있지만(ex. Log4Shell CVE-2021-44228) 지속적으로 보안 패치를 해주고 관리해주는 소프트웨어를 쓰는 편이 보안 위험으로부터 최소화 할 수 있다고 생각합니다.


    클라우드 보안은 서비스를 안정적으로 운영하여 신뢰성 있는 서비스를 제공하고 안전하게 데이터를 관리해야 합니다. 이를 위한 네트워크 보안, 안전한 인프라 구조 설계, 서비스 이중화, 적절한 계정 관리, 보안 패치에 대해 정리해 보았습니다. 항목마다 구체적인 예시도 같이 곁들이면서 설명하고 싶었는데 이번 글은 이론적인 이야기를 중심으로 풀어냈습니다. 다음에 기회가 된다면 하나씩 정리하는 시간을 갖도록 하겠습니다.

    보안은 어렵습니다. 보안 분야를 접하면서 든 생각이지만 컴퓨터 분야의 꽃은 보안이 아닐까 생각이 듭니다. Low-level부터 high-level까지 전반적인 컴퓨터 이해가 있어야 효과적인 보안을 할 수 있다고 생각합니다. 클라우드 보안이라는 주제로 글을 정리하면서 평소 보안을 low-level 시선으로만 봤던 시야를 high-level로도 넓혀주는 시간이었던 것 같습니다.