공격자의 사고방식: 공격자의 관점에서 보안을 바라보는 개발자를 위한 가이드
요약
개발자가 보안을 강화하기 위해 공격자의 관점에서 시스템을 바라보는 사고방식의 전환을 제안합니다. 공격자의 주요 목표와 정찰 단계, 공격 표면 매핑 과정을 통해 탄력성 있는 소프트웨어를 구축하는 방법을 설명합니다.
핵심 포인트
- 보안은 벽을 쌓는 것이 아니라 공격자의 의도를 이해하는 것임
- 공격자는 데이터 탈취, 권한 획득 등 명확한 목표를 가지고 움직임
- 정찰(Reconnaissance) 단계에서 공개된 정보 수집이 매우 중요함
- 기능 중심이 아닌 진입점(Entry points) 중심으로 공격 표면을 파악해야 함
"보안은 벽을 쌓는 것이 아닙니다. 누군가가 그 벽을 어떻게 넘으려 할지 이해하는 것입니다."
대부분의 개발자는 단 한 사람, 즉 사용자를 염두에 두고 소프트웨어를 구축합니다.
공격자는 그렇지 않습니다.
여러분이 사용자 여정(user journeys), 기능 출시, 그리고 UX 개선을 생각하고 있는 동안, 공격자들은 매우 다른 질문들을 던지고 있습니다:
- 인증(authentication)을 우회할 수 있는가?
- 다른 사용자의 데이터에 접근할 수 있는가?
- API 요청을 조작할 수 있는가?
- 애플리케이션이 나를 신뢰하게 만들 수 있는가?
- 개발자들이 어떤 가정을 했는가?
개발자가 할 수 있는 가장 큰 사고방식의 전환 중 하나는 공격자처럼 생각하는 법을 배우는 것입니다. 이는 시스템을 악용하기 위해서가 아니라, 오용에 대해 탄력성(resilient)을 갖춘 소프트웨어를 구축하기 위함입니다.
공격자들이 일반적으로 애플리케이션에 어떻게 접근하는지, 그리고 더 중요하게는 개발자들이 그러한 사고방식으로부터 무엇을 배울 수 있는지 살펴보겠습니다.
공격자의 목표
일반적인 믿음과는 달리, 대부분의 공격자는 정교한 악성코드(malware)를 작성하거나 영화에 나올 법한 제로데이(zero-day) 취약점을 발견하려고 시도하지 않습니다.
대부분의 공격은 명확한 목표를 가지고 있습니다:
- 데이터 탈취
- 자격 증명(credentials) 획득
- 권한 없는 접근(unauthorized access) 획득
- 임의 코드 실행(execute arbitrary code)
- 랜섬을 위한 시스템 암호화
- 클라우드 리소스 남용
- 침해된 시스템의 수익화
그들이 취하는 모든 행동은 이러한 목표 중 하나를 수행합니다.
단 하나의 익스플로잇(exploit)을 작성하기 전에, 그들은 타겟을 이해하는 데 시간을 보냅니다.
1단계: 정찰 (Reconnaissance)
전문적인 공격자들은 취약점을 악용하는 것보다 정보를 수집하는 데 훨씬 더 많은 시간을 소비합니다.
그들은 다음과 같은 질문에 답하기를 원합니다:
- 애플리케이션을 구동하는 기술은 무엇인가?
- AWS, Azure, 또는 Google Cloud에서 실행 중인가?
- 어떤 프레임워크(framework) 버전이 사용되고 있는가?
- 어떤 API가 공개적으로 접근 가능한가?
- 노출된 스테이징(staging) 환경이 있는가?
- GitHub에 유출된 자격 증명이 포함되어 있는가?
- 잊혀진 서브도메인(subdomains)이 있는가?
- 어떤 직원이 권한 있는 접근(privileged access)을 가지고 있는가?
이 과정을 **정찰 (reconnaissance)**이라고 합니다.
일부 일반적인 공개 소스에는 다음과 같은 것들이 포함됩니다:
- GitHub 저장소 (repositories)
- DNS 레코드 (DNS records)
- 인증서 투명성 로그 (Certificate Transparency logs)
- 검색 엔진 (Search engines)
- 공개 문서 (Public documentation)
- 채용 공고 (Job postings)
- 기술 블로그 포스트 (Technical blog posts)
- 공개 클라우드 스토리지 (Public cloud storage)
공격자들은 개발자가 실수로 노출한 정보를 매우 좋아합니다.
잊혀진 백업 파일이나 공개된 .env 파일은 값비싼 익스플로잇 (exploit)보다 더 큰 가치가 있을 수 있습니다.
2단계: 공격 표면 매핑 (Mapping the Attack Surface)
대상을 파악하고 나면, 공격자들은 자신이 상호작용할 수 있는 모든 것을 식별하기 시작합니다.
개발자들은 종종 기능 (features)에 대해 생각합니다:
로그인 (Login)
대시보드 (Dashboard)
결제 (Payments)
...
공격자들은 진입점 (entry points)에 대해 생각합니다:
로그인 엔드포인트 (Login endpoint)
비밀번호 재설정 (Password reset)
회원가입 (Registration)
...
노출된 모든 엔드포인트는 잠재적인 기회가 됩니다.
공격 표면 (attack surface)이 넓을수록 실수가 발생할 기회도 더 많아집니다.
3단계: 모든 입력값 테스트 (Testing Every Input)
공격자들은 당신의 검증 (validation)을 신뢰하지 않습니다.
대신, 그들은 다음과 같은 질문을 던집니다:
- 음수를 보내면 어떻게 될까?
- 100MB 크기의 페이로드 (payload)를 제출하면 어떻게 될까?
- 실행 가능한 파일을 업로드할 수 있을까?
- 필수 파라미터 (parameters)를 제거하면 어떻게 될까?
- 예상치 못한 문자를 주입 (inject)할 수 있을까?
- 서버가 무엇인가를 검증하고 있는가?
예를 들어, 일반 사용자는 다음과 같이 제출합니다:
{
"email": "alice@example.com"
}
하지만 공격자는 다음과 같이 시도할 수 있습니다:
{
"email": "' OR 1=1 --"
}
또는:
{
"role": "admin"
}
또는 심지어:
{
"price": -5000
}
공격자들은 애플리케이션을 의도된 대로 사용하지 않습니다.
그들은 개발자가 전제한 가정 (assumptions)들을 찾아내려고 시도합니다.
4단계: 인증 무력화 (Breaking Authentication)
인증 (Authentication)은 보통 가장 먼저 노리는 주요 목표 중 하나입니다.
공격자들은 다음과 같은 취약점을 찾습니다:
- 취약한 비밀번호 (Weak passwords)
- 비밀번호 재사용 (Password reuse)
- 크리덴셜 스터핑 (Credential stuffing)
- 무차별 대입 공격 (Brute-force attacks)
- 예측 가능한 재설정 토큰 (Predictable reset tokens)
- 세션 고정 (Session fixation)
- 취약한 JWT 검증 (Weak JWT validation)
- 다요소 인증 (MFA) 누락
그들의 목표가 항상 비밀번호를 해킹하는 것은 아닙니다.
때로는 유효한 세션 쿠키 (session cookie)를 훔치는 것이 훨씬 더 쉬울 수도 있습니다.
인증이 실패하면, 그 뒤에 있는 모든 것이 취약해집니다.
5단계: 권한 부여 (Authorization) 악용
인증 (Authentication)은 다음 질문에 답합니다:
당신은 누구입니까?
권한 부여 (Authorization)는 다음 질문에 답합니다:
당신은 무엇을 할 수 있습니까?
많은 애플리케이션이 첫 번째 부분은 제대로 처리하지만, 두 번째 부분은 틀리곤 합니다.
다음 API를 고려해 보세요:
GET /api/users/123/profile
공격자가 이를 다음과 같이 변경합니다:
GET /api/users/124/profile
만약 서버가 다른 사용자의 프로필을 반환한다면, 당신은 방금 민감한 데이터를 노출한 것입니다.
이러한 유형의 취약점은 객체 수준 권한 부여 결함 (Broken Object Level Authorization, BOLA) 또는 **부적절한 직접 객체 참조 (Insecure Direct Object Reference, IDOR)**로 알려져 있습니다.
이는 가장 흔한 API 보안 문제 중 하나입니다.
해결책은 ID를 숨기는 것이 아닙니다.
해결책은 모든 요청에서 소유권을 검증하는 것입니다.
6단계: UI 너머를 바라보기
개발자들은 사용자 인터페이스 (UI)를 다듬는 데 수개월을 소비합니다.
공격자들은 대개 이를 무시합니다.
대신, 그들은 네트워크 트래픽을 조사합니다.
그들은 API를 직접 호출합니다.
그들은 요청을 수정합니다.
그들은 스크립트를 사용하여 수천 개의 요청을 자동화합니다.
현대적인 애플리케이션은 API를 통해 기능을 노출하므로, 백엔드 (backend)가 실제 공격 표면 (attack surface)이 됩니다.
이것이 서버 측 검증 (server-side validation)이 필수적인 이유입니다.
프론트엔드 (frontend)에서만 강제되는 모든 것은 우회될 수 있습니다.
7단계: 작은 약점들을 사슬처럼 엮기 (Chaining)
단 하나의 취약점이 치명적인 침해로 이어지는 경우는 드뭅니다.
대신, 공격자들은 여러 개의 작은 문제들을 결합합니다.
다음 시퀀스를 상상해 보세요:
취약한 비밀번호
↓
...
각각의 개별적인 문제는 사소해 보일 수 있습니다.
하지만 그것들이 합쳐지면 파괴적으로 변합니다.
보안 전문가들은 이를 종종 **공격 체인 (attack chain)**이라고 부릅니다.
체인의 연결 고리 중 어느 하나라도 끊어내면 공격을 막을 수 있습니다.
8단계: 설정 오류 (Misconfigurations) 타겟팅
할리우드는 제로데이 (zero-day) 취약점 악용을 좋아합니다.
현실은 훨씬 덜 화려합니다.
많은 성공적인 공격은 단순한 설정 실수 때문에 발생합니다.
예시는 다음과 같습니다:
- 퍼블릭 클라우드 스토리지 버킷 (Public cloud storage buckets)
- 기본 관리자 자격 증명 (Default administrator credentials)
- 프로덕션 환경에서의 디버그 모드 활성화 (Debug mode enabled in production)
- 개방된 데이터베이스 (Open databases)
- 과도하게 허용된 IAM 역할 (Overly permissive IAM roles)
- Git에 커밋된 비밀 정보 (Secrets committed to Git)
- 패치되지 않은 종속성 (Unpatched dependencies)
- 노출된 관리 콘솔 (Exposed management consoles)
이러한 실수들은 정교한 취약점보다 훨씬 더 흔하게 발생합니다.
단계 9: 클라우드 공격 (Attacking the Cloud)
애플리케이션은 더 이상 단일 서버에서만 실행되지 않습니다.
오늘날의 인프라는 다음과 같은 것들을 포함합니다:
- Kubernetes
- Docker
- AWS
- Azure
- Google Cloud
- CI/CD 파이프라인 (CI/CD pipelines)
- 서버리스 함수 (Serverless functions)
공격자들은 운영 체제(OS) 대신 클라우드 ID(Identity)에 점점 더 집중하고 있습니다.
전형적인 공격 대상은 다음과 같습니다:
- IAM 역할 (IAM roles)
- 임시 자격 증명 (Temporary credentials)
- 메타데이터 서비스 (Metadata services)
- Kubernetes 비밀 정보 (Kubernetes secrets)
- 서비스 계정 (Service accounts)
- API 키 (API keys)
ID(Identity)가 새로운 보안 경계(Security perimeter)가 되었습니다.
단계 10: 소프트웨어 공급망 침해 (Compromising the Software Supply Chain)
여러분의 애플리케이션은 환경의 단 한 부분일 뿐입니다.
공격자들은 점점 더 다음을 목표로 삼습니다:
- 오픈 소스 패키지 (Open-source packages)
- CI/CD 파이프라인 (CI/CD pipelines)
- 빌드 서버 (Build servers)
- 패키지 저장소 (Package repositories)
- 제3자 라이브러리 (Third-party libraries)
침해된 종속성은 수천 개의 다운스트림 애플리케이션에 영향을 미칠 수 있습니다.
개발자는 다음과 같은 조치를 취해야 합니다:
- 정기적인 종속성 업데이트 (Regularly update dependencies)
- 패키지 출처 검증 (Verify package sources)
- 종속성 스캐닝 활성화 (Enable dependency scanning)
- 빌드 파이프라인 검토 (Review build pipelines)
- 소프트웨어 자재 명세서 (SBOMs) 사용
공급망 보안은 이제 소프트웨어 엔지니어링의 핵심 부분입니다.
단계 11: 인간 행동 악용 (Exploiting Human Behavior)
가장 정교한 방화벽이라도 누군가가 자발적으로 자신의 자격 증명을 넘겨주는 것을 막을 수는 없습니다.
공격자들은 빈번하게 다음을 사용합니다:
- 피싱 이메일 (Phishing emails)
- 가짜 로그인 페이지 (Fake login pages)
- 사회 공학 (Social engineering)
- 가짜 채용 담당자 (Fake recruiters)
- SMS 사기 (SMS scams)
- 보이스 피싱 (Voice phishing)
- MFA 피로 공격 (MFA fatigue attacks)
인간은 여전히 가장 많이 표적이 되는 공격 벡터(Attack vector) 중 하나입니다.
보안 의식은 안전한 코드만큼이나 중요합니다.
단계 12: 지속성 유지 (Maintaining Persistence)
접근 권한을 얻은 후, 공격자들은 종종 환경 내부에 머물려고 시도합니다.
일반적인 지속성 유지 기법에는 다음과 같은 것들이 포함됩니다:
- 숨겨진 계정 생성 (Creating hidden accounts)
- SSH 키 설치 (Installing SSH keys)
- 예약된 작업 생성 (Creating scheduled tasks)
- 새로운 API 토큰 등록 (Registering new API tokens)
- 백도어 배포 (Deploying backdoors)
- OAuth 애플리케이션 생성 (Creating OAuth applications)
공격자가 탐지되지 않은 채 머무는 시간이 길어질수록, 더 큰 피해를 입힐 수 있습니다.
13단계: 흔적 지우기 (Covering Their Tracks)
전문적인 공격자들은 방어자가 자신들이 침입했다는 사실을 알기를 원하지 않습니다.
전형적인 활동에는 다음과 같은 것들이 포함됩니다:
- 로그 삭제 (Deleting logs)
- 셸 히스토리 삭제 (Clearing shell history)
- 타임스탬프 수정 (Modifying timestamps)
- 모니터링 비활성화 (Disabling monitoring)
- 정상적인 트래픽에 섞이기 (Blending into legitimate traffic)
- 포렌식 증거 제거 (Removing forensic evidence)
훌륭한 로깅 (Logging)과 중앙 집중식 모니터링 (Centralized monitoring)은 이러한 활동을 현저히 어렵게 만듭니다.
개발자가 생각해야 하는 방식
다음과 같이 질문하는 대신:
"이 기능이 작동하는가?"
이렇게 질문하십시오:
- 내가 하고 있는 가정은 무엇인가?
- 모든 입력값이 악의적이라면 어떤 일이 발생하는가?
- 사용자가 접근해서는 안 되는 데이터에 접근할 수 있는가?
- 세션 토큰 (Session token)이 유출된다면 어떻게 되는가?
- 공격자가 클라이언트 (Client)를 제어한다면 어떻게 되는가?
- 하나의 서비스가 침해된다면 어떤 일이 발생하는가?
- 폭발 반경 (Blast radius)은 어느 정도인가?
보안은 본질적으로 가정을 의심하는 것에 관한 것입니다.
실전 보안 체크리스트
애플리케이션을 배포하기 전에 스스로에게 질문하십시오:
인증 (Authentication)
- 권한이 있는 사용자에게 MFA (다요소 인증)가 활성화되어 있는가?
- 비밀번호가 안전하게 해싱 (Hashed)되어 있는가?
- 로그인 시도에 대해 속도 제한 (Rate-limited)이 적용되어 있는가?
- 로그아웃 시 세션이 무효화되는가?
인가 (Authorization)
- 모든 요청이 서버 측 (Server-side)에서 인가되는가?
- 객체 소유권 확인 (Object ownership checks)이 구현되어 있는가?
- 관리자 엔드포인트 (Admin endpoints)가 보호되고 있는가?
입력값 검증 (Input Validation)
- 모든 입력값이 검증되는가?
- 업로드된 파일이 스캔되는가?
- 크기 제한이 강제되는가?
- 위험한 파일 유형이 차단되는가?
비밀 정보 (Secrets)
- 비밀 정보가 소스 코드 외부에 저장되어 있는가?
- API 키가 정기적으로 교체 (Rotated)되는가?
- 자격 증명 (Credentials)이 암호화되어 있는가?
인프라 (Infrastructure)
- 불필요한 포트가 닫혀 있는가?
- 종속성 (Dependencies)이 업데이트되어 있는가?
- 디버그 모드 (Debug mode)가 비활성화되어 있는가?
- 클라우드 권한이 최소 권한 원칙 (Least privilege)에 기반하고 있는가?
모니터링 (Monitoring)
- 보안 이벤트가 기록(Log)되고 있는가?
- 로그가 중앙 집중화(Centralized)되어 있는가?
- 의심스러운 동작에 대한 알림(Alert)이 설정되어 있는가?
- 사고 대응(Incident response) 절차가 문서화되어 있는가?
가장 큰 교훈 (The Biggest Lesson)
최고의 보안 엔지니어는 개발자처럼 생각하지 않습니다.
그들은 호기심 많고 끈질긴 공격자(Adversary)처럼 생각합니다.
그들은 다음과 같이 가정합니다:
- 모든 입력값은 조작될 수 있다.
- 모든 엔드포인트(Endpoint)는 테스트될 것이다.
- 모든 가정은 도전받을 것이다.
- 모든 지름길(Shortcut)은 결국 발견될 것이다.
이러한 가정들을 염두에 두고 소프트웨어를 구축한다면, 자연스럽게 훨씬 더 탄력적인(Resilient) 시스템을 만들 수 있을 것입니다.
마치며 (Final Thoughts)
보안은 편집증을 갖는 것이 아닙니다.
준비되어 있는 것입니다.
공격자는 해피 패스(Happy path)를 따르지 않습니다. 그들은 엣지 케이스(Edge cases), 예상치 못한 입력값, 잊혀진 설정, 그리고 잘못된 신뢰를 탐색합니다. 설계, 개발 및 테스트 단계에서 이러한 관점을 채택함으로써, 개발자는 취약점이 실제 사고(Incident)로 이어지기 훨씬 전에 식별할 수 있습니다.
하룻밤 사이에 화이트햇 해커(Ethical hacker)가 될 필요는 없습니다. 기능을 만들 때마다 다음과 같은 단순한 질문 하나를 던지는 것부터 시작하세요:
"만약 내가 이것을 망가뜨리고 싶다면, 어디서부터 시작할 것인가?"
이 단 하나의 질문이 소프트웨어를 설계하는 방식을 변화시키고, 여러분의 애플리케이션을 훨씬 더 안전하게 만들 수 있습니다.
더 읽어보기 (Further Reading)
- OWASP Top 10
- OWASP API Security Top 10
- MITRE ATT&CK Framework
- NIST Secure Software Development Framework (SSDF)
- CIS Critical Security Controls
이 글이 도움이 되었다면, 소프트웨어 엔지니어링, DevSecOps, 클라우드 아키텍처, 백엔드 시스템, 그리고 AI 기반 개발에 대한 더 깊이 있는 분석을 위해 저를 팔로우해 주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기