์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ” ๋กœ๊ทธ ํŒŒ์ผ ๋ถ„์„ ๋ฐ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€ ์†Œ์Šค์ฝ”๋“œ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๐Ÿ” ๋กœ๊ทธ ํŒŒ์ผ ๋ถ„์„ ๋ฐ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€ ์†Œ์Šค์ฝ”๋“œ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

์‹ค์ „์—์„œ ๋ฐ”๋กœ ์“ฐ๋Š” ๋ณด์•ˆ ๋กœ๊ทธ ๋ถ„์„ ๊ธฐ๋ฒ•๊ณผ ์ฝ”๋“œ ์˜ˆ์ œ

Server Logs Analysis Pattern Match Anomaly Detect Threat Hunt Alert! Security Event Detected

๐ŸŽฏ ๋กœ๊ทธ ๋ถ„์„์ด ์™œ ์ค‘์š”ํ• ๊นŒ?

์•ˆ๋…•! ์˜ค๋Š˜์€ ์ •๋ง ์‹ค๋ฌด์—์„œ ๊ผญ ํ•„์š”ํ•œ ์ฃผ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์™”์–ด. ๋ฐ”๋กœ ๋กœ๊ทธ ํŒŒ์ผ ๋ถ„์„๊ณผ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€์— ๊ด€ํ•œ ์ด์•ผ๊ธฐ์•ผ. ๐Ÿ˜Š

์š”์ฆ˜ IT ์‹œ์Šคํ…œ์„ ์šด์˜ํ•˜๋‹ค ๋ณด๋ฉด ๋งค์ผ๊ฐ™์ด ์—„์ฒญ๋‚œ ์–‘์˜ ๋กœ๊ทธ๊ฐ€ ์Œ“์ด์ž–์•„? ์›น ์„œ๋ฒ„ ๋กœ๊ทธ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ, ์‹œ์Šคํ…œ ๋กœ๊ทธ, ๋ฐฉํ™”๋ฒฝ ๋กœ๊ทธ ๋“ฑ๋“ฑ... ์ด ๋กœ๊ทธ๋“ค์€ ๊ทธ๋ƒฅ ๋””์Šคํฌ ๊ณต๊ฐ„๋งŒ ์ฐจ์ง€ํ•˜๋Š” ์“ธ๋ชจ์—†๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ์•ผ. ์˜คํžˆ๋ ค ์‹œ์Šคํ…œ์˜ ๊ฑด๊ฐ• ์ƒํƒœ๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๋ฐ”์ดํƒˆ ์‚ฌ์ธ์ด๊ณ , ๋ณด์•ˆ ์นจํ•ด ์‚ฌ๊ณ ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฒ”์ธ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ๊ฒฐ์ •์ ์ธ ์ฆ๊ฑฐ๊ฐ€ ๋˜์ง€! ๐Ÿ”

์‹ค์ œ๋กœ ๋งŽ์€ ๋ณด์•ˆ ์‚ฌ๊ณ ๋“ค์ด ๋ฐœ์ƒํ•œ ํ›„ ๋ช‡ ์ฃผ, ์‹ฌ์ง€์–ด ๋ช‡ ๋‹ฌ์ด ์ง€๋‚˜์„œ์•ผ ๋ฐœ๊ฒฌ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„. ์™œ ๊ทธ๋Ÿด๊นŒ? ๋ฐ”๋กœ ๋กœ๊ทธ๋ฅผ ์ œ๋Œ€๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด์•ผ. ๊ณต๊ฒฉ์ž๋“ค์€ ์ด๋ฏธ ์‹œ์Šคํ…œ์— ์นจํˆฌํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋นผ๊ฐ€๊ณ  ์žˆ๋Š”๋ฐ, ์šฐ๋ฆฌ๋Š” ๊ทธ ์‚ฌ์‹ค์กฐ์ฐจ ๋ชจ๋ฅด๊ณ  ์žˆ๋Š” ๊ฑฐ์ง€. ๐Ÿ˜ฑ

๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋กœ๊ทธ ๋ถ„์„ ์†Œ์Šค์ฝ”๋“œ์™€ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€ ๊ธฐ๋ฒ•๋“ค์„ ์นœ๊ตฌ์ฒ˜๋Ÿผ ํŽธํ•˜๊ฒŒ ์„ค๋ช…ํ•ด์ค„๊ฒŒ!
๐Ÿ’ก ์ด ๊ธ€์—์„œ ๋ฐฐ์šธ ๋‚ด์šฉ

โœ… ๋กœ๊ทธ ํŒŒ์ผ์˜ ๊ตฌ์กฐ์™€ ์ข…๋ฅ˜ ์ดํ•ดํ•˜๊ธฐ
โœ… Python์„ ํ™œ์šฉํ•œ ๋กœ๊ทธ ํŒŒ์‹ฑ ๊ธฐ๋ฒ•
โœ… ์ •๊ทœํ‘œํ˜„์‹์„ ์ด์šฉํ•œ ํŒจํ„ด ๋งค์นญ
โœ… ์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง ๊ตฌํ˜„
โœ… ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€ ์•Œ๊ณ ๋ฆฌ์ฆ˜
โœ… ์ด์ƒ ์ง•ํ›„ ํƒ์ง€(Anomaly Detection)
โœ… ์‹ค์ „ ์˜ˆ์ œ ์ฝ”๋“œ์™€ ํ™œ์šฉ๋ฒ•

๐Ÿ“‹ ๋กœ๊ทธ ํŒŒ์ผ์˜ ์ข…๋ฅ˜์™€ ๊ตฌ์กฐ

๋จผ์ € ์šฐ๋ฆฌ๊ฐ€ ๋‹ค๋ฃฐ ๋กœ๊ทธ ํŒŒ์ผ๋“ค์ด ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ? ๊ฐ ๋กœ๊ทธ๋งˆ๋‹ค ํ˜•์‹๋„ ๋‹ค๋ฅด๊ณ  ๋‹ด๊ณ  ์žˆ๋Š” ์ •๋ณด๋„ ๋‹ฌ๋ผ์„œ, ์ด๊ฑธ ์ดํ•ดํ•˜๋Š” ๊ฒŒ ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„์•ผ! ๐Ÿ“š

1. ์›น ์„œ๋ฒ„ ๋กœ๊ทธ (Apache/Nginx)

์›น ์„œ๋ฒ„ ๋กœ๊ทธ๋Š” ๊ฐ€์žฅ ํ”ํ•˜๊ฒŒ ์ ‘ํ•˜๋Š” ๋กœ๊ทธ ์ค‘ ํ•˜๋‚˜์•ผ. ๋ˆ„๊ฐ€ ์–ธ์ œ ์–ด๋–ค ํŽ˜์ด์ง€์— ์ ‘์†ํ–ˆ๋Š”์ง€, ์–ด๋–ค ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”์ง€ ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ ์žˆ์ง€.

192.168.1.100 - - [15/Jan/2024:10:30:45 +0900] "GET /admin/login.php HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
10.0.0.50 - admin [15/Jan/2024:10:31:12 +0900] "POST /api/users HTTP/1.1" 403 567 "-" "curl/7.68.0"
172.16.0.25 - - [15/Jan/2024:10:31:45 +0900] "GET /../../../etc/passwd HTTP/1.1" 404 162 "-" "Nikto"
๋ณด์ด์ง€? ์„ธ ๋ฒˆ์งธ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด ๋ญ”๊ฐ€ ์ˆ˜์ƒํ•ด ๋ณด์ด์ง€ ์•Š์•„? ๐Ÿ˜ /../../../etc/passwd๋ฅผ ์š”์ฒญํ•˜๊ณ  ์žˆ์–ด. ์ด๊ฑด ์ „ํ˜•์ ์ธ Path Traversal ๊ณต๊ฒฉ ์‹œ๋„์•ผ!

2. ์‹œ์Šคํ…œ ๋กœ๊ทธ (Syslog)

๋ฆฌ๋ˆ…์Šค ์‹œ์Šคํ…œ์—์„œ๋Š” /var/log ๋””๋ ‰ํ† ๋ฆฌ์— ๋‹ค์–‘ํ•œ ์‹œ์Šคํ…œ ๋กœ๊ทธ๊ฐ€ ์ €์žฅ๋ผ. auth.log๋Š” ์ธ์ฆ ๊ด€๋ จ, syslog๋Š” ์‹œ์Šคํ…œ ์ „๋ฐ˜์ ์ธ ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋กํ•˜์ง€.

Jan 15 10:25:30 webserver sshd[12345]: Failed password for root from 203.0.113.50 port 45678 ssh2
Jan 15 10:25:35 webserver sshd[12346]: Failed password for root from 203.0.113.50 port 45679 ssh2
Jan 15 10:25:40 webserver sshd[12347]: Failed password for admin from 203.0.113.50 port 45680 ssh2
์ด๊ฒƒ๋„ ์ˆ˜์ƒํ•˜์ง€? ๊ฐ™์€ IP์—์„œ ์งง์€ ์‹œ๊ฐ„ ๋™์•ˆ ์—ฌ๋Ÿฌ ๋ฒˆ ๋กœ๊ทธ์ธ ์‹คํŒจ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์–ด. ์ „ํ˜•์ ์ธ Brute Force ๊ณต๊ฒฉ์ด์•ผ! ๐Ÿšจ

3. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ

์šฐ๋ฆฌ๊ฐ€ ๊ฐœ๋ฐœํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” ๋กœ๊ทธ๋“ค์ด์•ผ. ๋ณดํ†ต JSON ํ˜•์‹์ด๋‚˜ ๊ตฌ์กฐํ™”๋œ ํ˜•ํƒœ๋กœ ์ €์žฅํ•˜๋Š” ๊ฒŒ ๋ถ„์„ํ•˜๊ธฐ ์ข‹์•„.

{
  "timestamp": "2024-01-15T10:30:45Z",
  "level": "ERROR",
  "user_id": "user123",
  "action": "database_query",
  "query": "SELECT * FROM users WHERE id=1 OR 1=1--",
  "ip_address": "192.168.1.100"
}
์ด ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ฉด... OR 1=1--์ด ๋ณด์ด์ง€? ๋งž์•„, SQL Injection ๊ณต๊ฒฉ ์‹œ๋„์•ผ! ๐Ÿ˜ฑ

๐Ÿ Python์œผ๋กœ ๋กœ๊ทธ ํŒŒ์ผ ํŒŒ์‹ฑํ•˜๊ธฐ

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณผ๊นŒ? Python์€ ๋กœ๊ทธ ๋ถ„์„์— ์ •๋ง ์ตœ์ ํ™”๋œ ์–ธ์–ด์•ผ. ๊ฐ•๋ ฅํ•œ ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ ๋Šฅ๋ ฅ๊ณผ ๋‹ค์–‘ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋•๋ถ„์ด์ง€! ๐Ÿ’ช

๊ธฐ๋ณธ ๋กœ๊ทธ ํŒŒ์„œ ๊ตฌํ˜„

๋จผ์ € ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋กœ๊ทธ ํŒŒ์„œ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ณด์ž. ์›น ์„œ๋ฒ„ ๋กœ๊ทธ๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ์ฝ”๋“œ์•ผ.

import re
from datetime import datetime
from collections import defaultdict

class LogParser:
    def __init__(self):
        # Apache/Nginx Combined Log Format ์ •๊ทœํ‘œํ˜„์‹
        self.log_pattern = re.compile(
            r'(?P<ip>[\d.]+) - (?P<user>[\w-]+) '
            r'\[(?P<timestamp>[^\]]+)\] '
            r'"(?P<method>\w+) (?P<path>[^\s]+) (?P<protocol>[^"]+)" '
            r'(?P<status>\d+) (?P<size>\d+) '
            r'"(?P<referer>[^"]*)" "(?P<user_agent>[^"]*)"'
        )
        
    def parse_line(self, line):
        """ํ•œ ์ค„์˜ ๋กœ๊ทธ๋ฅผ ํŒŒ์‹ฑํ•˜์—ฌ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๋ฐ˜ํ™˜"""
        match = self.log_pattern.match(line)
        if match:
            return match.groupdict()
        return None
    
    def parse_file(self, filepath):
        """๋กœ๊ทธ ํŒŒ์ผ ์ „์ฒด๋ฅผ ํŒŒ์‹ฑ"""
        parsed_logs = []
        
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                for line_num, line in enumerate(f, 1):
                    parsed = self.parse_line(line.strip())
                    if parsed:
                        parsed['line_number'] = line_num
                        parsed_logs.append(parsed)
                    else:
                        print(f"โš ๏ธ ํŒŒ์‹ฑ ์‹คํŒจ (๋ผ์ธ {line_num}): {line[:50]}...")
        
        except FileNotFoundError:
            print(f"โŒ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {filepath}")
        except Exception as e:
            print(f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
        
        return parsed_logs

# ์‚ฌ์šฉ ์˜ˆ์ œ
parser = LogParser()
logs = parser.parse_file('/var/log/apache2/access.log')

print(f"โœ… ์ด {len(logs)}๊ฐœ์˜ ๋กœ๊ทธ ์—”ํŠธ๋ฆฌ๋ฅผ ํŒŒ์‹ฑํ–ˆ์Šต๋‹ˆ๋‹ค!")
if logs:
    print(f"์ฒซ ๋ฒˆ์งธ ๋กœ๊ทธ: {logs[0]}")
์ด ์ฝ”๋“œ์˜ ํ•ต์‹ฌ์€ ์ •๊ทœํ‘œํ˜„์‹(Regular Expression)์ด์•ผ. ์ •๊ทœํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋กœ๊ทธ ํ˜•์‹๋„ ์‰ฝ๊ฒŒ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ˜Ž

์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง

๋กœ๊ทธ ํŒŒ์ผ์€ ๊ณ„์† ์—…๋ฐ์ดํŠธ๋˜์ž–์•„? ๊ทธ๋ž˜์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ƒˆ๋กœ์šด ๋กœ๊ทธ๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•ด. ์ด๊ฑธ ๊ตฌํ˜„ํ•ด๋ณด์ž!

import time
import os

class RealTimeLogMonitor:
    def __init__(self, filepath, callback):
        self.filepath = filepath
        self.callback = callback
        self.parser = LogParser()
        
    def follow(self):
        """tail -f ์ฒ˜๋Ÿผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋กœ๊ทธ ์ถ”์ """
        print(f"๐Ÿ” {self.filepath} ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘...")
        
        # ํŒŒ์ผ์˜ ๋์œผ๋กœ ์ด๋™
        with open(self.filepath, 'r') as f:
            f.seek(0, os.SEEK_END)
            
            while True:
                line = f.readline()
                
                if not line:
                    time.sleep(0.1)  # ์ƒˆ ๋กœ๊ทธ ๋Œ€๊ธฐ
                    continue
                
                # ๋กœ๊ทธ ํŒŒ์‹ฑ ๋ฐ ์ฝœ๋ฐฑ ์‹คํ–‰
                parsed = self.parser.parse_line(line.strip())
                if parsed:
                    self.callback(parsed)

def security_event_handler(log_entry):
    """๋ณด์•ˆ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์ฝœ๋ฐฑ ํ•จ์ˆ˜"""
    ip = log_entry['ip']
    path = log_entry['path']
    status = int(log_entry['status'])
    
    # ์˜์‹ฌ์Šค๋Ÿฌ์šด ํŒจํ„ด ํƒ์ง€
    suspicious_patterns = [
        r'\.\./\.\./',  # Path Traversal
        r'union.*select',  # SQL Injection
        r'<script>',  # XSS
        r'/etc/passwd',  # ์‹œ์Šคํ…œ ํŒŒ์ผ ์ ‘๊ทผ
        r'cmd\.exe',  # ๋ช…๋ น์–ด ์‹คํ–‰
    ]
    
    for pattern in suspicious_patterns:
        if re.search(pattern, path, re.IGNORECASE):
            print(f"๐Ÿšจ ๋ณด์•ˆ ์œ„ํ˜‘ ํƒ์ง€!")
            print(f"   IP: {ip}")
            print(f"   ๊ฒฝ๋กœ: {path}")
            print(f"   ํŒจํ„ด: {pattern}")
            print(f"   ์ƒํƒœ ์ฝ”๋“œ: {status}")
            print("-" * 50)
            
            # ์—ฌ๊ธฐ์„œ ์•Œ๋ฆผ ์ „์†ก, DB ์ €์žฅ ๋“ฑ์˜ ์ž‘์—… ์ˆ˜ํ–‰
            send_alert(log_entry)

def send_alert(log_entry):
    """์•Œ๋ฆผ ์ „์†ก (์ด๋ฉ”์ผ, Slack ๋“ฑ)"""
    # ์‹ค์ œ ๊ตฌํ˜„์—์„œ๋Š” ์ด๋ฉ”์ผ์ด๋‚˜ Slack ์›นํ›… ๋“ฑ์„ ์‚ฌ์šฉ
    print(f"๐Ÿ“ง ๊ด€๋ฆฌ์ž์—๊ฒŒ ์•Œ๋ฆผ ์ „์†ก: {log_entry['ip']} ์˜์‹ฌ ํ™œ๋™ ํƒ์ง€")

# ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘
monitor = RealTimeLogMonitor('/var/log/apache2/access.log', security_event_handler)
# monitor.follow()  # ์‹ค์ œ ์‹คํ–‰ ์‹œ ์ฃผ์„ ํ•ด์ œ
์ด ์ฝ”๋“œ๋Š” ๋งˆ์น˜ tail -f ๋ช…๋ น์–ด์ฒ˜๋Ÿผ ๋กœ๊ทธ ํŒŒ์ผ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถ”์ ํ•ด. ์ƒˆ๋กœ์šด ๋กœ๊ทธ๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด ์ฆ‰์‹œ ํŒŒ์‹ฑํ•˜๊ณ , ์˜์‹ฌ์Šค๋Ÿฌ์šด ํŒจํ„ด์ด ๋ฐœ๊ฒฌ๋˜๋ฉด ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๊ฑฐ์ง€! ๐Ÿ””
๐Ÿ’ก Pro Tip

์‹ค๋ฌด์—์„œ๋Š” ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๋กœ๊ทธ ๋ถ„์„ ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„ ์ปจ์„คํŒ…์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์˜ ๋กœ๊ทธ ๋ถ„์„ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ๋Š” ๊ฒฝํ—˜ ๋งŽ์€ ์ „๋ฌธ๊ฐ€์˜ ์กฐ์–ธ์ด ์ •๋ง ๋„์›€์ด ๋˜๊ฑฐ๋“ ! ๐Ÿ˜Š

๐ŸŽฏ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€ ์•Œ๊ณ ๋ฆฌ์ฆ˜

์ด์ œ ์ข€ ๋” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์œผ๋กœ ๋„˜์–ด๊ฐ€๋ณผ๊นŒ? ๋‹จ์ˆœํžˆ ํŒจํ„ด ๋งค์นญ๋งŒ์œผ๋กœ๋Š” ๋ชจ๋“  ๊ณต๊ฒฉ์„ ํƒ์ง€ํ•  ์ˆ˜ ์—†์–ด. ๋” ๋˜‘๋˜‘ํ•œ ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜์ง€! ๐Ÿง 

1. ๋นˆ๋„ ๊ธฐ๋ฐ˜ ํƒ์ง€ (Frequency-based Detection)

๊ฐ™์€ IP์—์„œ ์งง์€ ์‹œ๊ฐ„ ๋™์•ˆ ๋„ˆ๋ฌด ๋งŽ์€ ์š”์ฒญ์ด ์˜ค๋ฉด ์˜์‹ฌํ•ด๋ด์•ผ ํ•ด. DDoS ๊ณต๊ฒฉ์ด๋‚˜ Brute Force ๊ณต๊ฒฉ์ผ ์ˆ˜ ์žˆ๊ฑฐ๋“ .

from collections import defaultdict
from datetime import datetime, timedelta

class FrequencyDetector:
    def __init__(self, time_window=60, threshold=100):
        """
        time_window: ์‹œ๊ฐ„ ์œˆ๋„์šฐ (์ดˆ)
        threshold: ์ž„๊ณ„๊ฐ’ (ํ•ด๋‹น ์‹œ๊ฐ„ ๋‚ด ์ตœ๋Œ€ ์š”์ฒญ ์ˆ˜)
        """
        self.time_window = time_window
        self.threshold = threshold
        self.request_history = defaultdict(list)
        
    def check_request(self, ip, timestamp):
        """์š”์ฒญ ๋นˆ๋„ ์ฒดํฌ"""
        current_time = datetime.strptime(timestamp, '%d/%b/%Y:%H:%M:%S %z')
        
        # ํ•ด๋‹น IP์˜ ์š”์ฒญ ๊ธฐ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
        requests = self.request_history[ip]
        
        # ์‹œ๊ฐ„ ์œˆ๋„์šฐ ๋ฐ–์˜ ์˜ค๋ž˜๋œ ์š”์ฒญ ์ œ๊ฑฐ
        cutoff_time = current_time - timedelta(seconds=self.time_window)
        requests = [t for t in requests if t > cutoff_time]
        
        # ํ˜„์žฌ ์š”์ฒญ ์ถ”๊ฐ€
        requests.append(current_time)
        self.request_history[ip] = requests
        
        # ์ž„๊ณ„๊ฐ’ ์ดˆ๊ณผ ์ฒดํฌ
        if len(requests) > self.threshold:
            return True, len(requests)
        
        return False, len(requests)
    
    def get_top_requesters(self, top_n=10):
        """๊ฐ€์žฅ ๋งŽ์ด ์š”์ฒญํ•œ IP ๋ชฉ๋ก"""
        ip_counts = {ip: len(times) for ip, times in self.request_history.items()}
        sorted_ips = sorted(ip_counts.items(), key=lambda x: x[1], reverse=True)
        return sorted_ips[:top_n]

# ์‚ฌ์šฉ ์˜ˆ์ œ
detector = FrequencyDetector(time_window=60, threshold=100)

# ๋กœ๊ทธ ๋ถ„์„ ์ค‘...
for log in logs:
    is_suspicious, count = detector.check_request(
        log['ip'], 
        log['timestamp']
    )
    
    if is_suspicious:
        print(f"โš ๏ธ ์˜์‹ฌ์Šค๋Ÿฌ์šด ํ™œ๋™ ํƒ์ง€!")
        print(f"   IP: {log['ip']}")
        print(f"   60์ดˆ ๋‚ด ์š”์ฒญ ์ˆ˜: {count}")
        print(f"   ์ž„๊ณ„๊ฐ’: {detector.threshold}")
        print("-" * 50)

# ์ƒ์œ„ ์š”์ฒญ์ž ์ถœ๋ ฅ
print("\n๐Ÿ“Š ์ƒ์œ„ 10๊ฐœ ์š”์ฒญ IP:")
for ip, count in detector.get_top_requesters():
    print(f"   {ip}: {count}ํšŒ")
์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ(Sliding Window) ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด. ํŠน์ • ์‹œ๊ฐ„ ๋™์•ˆ์˜ ์š”์ฒญ ์ˆ˜๋ฅผ ๊ณ„์† ์ถ”์ ํ•˜๋ฉด์„œ, ์ž„๊ณ„๊ฐ’์„ ๋„˜์œผ๋ฉด ๊ฒฝ๊ณ ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฑฐ์•ผ! ๐Ÿ“ˆ

2. ์ด์ƒ ์ง•ํ›„ ํƒ์ง€ (Anomaly Detection)

์ •์ƒ์ ์ธ ํŒจํ„ด์—์„œ ๋ฒ—์–ด๋‚œ ํ–‰๋™์„ ํƒ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์•ผ. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ‰์†Œ์—๋Š” ์˜ค์ „ 9์‹œ~6์‹œ์—๋งŒ ์ ‘์†ํ•˜๋˜ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ‘์ž๊ธฐ ์ƒˆ๋ฒฝ 3์‹œ์— ์ ‘์†ํ•œ๋‹ค๋ฉด? ์˜์‹ฌํ•ด๋ด์•ผ๊ฒ ์ง€! ๐ŸŒ™

import numpy as np
from datetime import datetime

class AnomalyDetector:
    def __init__(self):
        self.user_profiles = defaultdict(lambda: {
            'access_hours': [],
            'request_sizes': [],
            'accessed_paths': set()
        })
        
    def learn_profile(self, logs):
        """์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ํ•™์Šต"""
        for log in logs:
            user = log.get('user', 'anonymous')
            timestamp = datetime.strptime(log['timestamp'], '%d/%b/%Y:%H:%M:%S %z')
            hour = timestamp.hour
            size = int(log['size'])
            path = log['path']
            
            profile = self.user_profiles[user]
            profile['access_hours'].append(hour)
            profile['request_sizes'].append(size)
            profile['accessed_paths'].add(path)
        
        # ํ†ต๊ณ„ ๊ณ„์‚ฐ
        for user, profile in self.user_profiles.items():
            hours = np.array(profile['access_hours'])
            sizes = np.array(profile['request_sizes'])
            
            profile['hour_mean'] = np.mean(hours)
            profile['hour_std'] = np.std(hours)
            profile['size_mean'] = np.mean(sizes)
            profile['size_std'] = np.std(sizes)
    
    def detect_anomaly(self, log, z_threshold=3):
        """์ด์ƒ ์ง•ํ›„ ํƒ์ง€ (Z-score ๊ธฐ๋ฐ˜)"""
        user = log.get('user', 'anonymous')
        
        if user not in self.user_profiles:
            return False, "์‹ ๊ทœ ์‚ฌ์šฉ์ž"
        
        profile = self.user_profiles[user]
        timestamp = datetime.strptime(log['timestamp'], '%d/%b/%Y:%H:%M:%S %z')
        hour = timestamp.hour
        size = int(log['size'])
        path = log['path']
        
        anomalies = []
        
        # ์ ‘์† ์‹œ๊ฐ„ ์ด์ƒ ํƒ์ง€
        if profile['hour_std'] > 0:
            hour_z = abs(hour - profile['hour_mean']) / profile['hour_std']
            if hour_z > z_threshold:
                anomalies.append(f"๋น„์ •์ƒ์ ์ธ ์ ‘์† ์‹œ๊ฐ„ (Z-score: {hour_z:.2f})")
        
        # ์š”์ฒญ ํฌ๊ธฐ ์ด์ƒ ํƒ์ง€
        if profile['size_std'] > 0:
            size_z = abs(size - profile['size_mean']) / profile['size_std']
            if size_z > z_threshold:
                anomalies.append(f"๋น„์ •์ƒ์ ์ธ ์š”์ฒญ ํฌ๊ธฐ (Z-score: {size_z:.2f})")
        
        # ์ƒˆ๋กœ์šด ๊ฒฝ๋กœ ์ ‘๊ทผ ํƒ์ง€
        if path not in profile['accessed_paths']:
            anomalies.append(f"์ฒ˜์Œ ์ ‘๊ทผํ•˜๋Š” ๊ฒฝ๋กœ: {path}")
        
        return len(anomalies) > 0, anomalies

# ์‚ฌ์šฉ ์˜ˆ์ œ
anomaly_detector = AnomalyDetector()

# ํ•™์Šต ๋‹จ๊ณ„ (๊ณผ๊ฑฐ ๋กœ๊ทธ๋กœ ์ •์ƒ ํŒจํ„ด ํ•™์Šต)
print("๐Ÿ“š ์ •์ƒ ํŒจํ„ด ํ•™์Šต ์ค‘...")
training_logs = parser.parse_file('/var/log/apache2/access.log.1')  # ๊ณผ๊ฑฐ ๋กœ๊ทธ
anomaly_detector.learn_profile(training_logs)

# ํƒ์ง€ ๋‹จ๊ณ„ (์ƒˆ๋กœ์šด ๋กœ๊ทธ ๋ถ„์„)
print("\n๐Ÿ” ์ด์ƒ ์ง•ํ›„ ํƒ์ง€ ์ค‘...")
for log in logs:
    is_anomaly, details = anomaly_detector.detect_anomaly(log)
    
    if is_anomaly:
        print(f"๐Ÿšจ ์ด์ƒ ์ง•ํ›„ ๋ฐœ๊ฒฌ!")
        print(f"   ์‚ฌ์šฉ์ž: {log.get('user', 'anonymous')}")
        print(f"   IP: {log['ip']}")
        print(f"   ์‹œ๊ฐ„: {log['timestamp']}")
        print(f"   ์ƒ์„ธ:")
        for detail in details:
            print(f"      - {detail}")
        print("-" * 50)
์ด ๋ฐฉ๋ฒ•์€ ํ†ต๊ณ„์  ์ ‘๊ทผ์„ ์‚ฌ์šฉํ•ด. Z-score๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ํ‰๊ท ์—์„œ ์–ผ๋งˆ๋‚˜ ๋ฒ—์–ด๋‚ฌ๋Š”์ง€ ์ธก์ •ํ•˜๋Š” ๊ฑฐ์•ผ. 3 ์‹œ๊ทธ๋งˆ(ํ‘œ์ค€ํŽธ์ฐจ) ์ด์ƒ ๋ฒ—์–ด๋‚˜๋ฉด ์ด์ƒํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•˜์ง€! ๐Ÿ“Š
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ

์ด์ƒ ์ง•ํ›„ ํƒ์ง€๋Š” False Positive(์˜คํƒ)๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ํ•ด์™ธ ์ถœ์žฅ์„ ๊ฐ€์„œ ๋‹ค๋ฅธ ์‹œ๊ฐ„๋Œ€์— ์ ‘์†ํ•˜๋ฉด ์ด์ƒ ์ง•ํ›„๋กœ ํƒ์ง€๋  ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๊ทธ๋ž˜์„œ ์ž„๊ณ„๊ฐ’ ์กฐ์ •๊ณผ ์ถ”๊ฐ€์ ์ธ ์ปจํ…์ŠคํŠธ ๋ถ„์„์ด ์ค‘์š”ํ•ด! ๐ŸŽฏ

๐Ÿ” ๊ณ ๊ธ‰ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€

์ด์ œ ์ •๋ง ์‹ค์ „์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•๋“ค์„ ์•Œ์•„๋ณด์ž! ๐Ÿ˜Ž

1. ๋‹ค์ค‘ ๋กœ๊ทธ ์†Œ์Šค ์ƒ๊ด€ ๋ถ„์„

์‹ค์ œ ๊ณต๊ฒฉ์€ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋กœ ์ด๋ฃจ์–ด์ ธ. ์˜ˆ๋ฅผ ๋“ค์–ด:
1๏ธโƒฃ ํฌํŠธ ์Šค์บ”์œผ๋กœ ์ทจ์•ฝ์  ํƒ์ƒ‰
2๏ธโƒฃ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ทจ์•ฝ์  ๊ณต๊ฒฉ
3๏ธโƒฃ ๊ถŒํ•œ ์ƒ์Šน ์‹œ๋„
4๏ธโƒฃ ๋ฐ์ดํ„ฐ ์œ ์ถœ

๊ฐ ๋‹จ๊ณ„๋Š” ๋‹ค๋ฅธ ๋กœ๊ทธ ํŒŒ์ผ์— ๊ธฐ๋ก๋  ์ˆ˜ ์žˆ์–ด. ๊ทธ๋ž˜์„œ ์—ฌ๋Ÿฌ ๋กœ๊ทธ๋ฅผ ํ•จ๊ป˜ ๋ถ„์„ํ•ด์•ผ ํ•ด!

class MultiSourceCorrelator:
    def __init__(self):
        self.events = []
        self.attack_chains = []
        
    def add_event(self, source, event_type, ip, timestamp, details):
        """์ด๋ฒคํŠธ ์ถ”๊ฐ€"""
        event = {
            'source': source,
            'type': event_type,
            'ip': ip,
            'timestamp': timestamp,
            'details': details
        }
        self.events.append(event)
        
    def correlate_events(self, time_window=300):
        """์ด๋ฒคํŠธ ์ƒ๊ด€ ๋ถ„์„ (5๋ถ„ ์œˆ๋„์šฐ)"""
        # IP๋ณ„๋กœ ์ด๋ฒคํŠธ ๊ทธ๋ฃนํ™”
        ip_events = defaultdict(list)
        for event in self.events:
            ip_events[event['ip']].append(event)
        
        # ๊ณต๊ฒฉ ์ฒด์ธ ํƒ์ง€
        for ip, events in ip_events.items():
            # ์‹œ๊ฐ„์ˆœ ์ •๋ ฌ
            events.sort(key=lambda x: x['timestamp'])
            
            # ์˜์‹ฌ์Šค๋Ÿฌ์šด ํŒจํ„ด ์ฐพ๊ธฐ
            attack_patterns = self._detect_attack_patterns(events, time_window)
            
            if attack_patterns:
                self.attack_chains.append({
                    'ip': ip,
                    'patterns': attack_patterns,
                    'events': events
                })
        
        return self.attack_chains
    
    def _detect_attack_patterns(self, events, time_window):
        """๊ณต๊ฒฉ ํŒจํ„ด ํƒ์ง€"""
        patterns = []
        
        # ํŒจํ„ด 1: ํฌํŠธ ์Šค์บ” โ†’ ์›น ๊ณต๊ฒฉ
        port_scan = any(e['type'] == 'port_scan' for e in events)
        web_attack = any(e['type'] == 'web_attack' for e in events)
        
        if port_scan and web_attack:
            patterns.append('reconnaissance_to_exploitation')
        
        # ํŒจํ„ด 2: ๋กœ๊ทธ์ธ ์‹คํŒจ โ†’ ์„ฑ๊ณต โ†’ ๊ถŒํ•œ ์ƒ์Šน
        login_failures = [e for e in events if e['type'] == 'login_failure']
        login_success = [e for e in events if e['type'] == 'login_success']
        privilege_escalation = [e for e in events if e['type'] == 'privilege_escalation']
        
        if len(login_failures) > 5 and login_success and privilege_escalation:
            patterns.append('brute_force_to_privilege_escalation')
        
        # ํŒจํ„ด 3: SQL Injection โ†’ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ
        sql_injection = any(e['type'] == 'sql_injection' for e in events)
        data_access = any(e['type'] == 'sensitive_data_access' for e in events)
        
        if sql_injection and data_access:
            patterns.append('sql_injection_to_data_breach')
        
        return patterns

# ์‚ฌ์šฉ ์˜ˆ์ œ
correlator = MultiSourceCorrelator()

# ๋ฐฉํ™”๋ฒฝ ๋กœ๊ทธ์—์„œ ํฌํŠธ ์Šค์บ” ํƒ์ง€
correlator.add_event(
    source='firewall',
    event_type='port_scan',
    ip='203.0.113.50',
    timestamp=datetime.now(),
    details={'scanned_ports': [22, 80, 443, 3306]}
)

# ์›น ์„œ๋ฒ„ ๋กœ๊ทธ์—์„œ SQL Injection ์‹œ๋„ ํƒ์ง€
correlator.add_event(
    source='web_server',
    event_type='sql_injection',
    ip='203.0.113.50',
    timestamp=datetime.now(),
    details={'query': "' OR '1'='1"}
)

# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋กœ๊ทธ์—์„œ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ํƒ์ง€
correlator.add_event(
    source='database',
    event_type='sensitive_data_access',
    ip='203.0.113.50',
    timestamp=datetime.now(),
    details={'table': 'users', 'columns': ['password', 'credit_card']}
)

# ์ƒ๊ด€ ๋ถ„์„ ์‹คํ–‰
attack_chains = correlator.correlate_events()

for chain in attack_chains:
    print(f"๐Ÿšจ ๊ณต๊ฒฉ ์ฒด์ธ ํƒ์ง€!")
    print(f"   ๊ณต๊ฒฉ์ž IP: {chain['ip']}")
    print(f"   ํƒ์ง€๋œ ํŒจํ„ด: {', '.join(chain['patterns'])}")
    print(f"   ๊ด€๋ จ ์ด๋ฒคํŠธ ์ˆ˜: {len(chain['events'])}")
    print("-" * 50)
์ด ๋ฐฉ๋ฒ•์€ Kill Chain Analysis๋ผ๊ณ  ๋ถˆ๋Ÿฌ. ๊ณต๊ฒฉ์ž์˜ ์ „์ฒด ๊ณต๊ฒฉ ํ๋ฆ„์„ ์ถ”์ ํ•ด์„œ ๋” ์ •ํ™•ํ•œ ํƒ์ง€๊ฐ€ ๊ฐ€๋Šฅํ•ด! ๐ŸŽฏ

2. ๋จธ์‹ ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜ ํƒ์ง€

์š”์ฆ˜์€ ๋จธ์‹ ๋Ÿฌ๋‹์„ ํ™œ์šฉํ•œ ๋กœ๊ทธ ๋ถ„์„์ด ๋Œ€์„ธ์•ผ. ํŠนํžˆ ์•Œ๋ ค์ง€์ง€ ์•Š์€ ์ƒˆ๋กœ์šด ๊ณต๊ฒฉ(Zero-day)์„ ํƒ์ง€ํ•˜๋Š” ๋ฐ ํšจ๊ณผ์ ์ด์ง€!

from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
import pandas as pd

class MLBasedDetector:
    def __init__(self):
        self.model = IsolationForest(contamination=0.1, random_state=42)
        self.scaler = StandardScaler()
        self.is_trained = False
        
    def extract_features(self, logs):
        """๋กœ๊ทธ์—์„œ ํŠน์ง• ์ถ”์ถœ"""
        features = []
        
        for log in logs:
            timestamp = datetime.strptime(log['timestamp'], '%d/%b/%Y:%H:%M:%S %z')
            
            feature = {
                'hour': timestamp.hour,
                'day_of_week': timestamp.weekday(),
                'status_code': int(log['status']),
                'response_size': int(log['size']),
                'path_length': len(log['path']),
                'has_query_string': '?' in log['path'],
                'method_post': log['method'] == 'POST',
                'method_get': log['method'] == 'GET',
            }
            
            features.append(feature)
        
        return pd.DataFrame(features)
    
    def train(self, normal_logs):
        """์ •์ƒ ๋กœ๊ทธ๋กœ ๋ชจ๋ธ ํ•™์Šต"""
        print("๐Ÿค– ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ํ•™์Šต ์ค‘...")
        
        features = self.extract_features(normal_logs)
        features_scaled = self.scaler.fit_transform(features)
        
        self.model.fit(features_scaled)
        self.is_trained = True
        
        print("โœ… ํ•™์Šต ์™„๋ฃŒ!")
    
    def predict(self, logs):
        """์ด์ƒ ๋กœ๊ทธ ์˜ˆ์ธก"""
        if not self.is_trained:
            raise Exception("๋ชจ๋ธ์ด ํ•™์Šต๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!")
        
        features = self.extract_features(logs)
        features_scaled = self.scaler.transform(features)
        
        # -1: ์ด์ƒ, 1: ์ •์ƒ
        predictions = self.model.predict(features_scaled)
        scores = self.model.score_samples(features_scaled)
        
        results = []
        for i, (log, pred, score) in enumerate(zip(logs, predictions, scores)):
            if pred == -1:
                results.append({
                    'log': log,
                    'anomaly_score': score,
                    'is_anomaly': True
                })
        
        return results

# ์‚ฌ์šฉ ์˜ˆ์ œ
ml_detector = MLBasedDetector()

# ์ •์ƒ ๋กœ๊ทธ๋กœ ํ•™์Šต
normal_logs = parser.parse_file('/var/log/apache2/access.log.old')
ml_detector.train(normal_logs)

# ์ƒˆ๋กœ์šด ๋กœ๊ทธ ๋ถ„์„
new_logs = parser.parse_file('/var/log/apache2/access.log')
anomalies = ml_detector.predict(new_logs)

print(f"\n๐Ÿ” {len(anomalies)}๊ฐœ์˜ ์ด์ƒ ๋กœ๊ทธ ํƒ์ง€!")
for anomaly in anomalies[:5]:  # ์ƒ์œ„ 5๊ฐœ๋งŒ ์ถœ๋ ฅ
    log = anomaly['log']
    score = anomaly['anomaly_score']
    
    print(f"\nโš ๏ธ ์ด์ƒ ๋กœ๊ทธ ๋ฐœ๊ฒฌ (์ ์ˆ˜: {score:.3f})")
    print(f"   IP: {log['ip']}")
    print(f"   ๊ฒฝ๋กœ: {log['path']}")
    print(f"   ์ƒํƒœ: {log['status']}")
    print(f"   ํฌ๊ธฐ: {log['size']}")
Isolation Forest ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์ด์ƒ์น˜ ํƒ์ง€์— ํŠนํ™”๋œ ๋จธ์‹ ๋Ÿฌ๋‹ ๊ธฐ๋ฒ•์ด์•ผ. ์ •์ƒ ๋ฐ์ดํ„ฐ์˜ ํŒจํ„ด์„ ํ•™์Šตํ•œ ํ›„, ๊ทธ ํŒจํ„ด์—์„œ ๋ฒ—์–ด๋‚œ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์ƒ์œผ๋กœ ํŒ๋‹จํ•˜์ง€! ๐Ÿค–

๐Ÿ“Š ์‹ค์ „ ํ†ตํ•ฉ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

์ž, ์ด์ œ ์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋ชจ๋“  ๊ฑธ ํ†ตํ•ฉํ•ด์„œ ์‹ค์ „์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์™„์ „ํ•œ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค์–ด๋ณด์ž! ๐Ÿ—๏ธ

import json
import sqlite3
from datetime import datetime
import threading
import queue

class SecurityMonitoringSystem:
    def __init__(self, db_path='security_events.db'):
        self.db_path = db_path
        self.event_queue = queue.Queue()
        
        # ๊ฐ์ข… ํƒ์ง€๊ธฐ ์ดˆ๊ธฐํ™”
        self.parser = LogParser()
        self.frequency_detector = FrequencyDetector(time_window=60, threshold=100)
        self.anomaly_detector = AnomalyDetector()
        self.correlator = MultiSourceCorrelator()
        self.ml_detector = MLBasedDetector()
        
        # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”
        self._init_database()
        
        # ์•Œ๋ฆผ ์„ค์ •
        self.alert_handlers = []
        
    def _init_database(self):
        """๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS security_events (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                event_type TEXT NOT NULL,
                severity TEXT NOT NULL,
                source_ip TEXT,
                details TEXT,
                created_at TEXT DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS blocked_ips (
                ip TEXT PRIMARY KEY,
                reason TEXT,
                blocked_at TEXT DEFAULT CURRENT_TIMESTAMP,
                block_count INTEGER DEFAULT 1
            )
        ''')
        
        conn.commit()
        conn.close()
    
    def add_alert_handler(self, handler):
        """์•Œ๋ฆผ ํ•ธ๋“ค๋Ÿฌ ์ถ”๊ฐ€"""
        self.alert_handlers.append(handler)
    
    def process_log(self, log_entry):
        """๋กœ๊ทธ ์ฒ˜๋ฆฌ ๋ฐ ๋ถ„์„"""
        threats = []
        
        # 1. ๋นˆ๋„ ๊ธฐ๋ฐ˜ ํƒ์ง€
        is_freq_suspicious, count = self.frequency_detector.check_request(
            log_entry['ip'],
            log_entry['timestamp']
        )
        
        if is_freq_suspicious:
            threats.append({
                'type': 'high_frequency',
                'severity': 'HIGH',
                'details': f'{count}ํšŒ ์š”์ฒญ (60์ดˆ ๋‚ด)'
            })
        
        # 2. ํŒจํ„ด ๋งค์นญ
        suspicious_patterns = {
            r'\.\./\.\./' : 'Path Traversal',
            r'union.*select': 'SQL Injection',
            r'<script>': 'XSS',
            r'/etc/passwd': 'System File Access',
            r'cmd\.exe': 'Command Execution',
        }
        
        for pattern, attack_type in suspicious_patterns.items():
            if re.search(pattern, log_entry['path'], re.IGNORECASE):
                threats.append({
                    'type': attack_type.lower().replace(' ', '_'),
                    'severity': 'CRITICAL',
                    'details': f'{attack_type} ์‹œ๋„ ํƒ์ง€'
                })
        
        # 3. ์ด์ƒ ์ง•ํ›„ ํƒ์ง€
        is_anomaly, anomaly_details = self.anomaly_detector.detect_anomaly(log_entry)
        
        if is_anomaly:
            threats.append({
                'type': 'anomaly',
                'severity': 'MEDIUM',
                'details': ', '.join(anomaly_details)
            })
        
        # ์œ„ํ˜‘์ด ํƒ์ง€๋˜๋ฉด ์ฒ˜๋ฆฌ
        if threats:
            self._handle_threats(log_entry, threats)
        
        return threats
    
    def _handle_threats(self, log_entry, threats):
        """์œ„ํ˜‘ ์ฒ˜๋ฆฌ"""
        ip = log_entry['ip']
        
        # ์‹ฌ๊ฐ๋„๋ณ„ ์ฒ˜๋ฆฌ
        critical_count = sum(1 for t in threats if t['severity'] == 'CRITICAL')
        
        if critical_count > 0:
            # CRITICAL ์œ„ํ˜‘์€ ์ฆ‰์‹œ ์ฐจ๋‹จ
            self._block_ip(ip, f"Critical threats detected: {critical_count}")
        
        # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ
        for threat in threats:
            self._save_event(
                timestamp=log_entry['timestamp'],
                event_type=threat['type'],
                severity=threat['severity'],
                source_ip=ip,
                details=json.dumps({
                    'threat': threat,
                    'log': log_entry
                })
            )
        
        # ์•Œ๋ฆผ ์ „์†ก
        self._send_alerts(log_entry, threats)
    
    def _block_ip(self, ip, reason):
        """IP ์ฐจ๋‹จ"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            INSERT INTO blocked_ips (ip, reason)
            VALUES (?, ?)
            ON CONFLICT(ip) DO UPDATE SET
                block_count = block_count + 1,
                blocked_at = CURRENT_TIMESTAMP
        ''', (ip, reason))
        
        conn.commit()
        conn.close()
        
        print(f"๐Ÿšซ IP ์ฐจ๋‹จ: {ip} - {reason}")
        
        # ์‹ค์ œ ๋ฐฉํ™”๋ฒฝ ๊ทœ์น™ ์ถ”๊ฐ€ (iptables ๋“ฑ)
        # os.system(f"iptables -A INPUT -s {ip} -j DROP")
    
    def _save_event(self, timestamp, event_type, severity, source_ip, details):
        """๋ณด์•ˆ ์ด๋ฒคํŠธ ์ €์žฅ"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            INSERT INTO security_events (timestamp, event_type, severity, source_ip, details)
            VALUES (?, ?, ?, ?, ?)
        ''', (timestamp, event_type, severity, source_ip, details))
        
        conn.commit()
        conn.close()
    
    def _send_alerts(self, log_entry, threats):
        """์•Œ๋ฆผ ์ „์†ก"""
        for handler in self.alert_handlers:
            try:
                handler(log_entry, threats)
            except Exception as e:
                print(f"โŒ ์•Œ๋ฆผ ์ „์†ก ์‹คํŒจ: {str(e)}")
    
    def get_statistics(self, hours=24):
        """ํ†ต๊ณ„ ์กฐํšŒ"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # ์ตœ๊ทผ ์ด๋ฒคํŠธ ์ˆ˜
        cursor.execute('''
            SELECT event_type, severity, COUNT(*) as count
            FROM security_events
            WHERE datetime(created_at) > datetime('now', '-' || ? || ' hours')
            GROUP BY event_type, severity
            ORDER BY count DESC
        ''', (hours,))
        
        events = cursor.fetchall()
        
        # ์ฐจ๋‹จ๋œ IP ์ˆ˜
        cursor.execute('SELECT COUNT(*) FROM blocked_ips')
        blocked_count = cursor.fetchone()[0]
        
        conn.close()
        
        return {
            'events': events,
            'blocked_ips': blocked_count
        }
    
    def start_monitoring(self, log_file):
        """๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘"""
        print("๐Ÿš€ ๋ณด์•ˆ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ์‹œ์ž‘!")
        print(f"๐Ÿ“ ๋ชจ๋‹ˆํ„ฐ๋ง ํŒŒ์ผ: {log_file}")
        print("-" * 50)
        
        monitor = RealTimeLogMonitor(log_file, self.process_log)
        
        try:
            monitor.follow()
        except KeyboardInterrupt:
            print("\n\nโน๏ธ ๋ชจ๋‹ˆํ„ฐ๋ง ์ค‘์ง€")
            self._print_summary()
    
    def _print_summary(self):
        """์š”์•ฝ ์ถœ๋ ฅ"""
        stats = self.get_statistics(hours=24)
        
        print("\n" + "=" * 50)
        print("๐Ÿ“Š 24์‹œ๊ฐ„ ๋ณด์•ˆ ์ด๋ฒคํŠธ ์š”์•ฝ")
        print("=" * 50)
        
        print("\n๐Ÿ” ํƒ์ง€๋œ ์ด๋ฒคํŠธ:")
        for event_type, severity, count in stats['events']:
            print(f"   [{severity}] {event_type}: {count}๊ฑด")
        
        print(f"\n๐Ÿšซ ์ฐจ๋‹จ๋œ IP ์ˆ˜: {stats['blocked_ips']}๊ฐœ")

# ์•Œ๋ฆผ ํ•ธ๋“ค๋Ÿฌ ์˜ˆ์ œ
def email_alert_handler(log_entry, threats):
    """์ด๋ฉ”์ผ ์•Œ๋ฆผ"""
    print(f"๐Ÿ“ง ์ด๋ฉ”์ผ ์•Œ๋ฆผ ์ „์†ก")
    print(f"   ์ˆ˜์‹ ์ž: security@company.com")
    print(f"   ์ œ๋ชฉ: [๋ณด์•ˆ ๊ฒฝ๊ณ ] {threats[0]['type']} ํƒ์ง€")
    # ์‹ค์ œ๋กœ๋Š” smtplib ๋“ฑ์„ ์‚ฌ์šฉํ•ด์„œ ์ด๋ฉ”์ผ ์ „์†ก

def slack_alert_handler(log_entry, threats):
    """Slack ์•Œ๋ฆผ"""
    print(f"๐Ÿ’ฌ Slack ์•Œ๋ฆผ ์ „์†ก")
    print(f"   ์ฑ„๋„: #security-alerts")
    # ์‹ค์ œ๋กœ๋Š” Slack Webhook์„ ์‚ฌ์šฉํ•ด์„œ ๋ฉ”์‹œ์ง€ ์ „์†ก

# ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ๋ฐ ์‹คํ–‰
system = SecurityMonitoringSystem()

# ์•Œ๋ฆผ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก
system.add_alert_handler(email_alert_handler)
system.add_alert_handler(slack_alert_handler)

# ๊ณผ๊ฑฐ ๋กœ๊ทธ๋กœ ํ•™์Šต
print("๐Ÿ“š ์ •์ƒ ํŒจํ„ด ํ•™์Šต ์ค‘...")
training_logs = system.parser.parse_file('/var/log/apache2/access.log.1')
system.anomaly_detector.learn_profile(training_logs)
system.ml_detector.train(training_logs)

# ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘
# system.start_monitoring('/var/log/apache2/access.log')
์™€! ์ด์ œ ์™„์ „ํ•œ ๋ณด์•ˆ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์ด ์™„์„ฑ๋์–ด! ๐ŸŽ‰ ์ด ์‹œ์Šคํ…œ์€:

โœ… ์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง
โœ… ๋‹ค์–‘ํ•œ ํƒ์ง€ ๊ธฐ๋ฒ• ํ†ตํ•ฉ
โœ… ์ž๋™ IP ์ฐจ๋‹จ
โœ… ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ
โœ… ๋‹ค์ค‘ ์ฑ„๋„ ์•Œ๋ฆผ
โœ… ํ†ต๊ณ„ ๋ฐ ๋ฆฌํฌํŒ…

๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ”๊ณ  ์žˆ์ง€! ๐Ÿ˜Ž

๐ŸŽจ ์‹œ๊ฐํ™” ๋ฐ ๋Œ€์‹œ๋ณด๋“œ

๋กœ๊ทธ ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ธฐ ์ข‹๊ฒŒ ์‹œ๊ฐํ™”ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด! ๊ด€๋ฆฌ์ž๊ฐ€ ํ•œ๋ˆˆ์— ๋ณด์•ˆ ์ƒํ™ฉ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ฑฐ๋“ . ๐Ÿ“Š

import matplotlib.pyplot as plt
import seaborn as sns
from collections import Counter

class SecurityDashboard:
    def __init__(self, system):
        self.system = system
        sns.set_style("whitegrid")
        
    def plot_event_timeline(self, hours=24):
        """์‹œ๊ฐ„๋Œ€๋ณ„ ์ด๋ฒคํŠธ ๊ทธ๋ž˜ํ”„"""
        conn = sqlite3.connect(self.system.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            SELECT strftime('%H', created_at) as hour, COUNT(*) as count
            FROM security_events
            WHERE datetime(created_at) > datetime('now', '-' || ? || ' hours')
            GROUP BY hour
            ORDER BY hour
        ''', (hours,))
        
        data = cursor.fetchall()
        conn.close()
        
        if not data:
            print("๐Ÿ“Š ํ‘œ์‹œํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
            return
        
        hours_list = [int(h) for h, _ in data]
        counts = [c for _, c in data]
        
        plt.figure(figsize=(12, 6))
        plt.bar(hours_list, counts, color='#667eea', alpha=0.7)
        plt.xlabel('์‹œ๊ฐ„ (Hour)')
        plt.ylabel('์ด๋ฒคํŠธ ์ˆ˜')
        plt.title(f'์ตœ๊ทผ {hours}์‹œ๊ฐ„ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ€์ž„๋ผ์ธ')
        plt.xticks(range(24))
        plt.grid(axis='y', alpha=0.3)
        plt.tight_layout()
        plt.savefig('event_timeline.png', dpi=300, bbox_inches='tight')
        print("โœ… ๊ทธ๋ž˜ํ”„ ์ €์žฅ: event_timeline.png")
        
    def plot_threat_distribution(self):
        """์œ„ํ˜‘ ์œ ํ˜•๋ณ„ ๋ถ„ํฌ"""
        conn = sqlite3.connect(self.system.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            SELECT event_type, COUNT(*) as count
            FROM security_events
            GROUP BY event_type
            ORDER BY count DESC
            LIMIT 10
        ''')
        
        data = cursor.fetchall()
        conn.close()
        
        if not data:
            print("๐Ÿ“Š ํ‘œ์‹œํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
            return
        
        types = [t for t, _ in data]
        counts = [c for _, c in data]
        
        plt.figure(figsize=(10, 8))
        colors = plt.cm.Spectral(range(len(types)))
        plt.pie(counts, labels=types, autopct='%1.1f%%', colors=colors, startangle=90)
        plt.title('์œ„ํ˜‘ ์œ ํ˜•๋ณ„ ๋ถ„ํฌ')
        plt.axis('equal')
        plt.tight_layout()
        plt.savefig('threat_distribution.png', dpi=300, bbox_inches='tight')
        print("โœ… ๊ทธ๋ž˜ํ”„ ์ €์žฅ: threat_distribution.png")
    
    def plot_top_attackers(self, top_n=10):
        """์ƒ์œ„ ๊ณต๊ฒฉ์ž IP"""
        conn = sqlite3.connect(self.system.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            SELECT source_ip, COUNT(*) as count
            FROM security_events
            WHERE source_ip IS NOT NULL
            GROUP BY source_ip
            ORDER BY count DESC
            LIMIT ?
        ''', (top_n,))
        
        data = cursor.fetchall()
        conn.close()
        
        if not data:
            print("๐Ÿ“Š ํ‘œ์‹œํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
            return
        
        ips = [ip for ip, _ in data]
        counts = [c for _, c in data]
        
        plt.figure(figsize=(12, 6))
        plt.barh(ips, counts, color='#f87171', alpha=0.7)
        plt.xlabel('์ด๋ฒคํŠธ ์ˆ˜')
        plt.ylabel('IP ์ฃผ์†Œ')
        plt.title(f'์ƒ์œ„ {top_n}๊ฐœ ๊ณต๊ฒฉ์ž IP')
        plt.gca().invert_yaxis()
        plt.tight_layout()
        plt.savefig('top_attackers.png', dpi=300, bbox_inches='tight')
        print("โœ… ๊ทธ๋ž˜ํ”„ ์ €์žฅ: top_attackers.png")
    
    def generate_report(self):
        """์ข…ํ•ฉ ๋ฆฌํฌํŠธ ์ƒ์„ฑ"""
        print("\n" + "=" * 60)
        print("๐Ÿ“Š ๋ณด์•ˆ ๋ชจ๋‹ˆํ„ฐ๋ง ์ข…ํ•ฉ ๋ฆฌํฌํŠธ")
        print("=" * 60)
        
        stats = self.system.get_statistics(hours=24)
        
        print("\n๐Ÿ“ˆ ์ตœ๊ทผ 24์‹œ๊ฐ„ ํ†ต๊ณ„:")
        print(f"   ์ด ์ด๋ฒคํŠธ ์ˆ˜: {sum(count for _, _, count in stats['events'])}๊ฑด")
        print(f"   ์ฐจ๋‹จ๋œ IP: {stats['blocked_ips']}๊ฐœ")
        
        print("\n๐Ÿ”ฅ ์‹ฌ๊ฐ๋„๋ณ„ ์ด๋ฒคํŠธ:")
        severity_counts = {}
        for _, severity, count in stats['events']:
            severity_counts[severity] = severity_counts.get(severity, 0) + count
        
        for severity in ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']:
            count = severity_counts.get(severity, 0)
            if count > 0:
                print(f"   [{severity}]: {count}๊ฑด")
        
        print("\n๐Ÿ“Š ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ ์ค‘...")
        self.plot_event_timeline()
        self.plot_threat_distribution()
        self.plot_top_attackers()
        
        print("\nโœ… ๋ฆฌํฌํŠธ ์ƒ์„ฑ ์™„๋ฃŒ!")

# ๋Œ€์‹œ๋ณด๋“œ ์‚ฌ์šฉ
dashboard = SecurityDashboard(system)
dashboard.generate_report()
์‹œ๊ฐํ™”๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ง๊ด€์ ์œผ๋กœ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด. ํŠนํžˆ ๊ฒฝ์˜์ง„์—๊ฒŒ ๋ณด๊ณ ํ•  ๋•Œ ๊ทธ๋ž˜ํ”„๊ฐ€ ์žˆ์œผ๋ฉด ํ›จ์”ฌ ํšจ๊ณผ์ ์ด์ง€! ๐Ÿ“ˆ
๐Ÿ’ก ์‹ค๋ฌด ํŒ

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ๋Š” ELK Stack(Elasticsearch, Logstash, Kibana)์ด๋‚˜ Splunk ๊ฐ™์€ ์ „๋ฌธ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ํ•˜์ง€๋งŒ ์ž‘์€ ๊ทœ๋ชจ์˜ ์‹œ์Šคํ…œ์ด๋‚˜ ํŠน์ • ๋ชฉ์ ์˜ ๋ถ„์„์—๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์ปค์Šคํ…€ ์†”๋ฃจ์…˜์ด ๋” ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ์–ด! ๊ทธ๋ฆฌ๊ณ  ์žฌ๋Šฅ๋„ท์—์„œ ๋กœ๊ทธ ๋ถ„์„ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„ ์ž๋ฌธ์„ ๊ตฌํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ์‹ค์ „ ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ์ „๋ฌธ๊ฐ€์˜ ์กฐ์–ธ์€ ์ •๋ง ๊ฐ’์ง€๊ฑฐ๋“ ! ๐Ÿ’Ž

๐Ÿ”ง ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐ ํ™•์žฅ์„ฑ

๋กœ๊ทธ ํŒŒ์ผ์€ ์ •๋ง ๋น ๋ฅด๊ฒŒ ์ปค์ ธ. ํ•˜๋ฃจ์— ์ˆ˜ GB์”ฉ ์Œ“์ด๋Š” ๊ฒฝ์šฐ๋„ ํ”ํ•˜์ง€. ๊ทธ๋ž˜์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ํ•„์ˆ˜์•ผ! โšก

1. ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ ํ™œ์šฉ

from multiprocessing import Pool, cpu_count
import os

class ParallelLogProcessor:
    def __init__(self, num_processes=None):
        self.num_processes = num_processes or cpu_count()
        self.parser = LogParser()
        
    def process_chunk(self, chunk):
        """๋กœ๊ทธ ์ฒญํฌ ์ฒ˜๋ฆฌ"""
        results = []
        for line in chunk:
            parsed = self.parser.parse_line(line.strip())
            if parsed:
                results.append(parsed)
        return results
    
    def split_file(self, filepath, chunk_size=10000):
        """ํŒŒ์ผ์„ ์ฒญํฌ๋กœ ๋ถ„ํ• """
        chunks = []
        current_chunk = []
        
        with open(filepath, 'r') as f:
            for line in f:
                current_chunk.append(line)
                
                if len(current_chunk) >= chunk_size:
                    chunks.append(current_chunk)
                    current_chunk = []
            
            if current_chunk:
                chunks.append(current_chunk)
        
        return chunks
    
    def process_file_parallel(self, filepath):
        """๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋กœ ํŒŒ์ผ ๋ถ„์„"""
        print(f"๐Ÿš€ {self.num_processes}๊ฐœ ํ”„๋กœ์„ธ์Šค๋กœ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์‹œ์ž‘...")
        
        chunks = self.split_file(filepath)
        print(f"๐Ÿ“ฆ {len(chunks)}๊ฐœ ์ฒญํฌ๋กœ ๋ถ„ํ• ")
        
        with Pool(processes=self.num_processes) as pool:
            results = pool.map(self.process_chunk, chunks)
        
        # ๊ฒฐ๊ณผ ๋ณ‘ํ•ฉ
        all_logs = []
        for chunk_result in results:
            all_logs.extend(chunk_result)
        
        print(f"โœ… {len(all_logs)}๊ฐœ ๋กœ๊ทธ ์ฒ˜๋ฆฌ ์™„๋ฃŒ!")
        return all_logs

# ์‚ฌ์šฉ ์˜ˆ์ œ
parallel_processor = ParallelLogProcessor()
logs = parallel_processor.process_file_parallel('/var/log/apache2/access.log')
๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด CPU ์ฝ”์–ด๋ฅผ ๋ชจ๋‘ ํ™œ์šฉํ•ด์„œ ์ฒ˜๋ฆฌ ์†๋„๋ฅผ ํฌ๊ฒŒ ๋†’์ผ ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

2. ์ธ๋ฑ์‹ฑ ๋ฐ ์บ์‹ฑ

import redis
import pickle

class CachedLogAnalyzer:
    def __init__(self, redis_host='localhost', redis_port=6379):
        self.redis_client = redis.Redis(host=redis_host, port=redis_port, db=0)
        self.cache_ttl = 3600  # 1์‹œ๊ฐ„
        
    def get_cached_analysis(self, cache_key):
        """์บ์‹œ์—์„œ ๋ถ„์„ ๊ฒฐ๊ณผ ๊ฐ€์ ธ์˜ค๊ธฐ"""
        cached = self.redis_client.get(cache_key)
        if cached:
            return pickle.loads(cached)
        return None
    
    def set_cached_analysis(self, cache_key, data):
        """๋ถ„์„ ๊ฒฐ๊ณผ ์บ์‹ฑ"""
        self.redis_client.setex(
            cache_key,
            self.cache_ttl,
            pickle.dumps(data)
        )
    
    def analyze_with_cache(self, ip_address):
        """์บ์‹œ๋ฅผ ํ™œ์šฉํ•œ ๋ถ„์„"""
        cache_key = f"analysis:{ip_address}"
        
        # ์บ์‹œ ํ™•์ธ
        cached_result = self.get_cached_analysis(cache_key)
        if cached_result:
            print(f"๐Ÿ’พ ์บ์‹œ ํžˆํŠธ: {ip_address}")
            return cached_result
        
        # ์บ์‹œ ๋ฏธ์Šค - ์ƒˆ๋กœ ๋ถ„์„
        print(f"๐Ÿ” ์ƒˆ๋กœ์šด ๋ถ„์„: {ip_address}")
        result = self._perform_analysis(ip_address)
        
        # ๊ฒฐ๊ณผ ์บ์‹ฑ
        self.set_cached_analysis(cache_key, result)
        
        return result
    
    def _perform_analysis(self, ip_address):
        """์‹ค์ œ ๋ถ„์„ ์ˆ˜ํ–‰"""
        # ๋ณต์žกํ•œ ๋ถ„์„ ๋กœ์ง...
        return {
            'ip': ip_address,
            'threat_level': 'LOW',
            'analysis_time': datetime.now().isoformat()
        }
Redis ๊ฐ™์€ ์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ˜๋ณต์ ์ธ ๋ถ„์„์„ ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด! โšก

๐Ÿ›ก๏ธ ์‹ค์ „ ๋ณด์•ˆ ์‹œ๋‚˜๋ฆฌ์˜ค

์ด์ œ ์‹ค์ œ ๋ณด์•ˆ ์‚ฌ๊ณ  ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์‹œ์Šคํ…œ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด์ž! ๐ŸŽฌ

์‹œ๋‚˜๋ฆฌ์˜ค 1: DDoS ๊ณต๊ฒฉ ํƒ์ง€ ๋ฐ ๋Œ€์‘

๊ณต๊ฒฉ์ž๊ฐ€ ๋ด‡๋„ท์„ ์ด์šฉํ•ด ์›น ์„œ๋ฒ„์— ๋Œ€๋Ÿ‰์˜ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ์ƒํ™ฉ์ด์•ผ.

class DDoSDetector:
    def __init__(self, threshold_per_second=100):
        self.threshold = threshold_per_second
        self.request_counts = defaultdict(list)
        
    def detect_ddos(self, logs, time_window=10):
        """DDoS ๊ณต๊ฒฉ ํƒ์ง€"""
        ddos_sources = []
        
        for log in logs:
            ip = log['ip']
            timestamp = datetime.strptime(log['timestamp'], '%d/%b/%Y:%H:%M:%S %z')
            
            # ์š”์ฒญ ๊ธฐ๋ก
            self.request_counts[ip].append(timestamp)
            
            # ์˜ค๋ž˜๋œ ๊ธฐ๋ก ์ œ๊ฑฐ
            cutoff = timestamp - timedelta(seconds=time_window)
            self.request_counts[ip] = [
                t for t in self.request_counts[ip] if t > cutoff
            ]
            
            # ์ดˆ๋‹น ์š”์ฒญ ์ˆ˜ ๊ณ„์‚ฐ
            requests_per_second = len(self.request_counts[ip]) / time_window
            
            if requests_per_second > self.threshold:
                ddos_sources.append({
                    'ip': ip,
                    'requests_per_second': requests_per_second,
                    'total_requests': len(self.request_counts[ip])
                })
        
        return ddos_sources
    
    def mitigate_ddos(self, sources):
        """DDoS ์™„ํ™” ์กฐ์น˜"""
        for source in sources:
            ip = source['ip']
            rps = source['requests_per_second']
            
            print(f"๐Ÿšจ DDoS ๊ณต๊ฒฉ ํƒ์ง€!")
            print(f"   ๊ณต๊ฒฉ์ž IP: {ip}")
            print(f"   ์ดˆ๋‹น ์š”์ฒญ: {rps:.2f}")
            
            # 1. IP ์ฐจ๋‹จ
            print(f"   โœ… IP ์ฐจ๋‹จ ์‹คํ–‰")
            # os.system(f"iptables -A INPUT -s {ip} -j DROP")
            
            # 2. Rate Limiting ์ ์šฉ
            print(f"   โœ… Rate Limiting ์ ์šฉ")
            # nginx rate limit ์„ค์ • ์—…๋ฐ์ดํŠธ
            
            # 3. CDN/WAF์— ์•Œ๋ฆผ
            print(f"   โœ… CDN/WAF ์•Œ๋ฆผ ์ „์†ก")

# ์‚ฌ์šฉ
ddos_detector = DDoSDetector(threshold_per_second=50)
ddos_sources = ddos_detector.detect_ddos(logs)

if ddos_sources:
    ddos_detector.mitigate_ddos(ddos_sources)
์‹œ๋‚˜๋ฆฌ์˜ค 2: ๋‚ด๋ถ€์ž ์œ„ํ˜‘ ํƒ์ง€

์ •์ƒ ์ง์› ๊ณ„์ •์ด ๊ฐ‘์ž๊ธฐ ์ด์ƒํ•œ ํ–‰๋™์„ ๋ณด์ด๋Š” ๊ฒฝ์šฐ์•ผ. ๊ณ„์ •์ด ํƒˆ์ทจ๋˜์—ˆ๊ฑฐ๋‚˜ ๋‚ด๋ถ€์ž๊ฐ€ ์•…์˜์ ์ธ ํ–‰๋™์„ ํ•˜๋Š” ๊ฑฐ์ง€.

class InsiderThreatDetector:
    def __init__(self):
        self.user_baselines = {}
        
    def build_baseline(self, user_id, historical_logs):
        """์‚ฌ์šฉ์ž ์ •์ƒ ํ–‰๋™ ํŒจํ„ด ๊ตฌ์ถ•"""
        baseline = {
            'typical_hours': set(),
            'typical_ips': set(),
            'typical_actions': set(),
            'avg_data_access': 0,
        }
        
        access_counts = []
        
        for log in historical_logs:
            if log.get('user') == user_id:
                timestamp = datetime.strptime(log['timestamp'], '%d/%b/%Y:%H:%M:%S %z')
                baseline['typical_hours'].add(timestamp.hour)
                baseline['typical_ips'].add(log['ip'])
                baseline['typical_actions'].add(log['method'])
                access_counts.append(int(log.get('size', 0)))
        
        if access_counts:
            baseline['avg_data_access'] = sum(access_counts) / len(access_counts)
        
        self.user_baselines[user_id] = baseline
        
    def detect_insider_threat(self, log):
        """๋‚ด๋ถ€์ž ์œ„ํ˜‘ ํƒ์ง€"""
        user_id = log.get('user')
        
        if not user_id or user_id not in self.user_baselines:
            return False, []
        
        baseline = self.user_baselines[user_id]
        anomalies = []
        
        # 1. ๋น„์ •์ƒ ์‹œ๊ฐ„๋Œ€ ์ ‘์†
        timestamp = datetime.strptime(log['timestamp'], '%d/%b/%Y:%H:%M:%S %z')
        if timestamp.hour not in baseline['typical_hours']:
            anomalies.append(f"๋น„์ •์ƒ ์‹œ๊ฐ„๋Œ€ ์ ‘์†: {timestamp.hour}์‹œ")
        
        # 2. ์ƒˆ๋กœ์šด IP ์ฃผ์†Œ
        if log['ip'] not in baseline['typical_ips']:
            anomalies.append(f"์ƒˆ๋กœ์šด IP ์ฃผ์†Œ: {log['ip']}")
        
        # 3. ๋น„์ •์ƒ์ ์ธ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ๋Ÿ‰
        data_size = int(log.get('size', 0))
        if data_size > baseline['avg_data_access'] * 10:  # ํ‰๊ท ์˜ 10๋ฐฐ
            anomalies.append(f"๋น„์ •์ƒ์ ์ธ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ: {data_size} bytes")
        
        # 4. ๋ฏผ๊ฐํ•œ ๊ฒฝ๋กœ ์ ‘๊ทผ
        sensitive_paths = ['/admin', '/api/users', '/backup', '/config']
        if any(path in log['path'] for path in sensitive_paths):
            if log['path'] not in baseline.get('typical_paths', set()):
                anomalies.append(f"๋ฏผ๊ฐํ•œ ๊ฒฝ๋กœ ์ ‘๊ทผ: {log['path']}")
        
        return len(anomalies) > 0, anomalies

# ์‚ฌ์šฉ
insider_detector = InsiderThreatDetector()

# ๊ณผ๊ฑฐ ๋กœ๊ทธ๋กœ ์ •์ƒ ํŒจํ„ด ํ•™์Šต
for user in ['user1', 'user2', 'admin']:
    user_logs = [log for log in historical_logs if log.get('user') == user]
    insider_detector.build_baseline(user, user_logs)

# ์‹ค์‹œ๊ฐ„ ํƒ์ง€
for log in new_logs:
    is_threat, anomalies = insider_detector.detect_insider_threat(log)
    
    if is_threat:
        print(f"โš ๏ธ ๋‚ด๋ถ€์ž ์œ„ํ˜‘ ์˜์‹ฌ!")
        print(f"   ์‚ฌ์šฉ์ž: {log.get('user')}")
        print(f"   ์ด์ƒ ์ง•ํ›„:")
        for anomaly in anomalies:
            print(f"      - {anomaly}")
์ด๋Ÿฐ ์‹์œผ๋กœ ๋‹ค์–‘ํ•œ ๋ณด์•ˆ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ›ก๏ธ

๐Ÿ“ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค ๋ฐ ๊ถŒ์žฅ์‚ฌํ•ญ

๋งˆ์ง€๋ง‰์œผ๋กœ ๋กœ๊ทธ ๋ถ„์„ ์‹œ์Šคํ…œ์„ ์šด์˜ํ•  ๋•Œ ๊ผญ ์ง€์ผœ์•ผ ํ•  ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋ฅผ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ! โœจ

ํ•ญ๋ชฉ ๊ถŒ์žฅ์‚ฌํ•ญ ์ด์œ 
๋กœ๊ทธ ๋ณด๊ด€ ๊ธฐ๊ฐ„ ์ตœ์†Œ 90์ผ, ๊ถŒ์žฅ 1๋…„ ์‚ฌ๊ณ  ์กฐ์‚ฌ ๋ฐ ๊ทœ์ • ์ค€์ˆ˜
๋กœ๊ทธ ํ˜•์‹ ๊ตฌ์กฐํ™”๋œ ํ˜•์‹ (JSON) ํŒŒ์‹ฑ ๋ฐ ๋ถ„์„ ์šฉ์ด
์‹œ๊ฐ„ ๋™๊ธฐํ™” NTP ์‚ฌ์šฉ ํ•„์ˆ˜ ์ •ํ™•ํ•œ ์‹œ๊ฐ„ ์ƒ๊ด€ ๋ถ„์„
๋กœ๊ทธ ๋ฌด๊ฒฐ์„ฑ ํ•ด์‹œ๊ฐ’ ์ €์žฅ ๋ฐ ๊ฒ€์ฆ ๋กœ๊ทธ ์œ„๋ณ€์กฐ ๋ฐฉ์ง€
๋ฐฑ์—… ์›๊ฒฉ์ง€ ๋ฐฑ์—… ํ•„์ˆ˜ ์žฌํ•ด ๋ณต๊ตฌ ๋Œ€๋น„
์ ‘๊ทผ ์ œ์–ด ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ ๋กœ๊ทธ ์ •๋ณด ๋ณดํ˜ธ

1. ๋กœ๊ทธ ์ˆ˜์ง‘ ํ‘œ์ค€ํ™”

๋ชจ๋“  ์‹œ์Šคํ…œ์—์„œ ์ผ๊ด€๋œ ํ˜•์‹์œผ๋กœ ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•ด์•ผ ํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ถ„์„์ด ํ›จ์”ฌ ์‰ฌ์›Œ์ ธ!

import logging
import json

class StructuredLogger:
    def __init__(self, name):
        self.logger = logging.getLogger(name)
        self.logger.setLevel(logging.INFO)
        
        # JSON ํฌ๋งท ํ•ธ๋“ค๋Ÿฌ
        handler = logging.StreamHandler()
        handler.setFormatter(self.JSONFormatter())
        self.logger.addHandler(handler)
    
    class JSONFormatter(logging.Formatter):
        def format(self, record):
            log_data = {
                'timestamp': datetime.now().isoformat(),
                'level': record.levelname,
                'logger': record.name,
                'message': record.getMessage(),
                'module': record.module,
                'function': record.funcName,
                'line': record.lineno
            }
            
            # ์ถ”๊ฐ€ ์ปจํ…์ŠคํŠธ ์ •๋ณด
            if hasattr(record, 'user_id'):
                log_data['user_id'] = record.user_id
            if hasattr(record, 'ip_address'):
                log_data['ip_address'] = record.ip_address
            if hasattr(record, 'action'):
                log_data['action'] = record.action
            
            return json.dumps(log_data)
    
    def log_security_event(self, event_type, user_id, ip_address, details):
        """๋ณด์•ˆ ์ด๋ฒคํŠธ ๋กœ๊น…"""
        extra = {
            'user_id': user_id,
            'ip_address': ip_address,
            'action': event_type
        }
        
        self.logger.warning(
            f"Security event: {event_type} - {details}",
            extra=extra
        )

# ์‚ฌ์šฉ
logger = StructuredLogger('security')
logger.log_security_event(
    event_type='login_failure',
    user_id='user123',
    ip_address='192.168.1.100',
    details='Invalid password'
)
2. ์•Œ๋ฆผ ํ”ผ๋กœ๋„ ๊ด€๋ฆฌ

๋„ˆ๋ฌด ๋งŽ์€ ์•Œ๋ฆผ์€ ์˜คํžˆ๋ ค ์—ญํšจ๊ณผ์•ผ. ์ค‘์š”ํ•œ ์•Œ๋ฆผ์„ ๋†“์น  ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๊ทธ๋ž˜์„œ ์•Œ๋ฆผ์„ ์ž˜ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด!

class AlertManager:
    def __init__(self):
        self.alert_history = defaultdict(list)
        self.cooldown_period = 300  # 5๋ถ„
        
    def should_send_alert(self, alert_type, ip_address):
        """์•Œ๋ฆผ ์ „์†ก ์—ฌ๋ถ€ ๊ฒฐ์ •"""
        key = f"{alert_type}:{ip_address}"
        now = datetime.now()
        
        # ์ตœ๊ทผ ์•Œ๋ฆผ ํ™•์ธ
        recent_alerts = self.alert_history[key]
        recent_alerts = [t for t in recent_alerts 
                        if (now - t).total_seconds() < self.cooldown_period]
        
        if recent_alerts:
            print(f"โธ๏ธ ์•Œ๋ฆผ ์ฟจ๋‹ค์šด ์ค‘: {key}")
            return False
        
        # ์•Œ๋ฆผ ๊ธฐ๋ก
        self.alert_history[key].append(now)
        return True
    
    def send_alert(self, alert_type, ip_address, details):
        """์•Œ๋ฆผ ์ „์†ก"""
        if self.should_send_alert(alert_type, ip_address):
            print(f"๐Ÿ”” ์•Œ๋ฆผ ์ „์†ก: {alert_type}")
            print(f"   IP: {ip_address}")
            print(f"   ์ƒ์„ธ: {details}")
            # ์‹ค์ œ ์•Œ๋ฆผ ์ „์†ก ๋กœ์ง
        else:
            print(f"โญ๏ธ ์•Œ๋ฆผ ์Šคํ‚ต (์ฟจ๋‹ค์šด)")

alert_manager = AlertManager()
alert_manager.send_alert('sql_injection', '192.168.1.100', 'Detected in /api/users')
3. ์ •๊ธฐ์ ์ธ ์‹œ์Šคํ…œ ์ ๊ฒ€

๋กœ๊ทธ ๋ถ„์„ ์‹œ์Šคํ…œ ์ž์ฒด๋„ ๋ชจ๋‹ˆํ„ฐ๋งํ•ด์•ผ ํ•ด. ์‹œ์Šคํ…œ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ณต๊ฒฉ์„ ๋†“์น  ์ˆ˜ ์žˆ๊ฑฐ๋“ !

class SystemHealthChecker:
    def __init__(self, system):
        self.system = system
        
    def check_health(self):
        """์‹œ์Šคํ…œ ๊ฑด๊ฐ• ์ƒํƒœ ์ฒดํฌ"""
        issues = []
        
        # 1. ๋””์Šคํฌ ๊ณต๊ฐ„ ์ฒดํฌ
        disk_usage = self._check_disk_space()
        if disk_usage > 80:
            issues.append(f"๋””์Šคํฌ ์‚ฌ์šฉ๋ฅ  ๋†’์Œ: {disk_usage}%")
        
        # 2. ๋กœ๊ทธ ํŒŒ์ผ ์ ‘๊ทผ ๊ฐ€๋Šฅ ์—ฌ๋ถ€
        if not self._check_log_file_access():
            issues.append("๋กœ๊ทธ ํŒŒ์ผ ์ ‘๊ทผ ๋ถˆ๊ฐ€")
        
        # 3. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ
        if not self._check_database_connection():
            issues.append("๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์‹คํŒจ")
        
        # 4. ์ตœ๊ทผ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ํ™•์ธ
        last_event_time = self._get_last_event_time()
        if last_event_time:
            time_diff = (datetime.now() - last_event_time).total_seconds()
            if time_diff > 600:  # 10๋ถ„
                issues.append(f"์ตœ๊ทผ ์ด๋ฒคํŠธ ์—†์Œ ({time_diff/60:.1f}๋ถ„)")
        
        return len(issues) == 0, issues
    
    def _check_disk_space(self):
        """๋””์Šคํฌ ๊ณต๊ฐ„ ์ฒดํฌ"""
        import shutil
        total, used, free = shutil.disk_usage("/")
        return (used / total) * 100
    
    def _check_log_file_access(self):
        """๋กœ๊ทธ ํŒŒ์ผ ์ ‘๊ทผ ์ฒดํฌ"""
        try:
            with open('/var/log/apache2/access.log', 'r') as f:
                f.read(1)
            return True
        except:
            return False
    
    def _check_database_connection(self):
        """๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ฒดํฌ"""
        try:
            conn = sqlite3.connect(self.system.db_path)
            conn.execute('SELECT 1')
            conn.close()
            return True
        except:
            return False
    
    def _get_last_event_time(self):
        """๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ ์‹œ๊ฐ„"""
        try:
            conn = sqlite3.connect(self.system.db_path)
            cursor = conn.cursor()
            cursor.execute('SELECT MAX(created_at) FROM security_events')
            result = cursor.fetchone()[0]
            conn.close()
            
            if result:
                return datetime.fromisoformat(result)
        except:
            pass
        return None

# ์ •๊ธฐ ์ ๊ฒ€ ์‹คํ–‰
health_checker = SystemHealthChecker(system)
is_healthy, issues = health_checker.check_health()

if not is_healthy:
    print("โš ๏ธ ์‹œ์Šคํ…œ ๊ฑด๊ฐ• ์ƒํƒœ ์ด์ƒ!")
    for issue in issues:
        print(f"   - {issue}")
else:
    print("โœ… ์‹œ์Šคํ…œ ์ •์ƒ ์ž‘๋™ ์ค‘")

๐ŸŽ“ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ๐Ÿ˜… ์šฐ๋ฆฌ๋Š” ์˜ค๋Š˜ ๋กœ๊ทธ ํŒŒ์ผ ๋ถ„์„๊ณผ ๋ณด์•ˆ ์ด๋ฒคํŠธ ํƒ์ง€์— ๋Œ€ํ•ด ์ •๋ง ๋งŽ์€ ๊ฑธ ๋ฐฐ์› ์–ด!

๐ŸŽฏ ํ•ต์‹ฌ ์š”์•ฝ

1๏ธโƒฃ ๋กœ๊ทธ๋Š” ์‹œ์Šคํ…œ์˜ ๋ธ”๋ž™๋ฐ•์Šค - ๋ชจ๋“  ํ™œ๋™์ด ๊ธฐ๋ก๋˜๋ฏ€๋กœ ๋ณด์•ˆ์˜ ํ•ต์‹ฌ์ด์•ผ

2๏ธโƒฃ ๋‹ค์–‘ํ•œ ํƒ์ง€ ๊ธฐ๋ฒ• ์กฐํ•ฉ - ํŒจํ„ด ๋งค์นญ, ๋นˆ๋„ ๋ถ„์„, ์ด์ƒ ์ง•ํ›„ ํƒ์ง€, ๋จธ์‹ ๋Ÿฌ๋‹์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํšจ๊ณผ์ ์ด์•ผ

3๏ธโƒฃ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ์ค‘์š” - ๊ณต๊ฒฉ์„ ๋นจ๋ฆฌ ๋ฐœ๊ฒฌํ• ์ˆ˜๋ก ํ”ผํ•ด๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์–ด

4๏ธโƒฃ ์ž๋™ํ™”๊ฐ€ ํ•ต์‹ฌ - ์‚ฌ๋žŒ์ด ๋ชจ๋“  ๋กœ๊ทธ๋ฅผ ์ผ์ผ์ด ํ™•์ธํ•  ์ˆ˜๋Š” ์—†์œผ๋‹ˆ๊นŒ

5๏ธโƒฃ ์ง€์†์ ์ธ ๊ฐœ์„  - ๊ณต๊ฒฉ ๊ธฐ๋ฒ•์€ ๊ณ„์† ์ง„ํ™”ํ•˜๋ฏ€๋กœ ํƒ์ง€ ์‹œ์Šคํ…œ๋„ ํ•จ๊ป˜ ๋ฐœ์ „ํ•ด์•ผ ํ•ด

๋กœ๊ทธ ๋ถ„์„์€ ๋‹จ์ˆœํžˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ ์ด์ƒ์ด์•ผ. ์‹œ์Šคํ…œ์„ ์ดํ•ดํ•˜๊ณ , ๊ณต๊ฒฉ์ž์˜ ๊ด€์ ์—์„œ ์ƒ๊ฐํ•˜๊ณ , ๋ฐ์ดํ„ฐ ์†์—์„œ ์˜๋ฏธ๋ฅผ ์ฐพ์•„๋‚ด๋Š” ์ข…ํ•ฉ์ ์ธ ๋Šฅ๋ ฅ์ด ํ•„์š”ํ•˜์ง€. ๐Ÿง 

์ฒ˜์Œ์—๋Š” ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•˜๋‚˜์”ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๊ตฌํ˜„ํ•ด๋ณด๋ฉด ์ •๋ง ์žฌ๋ฏธ์žˆ์–ด! ํŠนํžˆ ์‹ค์ œ๋กœ ๊ณต๊ฒฉ์„ ํƒ์ง€ํ–ˆ์„ ๋•Œ์˜ ๊ทธ ์งœ๋ฆฟํ•จ์€... ๋ง๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†์–ด! ๐Ÿ˜Ž

ํ˜น์‹œ ๋” ๊นŠ์ด ์žˆ๋Š” ํ•™์Šต์ด๋‚˜ ์‹ค๋ฌด ์ ์šฉ์— ์–ด๋ ค์›€์ด ์žˆ๋‹ค๋ฉด, ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์˜ ๋กœ๊ทธ ๋ถ„์„ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ๋Š” ๊ฒฝํ—˜์ด ์ •๋ง ์ค‘์š”ํ•˜๊ฑฐ๋“ !

์ž, ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„๋„ ์ง์ ‘ ๋กœ๊ทธ ๋ถ„์„ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค์–ด๋ณด๋Š” ๊ฑด ์–ด๋•Œ? ์—ฌ๋Ÿฌ๋ถ„์˜ ์‹œ์Šคํ…œ์„ ๋” ์•ˆ์ „ํ•˜๊ฒŒ ์ง€์ผœ์ค„ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ’ช

๊ถ๊ธˆํ•œ ์ ์ด ์žˆ๊ฑฐ๋‚˜ ๋” ์•Œ๊ณ  ์‹ถ์€ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“  ๋ฌผ์–ด๋ด! ํ•จ๊ป˜ ๋ฐฐ์šฐ๊ณ  ์„ฑ์žฅํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•˜๋‹ˆ๊นŒ! ๐ŸŒŸ

ํ•ดํ”ผ ์ฝ”๋”ฉ, ๊ทธ๋ฆฌ๊ณ  ์•ˆ์ „ํ•œ ์‹œ์Šคํ…œ ์šด์˜ ๋˜๊ธธ ๋ฐ”๋ผ! ๐Ÿš€โœจ
Happy Coding! Stay Secure, Stay Safe
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0