OPEN HYPER STEP
← 목록으로 (화이트햇 보안)
SECURITY · 75 / 84
security
CHAPTER 75 / 84
읽기 약 2
FUNCTION

이상 패턴 탐지: 브루트포스


핵심 개념

같은 IP 반복 실패 로그인을 시간 윈도우 기반으로 탐지하고 자동 차단한다.

본문

브루트포스의 흔적

📋 코드 (5줄)
# /var/log/auth.log — SSH 브루트포스
Apr 27 10:23:11 server sshd[1234]: Failed password for invalid user admin from 198.51.100.99 port 22
Apr 27 10:23:13 server sshd[1235]: Failed password for invalid user root from 198.51.100.99 port 22
Apr 27 10:23:15 server sshd[1236]: Failed password for invalid user test from 198.51.100.99 port 22
... (수백~수천 라인)

특징:

  • 같은 IP 반복
  • 다양한 사용자명 시도
  • 짧은 시간 간격 (1~5초)
  • 자정~새벽 시간대 집중

슬라이딩 윈도우 카운팅

PYTHON📋 코드 (70줄)
# ⚠️ 이 코드는 허가된 환경에서만 사용하세요.
import re
from datetime import datetime, timedelta
from collections import defaultdict, deque

# auth.log SSH 실패 패턴
SSH_FAILED = re.compile(
    r'^(?P<month>\w{3})\s+(?P<day>\d+)\s+'
    r'(?P<time>\d{2}:\d{2}:\d{2})\s+'
    r'\S+\s+sshd\[\d+\]:\s+'
    r'Failed password\s+'
    r'(?:for\s+(?:invalid user\s+)?(?P<user>\S+)\s+)?'
    r'from\s+(?P<ip>\S+)\s+port\s+\d+'
)


def parse_auth_time(month: str, day: str, time_str: str, year: int = 2026) -> datetime:
    """auth.log 시간을 datetime으로 (연도는 추정)."""
    return datetime.strptime(f'{year} {month} {day} {time_str}', '%Y %b %d %H:%M:%S')


def detect_bruteforce(
    log_path: str,
    threshold: int = 10,        # 5분 안에 10회 실패
    window_minutes: int = 5,
) -> dict:
    """슬라이딩 윈도우 기반 브루트포스 탐지."""
    # IP별 실패 시각 큐
    failures: dict[str, deque] = defaultdict(deque)
    detected = {}  # IP → {'count': N, 'first': time, 'last': time, 'users': set()}
    window = timedelta(minutes=window_minutes)

    with open(log_path, encoding='utf-8', errors='replace') as f:
        for line in f:
            m = SSH_FAILED.search(line)
            if not m:
                continue

            try:
                ts = parse_auth_time(m['month'], m['day'], m['time'])
            except ValueError:
                continue

            ip = m['ip']
            user = m['user'] or 'unknown'

            # 윈도우 밖의 오래된 기록 제거
            q = failures[ip]
            while q and q[0][0] < ts - window:
                q.popleft()
            q.append((ts, user))

            # 임계값 초과 → 탐지
            if len(q) >= threshold:
                if ip not in detected:
                    detected[ip] = {
                        'count': len(q),
                        'first_seen': q[0][0].isoformat(),
                        'last_seen': ts.isoformat(),
                        'users_tried': set(),
                    }
                detected[ip]['count'] = len(q)
                detected[ip]['last_seen'] = ts.isoformat()
                detected[ip]['users_tried'].update(u for _, u in q)

    # set → list로 (JSON 직렬화)
    for d in detected.values():
        d['users_tried'] = sorted(d['users_tried'])[:20]  # 최대 20개

    return detected

자동 차단 (fail2ban 흉내)

PYTHON📋 코드 (26줄)
# 탐지된 IP를 iptables로 차단 (sudo 권한 필요)
import subprocess

def block_ip(ip: str, reason: str = 'bruteforce') -> bool:
    """iptables로 IP 차단."""
    try:
        subprocess.run([
            'sudo', 'iptables', '-A', 'INPUT',
            '-s', ip, '-j', 'DROP',
            '-m', 'comment', '--comment', f'auto-block:{reason}',
        ], check=True, timeout=5)
        log.warning(f'⛔ 차단됨: {ip} ({reason})')
        return True
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
        log.error(f'차단 실패 {ip}: {e}')
        return False


def block_detected(detected: dict, min_count: int = 50):
    """50회 이상 실패한 IP만 차단 — 오탐 방지."""
    blocked = []
    for ip, info in detected.items():
        if info['count'] >= min_count:
            if block_ip(ip, f'bruteforce-{info["count"]}'):
                blocked.append(ip)
    return blocked

화이트리스트 — 오탐 방지

PYTHON📋 코드 (22줄)
import ipaddress

# 신뢰 IP는 절대 차단 안 함
WHITELIST = [
    ipaddress.ip_network('192.168.0.0/16'),  # 사내망
    ipaddress.ip_network('127.0.0.0/8'),     # 로컬
    ipaddress.ip_address('203.0.113.10'),    # 본사 외부 IP
]


def is_whitelisted(ip_str: str) -> bool:
    try:
        ip = ipaddress.ip_address(ip_str)
        for entry in WHITELIST:
            if isinstance(entry, ipaddress.IPv4Network):
                if ip in entry:
                    return True
            elif ip == entry:
                return True
    except ValueError:
        return False
    return False

실시간 모니터링

PYTHON📋 코드 (36줄)
import time

def tail_and_detect(log_path: str, threshold: int = 10):
    """tail -f 처럼 실시간 모니터링."""
    failures: dict[str, deque] = defaultdict(deque)
    window = timedelta(minutes=5)

    with open(log_path, encoding='utf-8', errors='replace') as f:
        f.seek(0, 2)  # 끝으로 이동
        while True:
            line = f.readline()
            if not line:
                time.sleep(0.5)
                continue

            m = SSH_FAILED.search(line)
            if not m:
                continue

            ip = m['ip']
            if is_whitelisted(ip):
                continue

            try:
                ts = parse_auth_time(m['month'], m['day'], m['time'])
            except ValueError:
                continue

            q = failures[ip]
            while q and q[0] < ts - window:
                q.popleft()
            q.append(ts)

            if len(q) == threshold:  # 임계 도달 순간만
                log.warning(f'🚨 브루트포스 감지: {ip} ({len(q)}회/5분)')
                # block_ip(ip, 'bruteforce')

⚠️ 운영 주의사항

  • 차단 전 logging만 + 사람 확인 권장 (자동 차단 = 자기 차단 위험)
  • NAT 환경: 100명이 같은 공용 IP를 공유 — 1명의 실수로 100명 차단
  • 실제 운영: fail2ban, CrowdSec, Cloudflare WAF 활용

AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 브루트포스 탐지 코드의
슬라이딩 윈도우·화이트리스트·차단 로직을
오탐 측면에서 분석하고 개선해줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

fail2ban / CrowdSec / Cloudflare WAF의
자체 Python 구현 대비 장단점을
비용·정확도·운영 측면에서 비교해줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 auth.log 한 달치를 분석해서
브루트포스 IP·시간대 패턴·공격 빈도를
시각화 가능한 종합 리포트로 만들어줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 SSH 보안 트렌드 —
fail2ban vs SSH key only vs Cloudflare Tunnel
1인 SaaS의 가성비 솔직히 알려줘.

⭐ 이것만 기억하세요
이상 패턴 탐지: 브루트포스 이 3가지만 확실히 잡으세요
1.슬라이딩 윈도우(5분 내 10회+)로 브루트포스를 정확히 탐지하면서 정상 사용자 오탐을 막을 수 있다
2.IP 차단은 화이트리스트 + 임계값 + 사람 검증 3단 안전망이 필수 — 자동 차단의 위험을 줄인다
3.다음 챕터에서 SQL Injection·XSS·경로 탐색 같은 웹 공격 패턴을 탐지한다


공유하기
진행도 75 / 84