์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ›ก๏ธ SQL Injection ๊ณต๊ฒฉ ํƒ์ง€ ์†Œ์Šค์ฝ”๋“œ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๐Ÿ›ก๏ธ SQL Injection ๊ณต๊ฒฉ ํƒ์ง€ ์†Œ์Šค์ฝ”๋“œ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

ํ•ด์ปค๋“ค์˜ ๊ฐ€์žฅ ์‚ฌ๋ž‘ํ•˜๋Š” ๊ณต๊ฒฉ๋ฒ•์„ ๋ง‰์•„๋‚ด๋Š” ์‹ค์ „ ์ฝ”๋“œ ๋Œ€๊ณต๊ฐœ!

SQL HACK SQL Injection Detection Your Code Guardian Filter Validate

๐ŸŽฏ SQL Injection, ์™œ ์ด๋ ‡๊ฒŒ ๋ฌด์„œ์šด ๊ฑธ๊นŒ?

์•ˆ๋…•! ์˜ค๋Š˜์€ ์›น ๋ณด์•ˆ์—์„œ ๊ฐ€์žฅ ์•…๋ช… ๋†’์€ ๊ณต๊ฒฉ ์ค‘ ํ•˜๋‚˜์ธ SQL Injection์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณผ๊ฒŒ. ๐Ÿ˜Š

์ƒ์ƒํ•ด๋ด. ๋„ˆ๊ฐ€ ์šด์˜ํ•˜๋Š” ์›น์‚ฌ์ดํŠธ์— ๋ˆ„๊ตฐ๊ฐ€ ๋กœ๊ทธ์ธํ•˜๋ ค๊ณ  ํ•˜๋Š”๋ฐ, ์•„์ด๋”” ์ž…๋ ฅ๋ž€์— ' OR '1'='1 ๊ฐ™์€ ์ด์ƒํ•œ ๋ฌธ์ž์—ด์„ ๋„ฃ์—ˆ์–ด. ๊ทธ๋Ÿฐ๋ฐ ๊ฐ‘์ž๊ธฐ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์œผ๋กœ ๋กœ๊ทธ์ธ์ด ๋˜์–ด๋ฒ„๋ฆฐ๋‹ค๋ฉด? ๐Ÿ˜ฑ ์ด๊ฒŒ ๋ฐ”๋กœ SQL Injection ๊ณต๊ฒฉ์ด์•ผ.

โš ๏ธ ์‹ค์ œ ํ”ผํ•ด ์‚ฌ๋ก€ 2017๋…„ ์—ํ€ดํŒฉ์Šค(Equifax) ํ•ดํ‚น ์‚ฌ๊ฑด์—์„œ๋Š” SQL Injection ์ทจ์•ฝ์ ์œผ๋กœ ์ธํ•ด 1์–ต 4์ฒœ๋งŒ ๋ช…์˜ ๊ฐœ์ธ์ •๋ณด๊ฐ€ ์œ ์ถœ๋˜์—ˆ์–ด. ์ด ์‚ฌ๊ฑด์œผ๋กœ ํšŒ์‚ฌ๋Š” 7์–ต ๋‹ฌ๋Ÿฌ ์ด์ƒ์˜ ํ•ฉ์˜๊ธˆ์„ ์ง€๋ถˆํ–ˆ์ง€. ๐Ÿ˜ฐ

SQL Injection์€ OWASP Top 10์—์„œ ํ•ญ์ƒ ์ƒ์œ„๊ถŒ์„ ์ฐจ์ง€ํ•˜๋Š” ์œ„ํ—˜ํ•œ ๊ณต๊ฒฉ์ด์•ผ. ๊ณต๊ฒฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ์กฐ์ž‘ํ•ด์„œ ์ธ์ฆ์„ ์šฐํšŒํ•˜๊ฑฐ๋‚˜, ๋ฐ์ดํ„ฐ๋ฅผ ํ›”์น˜๊ฑฐ๋‚˜, ์‹ฌ์ง€์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์™„์ „ํžˆ ์‚ญ์ œํ•  ์ˆ˜๋„ ์žˆ์–ด. ๐Ÿ’€

๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ ์ด๋Ÿฐ ๊ณต๊ฒฉ์„ ํƒ์ง€ํ•˜๊ณ  ๋ง‰์•„๋‚ด๋Š” ์‹ค์ „ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํ•จ๊ป˜ ์‚ดํŽด๋ณผ ๊ฑฐ์•ผ. ํ”„๋กœ๊ทธ๋žจ ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ ๋ณด์•ˆ์„ ๊ณ ๋ คํ•˜๋Š” ๊ฑด ์„ ํƒ์ด ์•„๋‹ˆ๋ผ ํ•„์ˆ˜๊ฑฐ๋“ ! ๐Ÿ”

๐Ÿ” SQL Injection ๊ณต๊ฒฉ์˜ ์›๋ฆฌ ์ดํ•ดํ•˜๊ธฐ

๋จผ์ € ๊ณต๊ฒฉ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ์•„์•ผ ๋ง‰์„ ์ˆ˜ ์žˆ๊ฒ ์ง€? ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ์„ค๋ช…ํ•ด์ค„๊ฒŒ.

์ผ๋ฐ˜์ ์ธ ๋กœ๊ทธ์ธ ์ฟผ๋ฆฌ๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ์–ด:

SELECT * FROM users WHERE username = 'admin' AND password = 'mypassword';

์ด์ œ ๊ณต๊ฒฉ์ž๊ฐ€ username ํ•„๋“œ์— admin' --๋ฅผ ์ž…๋ ฅํ•œ๋‹ค๋ฉด?

SELECT * FROM users WHERE username = 'admin' -- ' AND password = 'anything';

์—ฌ๊ธฐ์„œ --๋Š” SQL ์ฃผ์„์ด์•ผ. ๊ทธ ๋’ค์˜ ๋ชจ๋“  ๋‚ด์šฉ์ด ๋ฌด์‹œ๋˜๋Š” ๊ฑฐ์ง€. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ ์—†์ด admin ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ผ. ๐Ÿ˜จ

๐Ÿ’ก SQL Injection์˜ ์ฃผ์š” ์œ ํ˜• 1. Classic SQL Injection: ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ๋กœ, ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ”๋กœ ํ™”๋ฉด์— ํ‘œ์‹œ๋จ
2. Blind SQL Injection: ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ์ง์ ‘ ๋ณด์ด์ง€ ์•Š์ง€๋งŒ, ์ฐธ/๊ฑฐ์ง“ ์‘๋‹ต์œผ๋กœ ์ •๋ณด๋ฅผ ์ถ”์ถœ
3. Time-based Blind: ์‘๋‹ต ์‹œ๊ฐ„ ์ฐจ์ด๋ฅผ ์ด์šฉํ•ด ์ •๋ณด๋ฅผ ์–ป์–ด๋ƒ„
4. Union-based: UNION ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ์ถ”์ถœ
5. Error-based: ์˜๋„์ ์œผ๋กœ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ •๋ณด๋ฅผ ์–ป์Œ

์ด๋Ÿฐ ๊ณต๊ฒฉ๋“ค์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ๊ณผ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐํ™”๊ฐ€ ํ•ต์‹ฌ์ด์•ผ. ์ง€๊ธˆ๋ถ€ํ„ฐ ์‹ค์ œ ์ฝ”๋“œ๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ๋ณด์—ฌ์ค„๊ฒŒ! ๐Ÿš€

๐Ÿ› ๏ธ ๊ธฐ๋ณธ์ ์ธ SQL Injection ํƒ์ง€ ํŒจํ„ด

SQL Injection ๊ณต๊ฒฉ์—๋Š” ํŠน์ •ํ•œ ํŒจํ„ด๋“ค์ด ์žˆ์–ด. ์ด๋Ÿฐ ํŒจํ„ด๋“ค์„ ์ •๊ทœํ‘œํ˜„์‹์œผ๋กœ ํƒ์ง€ํ•˜๋Š” ๊ฒŒ ์ฒซ ๋ฒˆ์งธ ๋ฐฉ์–ด์„ ์ด์•ผ.

๐Ÿ“Œ Python์œผ๋กœ ๊ตฌํ˜„ํ•œ ๊ธฐ๋ณธ ํƒ์ง€ ํ•จ์ˆ˜

import re

def detect_sql_injection(user_input):
    """
    SQL Injection ๊ณต๊ฒฉ ํŒจํ„ด์„ ํƒ์ง€ํ•˜๋Š” ํ•จ์ˆ˜
    
    Args:
        user_input (str): ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ์ž…๋ ฅ๊ฐ’
        
    Returns:
        tuple: (is_malicious, detected_patterns)
    """
    
    # SQL Injection ๊ณต๊ฒฉ์— ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด๋“ค
    sql_patterns = [
        r"(\b(SELECT|UNION|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE)\b)",
        r"(--|\#|\/\*|\*\/)",  # SQL ์ฃผ์„
        r"(\bOR\b.*=.*)",  # OR 1=1 ๊ฐ™์€ ํŒจํ„ด
        r"(\bAND\b.*=.*)",  # AND 1=1 ๊ฐ™์€ ํŒจํ„ด
        r"('|\"|;|\\)",  # ํŠน์ˆ˜๋ฌธ์ž
        r"(\bxp_cmdshell\b)",  # ์‹œ์Šคํ…œ ๋ช…๋ น ์‹คํ–‰
        r"(\bSLEEP\b|\bBENCHMARK\b)",  # Time-based ๊ณต๊ฒฉ
        r"(\bINFORMATION_SCHEMA\b)",  # ์Šคํ‚ค๋งˆ ์ •๋ณด ์กฐํšŒ
        r"(\bLOAD_FILE\b|\bINTO\s+OUTFILE\b)",  # ํŒŒ์ผ ์กฐ์ž‘
        r"(\bCHAR\b|\bCONCAT\b|\bSUBSTRING\b)",  # ๋ฌธ์ž์—ด ํ•จ์ˆ˜ ์•…์šฉ
    ]
    
    detected_patterns = []
    
    # ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์ด ๊ฒ€์‚ฌ
    input_upper = user_input.upper()
    
    for pattern in sql_patterns:
        if re.search(pattern, input_upper, re.IGNORECASE):
            detected_patterns.append(pattern)
    
    is_malicious = len(detected_patterns) > 0
    
    return is_malicious, detected_patterns


# ์‚ฌ์šฉ ์˜ˆ์ œ
test_inputs = [
    "admin",  # ์ •์ƒ ์ž…๋ ฅ
    "admin' OR '1'='1",  # SQL Injection ์‹œ๋„
    "admin'; DROP TABLE users; --",  # ์œ„ํ—˜ํ•œ ๊ณต๊ฒฉ
    "user123",  # ์ •์ƒ ์ž…๋ ฅ
    "' UNION SELECT * FROM passwords --",  # UNION ๊ณต๊ฒฉ
]

print("๐Ÿ” SQL Injection ํƒ์ง€ ํ…Œ์ŠคํŠธ\n")
for test_input in test_inputs:
    is_malicious, patterns = detect_sql_injection(test_input)
    status = "๐Ÿšจ ์œ„ํ—˜" if is_malicious else "โœ… ์•ˆ์ „"
    print(f"{status} | ์ž…๋ ฅ: {test_input}")
    if patterns:
        print(f"   ํƒ์ง€๋œ ํŒจํ„ด: {len(patterns)}๊ฐœ\n")
๐Ÿ’ก Pro Tip: ์ •๊ทœํ‘œํ˜„์‹๋งŒ์œผ๋กœ๋Š” ์™„๋ฒฝํ•œ ๋ฐฉ์–ด๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด. ์ด๊ฑด ์ฒซ ๋ฒˆ์งธ ํ•„ํ„ฐ์ผ ๋ฟ์ด๊ณ , ๋’ค์—์„œ ์„ค๋ช…ํ•  Prepared Statement์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ด! ๐ŸŽฏ

๐Ÿ“Œ JavaScript/Node.js ๋ฒ„์ „

ํ”„๋ก ํŠธ์—”๋“œ์—์„œ๋„ 1์ฐจ ๊ฒ€์ฆ์„ ํ•  ์ˆ˜ ์žˆ์–ด. ๋ฌผ๋ก  ์„œ๋ฒ„ ์ธก ๊ฒ€์ฆ์ด ๋” ์ค‘์š”ํ•˜์ง€๋งŒ, ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ๋ฏธ๋ฆฌ ๊ฑธ๋Ÿฌ๋‚ด๋ฉด ๋ถˆํ•„์š”ํ•œ ์š”์ฒญ์„ ์ค„์ผ ์ˆ˜ ์žˆ์ง€!

/**
 * SQL Injection ํŒจํ„ด ํƒ์ง€ (JavaScript)
 * @param {string} userInput - ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’
 * @returns {Object} - {isMalicious, detectedPatterns}
 */
function detectSQLInjection(userInput) {
    const sqlPatterns = [
        /\b(SELECT|UNION|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE)\b/gi,
        /(--|#|\/\*|\*\/)/g,
        /\bOR\b.*=/gi,
        /\bAND\b.*=/gi,
        /['";\\]/g,
        /\bxp_cmdshell\b/gi,
        /\b(SLEEP|BENCHMARK)\b/gi,
        /\bINFORMATION_SCHEMA\b/gi,
        /\b(LOAD_FILE|INTO\s+OUTFILE)\b/gi,
        /\b(CHAR|CONCAT|SUBSTRING)\b/gi,
    ];
    
    const detectedPatterns = [];
    
    sqlPatterns.forEach((pattern, index) => {
        if (pattern.test(userInput)) {
            detectedPatterns.push(index);
        }
    });
    
    return {
        isMalicious: detectedPatterns.length > 0,
        detectedPatterns: detectedPatterns,
        riskLevel: detectedPatterns.length > 2 ? 'HIGH' : 
                   detectedPatterns.length > 0 ? 'MEDIUM' : 'LOW'
    };
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
const testInput = "admin' OR '1'='1";
const result = detectSQLInjection(testInput);

if (result.isMalicious) {
    console.log('๐Ÿšจ SQL Injection ๊ณต๊ฒฉ ํƒ์ง€!');
    console.log(`์œ„ํ—˜๋„: ${result.riskLevel}`);
    console.log(`ํƒ์ง€๋œ ํŒจํ„ด ์ˆ˜: ${result.detectedPatterns.length}`);
}

์ด ์ฝ”๋“œ๋“ค์€ ๊ธฐ๋ณธ์ ์ธ ํŒจํ„ด ๋งค์นญ์„ ์ˆ˜ํ–‰ํ•ด. ํ•˜์ง€๋งŒ ์ง„์งœ ๊ฐ•๋ ฅํ•œ ๋ฐฉ์–ด๋Š” ๋‹ค์Œ ์„น์…˜์—์„œ ๋‹ค๋ฃฐ ๊ฑฐ์•ผ! ๐Ÿ˜Ž

๐Ÿ” Prepared Statement: ์ตœ๊ฐ•์˜ ๋ฐฉ์–ด๋ง‰

์†”์งํžˆ ๋งํ•˜๋ฉด, ์œ„์˜ ํŒจํ„ด ํƒ์ง€๋Š” ๋ณด์กฐ ์ˆ˜๋‹จ์ผ ๋ฟ์ด์•ผ. Prepared Statement๊ฐ€ SQL Injection์„ ๋ง‰๋Š” ๊ฐ€์žฅ ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์ด๊ฑฐ๋“ ! ๐Ÿ›ก๏ธ

Prepared Statement๋Š” ์ฟผ๋ฆฌ ๊ตฌ์กฐ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ์ฒ˜๋ฆฌํ•ด. ๊ทธ๋ž˜์„œ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์ด ์ ˆ๋Œ€ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์–ด.

๐Ÿ“Œ Python (MySQL) ์˜ˆ์ œ

import mysql.connector
from mysql.connector import Error

class SecureDatabase:
    def __init__(self, host, database, user, password):
        """์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ํด๋ž˜์Šค"""
        try:
            self.connection = mysql.connector.connect(
                host=host,
                database=database,
                user=user,
                password=password
            )
            if self.connection.is_connected():
                print("โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์„ฑ๊ณต!")
        except Error as e:
            print(f"โŒ ์—ฐ๊ฒฐ ์‹คํŒจ: {e}")
            self.connection = None
    
    def safe_login(self, username, password):
        """
        Prepared Statement๋ฅผ ์‚ฌ์šฉํ•œ ์•ˆ์ „ํ•œ ๋กœ๊ทธ์ธ
        
        Args:
            username (str): ์‚ฌ์šฉ์ž๋ช…
            password (str): ๋น„๋ฐ€๋ฒˆํ˜ธ
            
        Returns:
            dict: ์‚ฌ์šฉ์ž ์ •๋ณด ๋˜๋Š” None
        """
        if not self.connection:
            return None
        
        try:
            cursor = self.connection.cursor(dictionary=True)
            
            # โŒ ์œ„ํ—˜ํ•œ ๋ฐฉ๋ฒ• (์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜์ง€ ๋ง ๊ฒƒ!)
            # query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
            
            # โœ… ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ• (Prepared Statement)
            query = "SELECT * FROM users WHERE username = %s AND password = %s"
            
            # ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ณ„๋„๋กœ ์ „๋‹ฌ
            cursor.execute(query, (username, password))
            
            result = cursor.fetchone()
            cursor.close()
            
            if result:
                print(f"โœ… ๋กœ๊ทธ์ธ ์„ฑ๊ณต: {result['username']}")
                return result
            else:
                print("โŒ ๋กœ๊ทธ์ธ ์‹คํŒจ: ์ž˜๋ชป๋œ ์ธ์ฆ ์ •๋ณด")
                return None
                
        except Error as e:
            print(f"โŒ ์ฟผ๋ฆฌ ์‹คํ–‰ ์˜ค๋ฅ˜: {e}")
            return None
    
    def safe_search(self, search_term):
        """
        Prepared Statement๋ฅผ ์‚ฌ์šฉํ•œ ์•ˆ์ „ํ•œ ๊ฒ€์ƒ‰
        
        Args:
            search_term (str): ๊ฒ€์ƒ‰์–ด
            
        Returns:
            list: ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ
        """
        if not self.connection:
            return []
        
        try:
            cursor = self.connection.cursor(dictionary=True)
            
            # LIKE ์ฟผ๋ฆฌ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
            query = "SELECT * FROM products WHERE name LIKE %s"
            
            # % ์™€์ผ๋“œ์นด๋“œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์— ํฌํ•จ
            search_pattern = f"%{search_term}%"
            cursor.execute(query, (search_pattern,))
            
            results = cursor.fetchall()
            cursor.close()
            
            print(f"โœ… ๊ฒ€์ƒ‰ ์™„๋ฃŒ: {len(results)}๊ฐœ ๊ฒฐ๊ณผ")
            return results
            
        except Error as e:
            print(f"โŒ ๊ฒ€์ƒ‰ ์˜ค๋ฅ˜: {e}")
            return []
    
    def safe_insert(self, username, email, password):
        """
        Prepared Statement๋ฅผ ์‚ฌ์šฉํ•œ ์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
        
        Args:
            username (str): ์‚ฌ์šฉ์ž๋ช…
            email (str): ์ด๋ฉ”์ผ
            password (str): ๋น„๋ฐ€๋ฒˆํ˜ธ
            
        Returns:
            bool: ์„ฑ๊ณต ์—ฌ๋ถ€
        """
        if not self.connection:
            return False
        
        try:
            cursor = self.connection.cursor()
            
            query = "INSERT INTO users (username, email, password) VALUES (%s, %s, %s)"
            cursor.execute(query, (username, email, password))
            
            self.connection.commit()
            cursor.close()
            
            print(f"โœ… ์‚ฌ์šฉ์ž '{username}' ๋“ฑ๋ก ์™„๋ฃŒ")
            return True
            
        except Error as e:
            print(f"โŒ ๋“ฑ๋ก ์‹คํŒจ: {e}")
            self.connection.rollback()
            return False
    
    def close(self):
        """๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ข…๋ฃŒ"""
        if self.connection and self.connection.is_connected():
            self.connection.close()
            print("โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ข…๋ฃŒ")


# ์‚ฌ์šฉ ์˜ˆ์ œ
if __name__ == "__main__":
    db = SecureDatabase(
        host="localhost",
        database="myapp",
        user="dbuser",
        password="dbpassword"
    )
    
    # ๊ณต๊ฒฉ ์‹œ๋„๋ฅผ ๋ง‰์•„๋‚ด๋Š” ํ…Œ์ŠคํŠธ
    malicious_input = "admin' OR '1'='1"
    db.safe_login(malicious_input, "anything")
    # ๊ฒฐ๊ณผ: Prepared Statement๊ฐ€ ์ด๊ฒƒ์„ ๋‹จ์ˆœ ๋ฌธ์ž์—ด๋กœ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ๊ณต๊ฒฉ ์‹คํŒจ!
    
    # ์ •์ƒ ๋กœ๊ทธ์ธ
    db.safe_login("john_doe", "correct_password")
    
    # ์•ˆ์ „ํ•œ ๊ฒ€์ƒ‰
    db.safe_search("laptop")
    
    db.close()
๐ŸŽฏ Prepared Statement์˜ ์ž‘๋™ ์›๋ฆฌ 1. ์ฟผ๋ฆฌ ๊ตฌ์กฐ ์ „์†ก: ๋จผ์ € ์ฟผ๋ฆฌ์˜ ๊ตฌ์กฐ๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „์†ก (ํ”Œ๋ ˆ์ด์Šคํ™€๋” ํฌํ•จ)
2. ์ฟผ๋ฆฌ ์ปดํŒŒ์ผ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฏธ๋ฆฌ ์ปดํŒŒ์ผ
3. ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ: ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ๋ณ„๋„๋กœ ์ „์†กํ•˜์—ฌ ๋ฐ”์ธ๋”ฉ
4. ์‹คํ–‰: ๋ฐ”์ธ๋”ฉ๋œ ๊ฐ’์€ ์ ˆ๋Œ€ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ' OR '1'='1 ๊ฐ™์€ ์ž…๋ ฅ๋„ ๊ทธ๋ƒฅ ๋ฌธ์ž์—ด๋กœ ์ฒ˜๋ฆฌ๋ผ์„œ ๊ณต๊ฒฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•ด์ ธ! ๐ŸŽ‰

๐Ÿ“Œ PHP (PDO) ์˜ˆ์ œ

PHP์—์„œ๋Š” PDO(PHP Data Objects)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด.

<?php
class SecureDatabasePHP {
    private $pdo;
    
    public function __construct($host, $dbname, $username, $password) {
        try {
            $dsn = "mysql:host=$host;dbname=$dbname;charset=utf8mb4";
            $options = [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false, // ์ง„์งœ Prepared Statement ์‚ฌ์šฉ
            ];
            
            $this->pdo = new PDO($dsn, $username, $password, $options);
            echo "โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์„ฑ๊ณต!\n";
            
        } catch (PDOException $e) {
            die("โŒ ์—ฐ๊ฒฐ ์‹คํŒจ: " . $e->getMessage());
        }
    }
    
    /**
     * ์•ˆ์ „ํ•œ ๋กœ๊ทธ์ธ ํ•จ์ˆ˜
     */
    public function safeLogin($username, $password) {
        try {
            // Prepared Statement ์‚ฌ์šฉ
            $stmt = $this->pdo->prepare(
                "SELECT * FROM users WHERE username = :username AND password = :password"
            );
            
            // ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ (Named Parameters)
            $stmt->execute([
                ':username' => $username,
                ':password' => $password
            ]);
            
            $user = $stmt->fetch();
            
            if ($user) {
                echo "โœ… ๋กœ๊ทธ์ธ ์„ฑ๊ณต: {$user['username']}\n";
                return $user;
            } else {
                echo "โŒ ๋กœ๊ทธ์ธ ์‹คํŒจ\n";
                return null;
            }
            
        } catch (PDOException $e) {
            echo "โŒ ์ฟผ๋ฆฌ ์˜ค๋ฅ˜: " . $e->getMessage() . "\n";
            return null;
        }
    }
    
    /**
     * ์•ˆ์ „ํ•œ ๊ฒ€์ƒ‰ ํ•จ์ˆ˜
     */
    public function safeSearch($searchTerm) {
        try {
            $stmt = $this->pdo->prepare(
                "SELECT * FROM products WHERE name LIKE :search"
            );
            
            // LIKE ํŒจํ„ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
            $stmt->execute([
                ':search' => "%$searchTerm%"
            ]);
            
            $results = $stmt->fetchAll();
            echo "โœ… ๊ฒ€์ƒ‰ ์™„๋ฃŒ: " . count($results) . "๊ฐœ ๊ฒฐ๊ณผ\n";
            
            return $results;
            
        } catch (PDOException $e) {
            echo "โŒ ๊ฒ€์ƒ‰ ์˜ค๋ฅ˜: " . $e->getMessage() . "\n";
            return [];
        }
    }
    
    /**
     * ์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
     */
    public function safeInsert($username, $email, $password) {
        try {
            $stmt = $this->pdo->prepare(
                "INSERT INTO users (username, email, password) VALUES (:username, :email, :password)"
            );
            
            $result = $stmt->execute([
                ':username' => $username,
                ':email' => $email,
                ':password' => password_hash($password, PASSWORD_DEFAULT) // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ!
            ]);
            
            if ($result) {
                echo "โœ… ์‚ฌ์šฉ์ž '$username' ๋“ฑ๋ก ์™„๋ฃŒ\n";
                return true;
            }
            
            return false;
            
        } catch (PDOException $e) {
            echo "โŒ ๋“ฑ๋ก ์‹คํŒจ: " . $e->getMessage() . "\n";
            return false;
        }
    }
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
$db = new SecureDatabasePHP('localhost', 'myapp', 'dbuser', 'dbpassword');

// SQL Injection ๊ณต๊ฒฉ ์‹œ๋„ (์‹คํŒจํ•จ!)
$db->safeLogin("admin' OR '1'='1", "anything");

// ์ •์ƒ ์‚ฌ์šฉ
$db->safeLogin("john_doe", "correct_password");
$db->safeSearch("laptop");
$db->safeInsert("new_user", "user@example.com", "securepass123");
?>
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ Prepared Statement๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋„ ์ฃผ์˜ํ•  ์ ์ด ์žˆ์–ด:

1. ํ…Œ์ด๋ธ”๋ช…/์ปฌ๋Ÿผ๋ช…์€ ๋ฐ”์ธ๋”ฉ ๋ถˆ๊ฐ€: ๋™์ ์œผ๋กœ ํ…Œ์ด๋ธ”๋ช…์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค๋ฉด ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์œผ๋กœ ๊ฒ€์ฆ
2. ORDER BY ์ ˆ: ์ •๋ ฌ ์ปฌ๋Ÿผ๋„ ๋ฐ”์ธ๋”ฉ์ด ์•ˆ ๋˜๋ฏ€๋กœ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๊ฒ€์ฆ ํ•„์š”
3. IN ์ ˆ: ์—ฌ๋Ÿฌ ๊ฐ’์„ ๋ฐ”์ธ๋”ฉํ•  ๋•Œ๋Š” ๊ฐ๊ฐ ํ”Œ๋ ˆ์ด์Šคํ™€๋” ์ƒ์„ฑ
4. LIMIT ์ ˆ: MySQL์—์„œ๋Š” LIMIT์— ๋ฐ”์ธ๋”ฉ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์ผ๋ถ€ DB๋Š” ๋ถˆ๊ฐ€๋Šฅ

๐ŸŽจ ๊ณ ๊ธ‰ ํƒ์ง€ ๊ธฐ๋ฒ•: ๋จธ์‹ ๋Ÿฌ๋‹ ํ™œ์šฉ

์ตœ๊ทผ์—๋Š” ๋จธ์‹ ๋Ÿฌ๋‹์„ ํ™œ์šฉํ•ด์„œ SQL Injection์„ ํƒ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๋งŽ์ด ์—ฐ๊ตฌ๋˜๊ณ  ์žˆ์–ด. ํŒจํ„ด ๋งค์นญ๋ณด๋‹ค ํ›จ์”ฌ ์ •๊ตํ•˜๊ฒŒ ๊ณต๊ฒฉ์„ ํƒ์ง€ํ•  ์ˆ˜ ์žˆ์ง€! ๐Ÿค–

๐Ÿ“Œ ๊ฐ„๋‹จํ•œ ML ๊ธฐ๋ฐ˜ ํƒ์ง€ ๋ชจ๋ธ

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score

class MLSQLInjectionDetector:
    """
    ๋จธ์‹ ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜ SQL Injection ํƒ์ง€๊ธฐ
    """
    
    def __init__(self):
        self.vectorizer = TfidfVectorizer(
            analyzer='char',  # ๋ฌธ์ž ๋‹จ์œ„ ๋ถ„์„
            ngram_range=(2, 4),  # 2~4๊ธ€์ž ์กฐํ•ฉ
            max_features=1000
        )
        self.classifier = RandomForestClassifier(
            n_estimators=100,
            random_state=42
        )
        self.is_trained = False
    
    def prepare_training_data(self):
        """
        ํ•™์Šต ๋ฐ์ดํ„ฐ ์ค€๋น„
        ์‹ค์ œ๋กœ๋Š” ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•ด!
        """
        # ์ •์ƒ ์ฟผ๋ฆฌ ์˜ˆ์ œ
        normal_queries = [
            "SELECT * FROM users WHERE id = 1",
            "INSERT INTO products VALUES (1, 'Laptop', 999)",
            "UPDATE users SET email = 'new@email.com' WHERE id = 5",
            "DELETE FROM orders WHERE order_id = 123",
            "admin",
            "john_doe",
            "user@example.com",
            "password123",
            "search term",
            "product name",
        ]
        
        # SQL Injection ๊ณต๊ฒฉ ์˜ˆ์ œ
        malicious_queries = [
            "admin' OR '1'='1",
            "' OR 1=1 --",
            "admin'--",
            "' UNION SELECT * FROM passwords --",
            "1'; DROP TABLE users; --",
            "' OR 'a'='a",
            "admin' AND 1=0 UNION ALL SELECT NULL, NULL, NULL --",
            "' UNION SELECT username, password FROM users --",
            "1' AND SLEEP(5) --",
            "' OR '1'='1' /*",
        ]
        
        # ๋ ˆ์ด๋ธ” ์ƒ์„ฑ (0: ์ •์ƒ, 1: ์•…์˜์ )
        X = normal_queries + malicious_queries
        y = [0] * len(normal_queries) + [1] * len(malicious_queries)
        
        return X, y
    
    def train(self):
        """
        ๋ชจ๋ธ ํ•™์Šต
        """
        print("๐ŸŽ“ ๋ชจ๋ธ ํ•™์Šต ์‹œ์ž‘...")
        
        X, y = self.prepare_training_data()
        
        # ๋ฐ์ดํ„ฐ ๋ถ„ํ• 
        X_train, X_test, y_train, y_test = train_test_split(
            X, y, test_size=0.2, random_state=42
        )
        
        # ํŠน์ง• ๋ฒกํ„ฐํ™”
        X_train_vec = self.vectorizer.fit_transform(X_train)
        X_test_vec = self.vectorizer.transform(X_test)
        
        # ๋ชจ๋ธ ํ•™์Šต
        self.classifier.fit(X_train_vec, y_train)
        
        # ํ‰๊ฐ€
        y_pred = self.classifier.predict(X_test_vec)
        accuracy = accuracy_score(y_test, y_pred)
        
        print(f"โœ… ํ•™์Šต ์™„๋ฃŒ! ์ •ํ™•๋„: {accuracy * 100:.2f}%")
        print("\n๐Ÿ“Š ์ƒ์„ธ ํ‰๊ฐ€:")
        print(classification_report(y_test, y_pred, 
                                   target_names=['์ •์ƒ', '์•…์˜์ ']))
        
        self.is_trained = True
    
    def predict(self, query):
        """
        SQL Injection ์—ฌ๋ถ€ ์˜ˆ์ธก
        
        Args:
            query (str): ๊ฒ€์‚ฌํ•  ์ฟผ๋ฆฌ
            
        Returns:
            dict: ์˜ˆ์ธก ๊ฒฐ๊ณผ
        """
        if not self.is_trained:
            raise Exception("๋ชจ๋ธ์ด ํ•™์Šต๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. train()์„ ๋จผ์ € ์‹คํ–‰ํ•˜์„ธ์š”.")
        
        # ๋ฒกํ„ฐํ™”
        query_vec = self.vectorizer.transform([query])
        
        # ์˜ˆ์ธก
        prediction = self.classifier.predict(query_vec)[0]
        probability = self.classifier.predict_proba(query_vec)[0]
        
        return {
            'is_malicious': bool(prediction),
            'confidence': float(probability[prediction]),
            'malicious_probability': float(probability[1]),
            'normal_probability': float(probability[0])
        }
    
    def batch_predict(self, queries):
        """
        ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ๋ฅผ ํ•œ ๋ฒˆ์— ๊ฒ€์‚ฌ
        """
        results = []
        for query in queries:
            result = self.predict(query)
            result['query'] = query
            results.append(result)
        return results


# ์‚ฌ์šฉ ์˜ˆ์ œ
if __name__ == "__main__":
    detector = MLSQLInjectionDetector()
    
    # ๋ชจ๋ธ ํ•™์Šต
    detector.train()
    
    print("\n" + "="*60)
    print("๐Ÿ” ์‹ค์‹œ๊ฐ„ ํƒ์ง€ ํ…Œ์ŠคํŠธ")
    print("="*60 + "\n")
    
    # ํ…Œ์ŠคํŠธ ์ฟผ๋ฆฌ๋“ค
    test_queries = [
        "john_doe",  # ์ •์ƒ
        "admin' OR '1'='1",  # ๊ณต๊ฒฉ
        "user@example.com",  # ์ •์ƒ
        "' UNION SELECT * FROM users --",  # ๊ณต๊ฒฉ
        "search laptop",  # ์ •์ƒ
        "1'; DROP TABLE products; --",  # ๊ณต๊ฒฉ
    ]
    
    for query in test_queries:
        result = detector.predict(query)
        
        status = "๐Ÿšจ ์œ„ํ—˜" if result['is_malicious'] else "โœ… ์•ˆ์ „"
        confidence = result['confidence'] * 100
        
        print(f"{status} | ์ž…๋ ฅ: {query}")
        print(f"   ์‹ ๋ขฐ๋„: {confidence:.1f}%")
        print(f"   ์•…์˜์  ํ™•๋ฅ : {result['malicious_probability']*100:.1f}%\n")
๐Ÿ’ก ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์˜ ์žฅ์  1. ์ƒˆ๋กœ์šด ๊ณต๊ฒฉ ํŒจํ„ด ํƒ์ง€: ์ •๊ทœํ‘œํ˜„์‹์— ์—†๋Š” ๋ณ€์ข… ๊ณต๊ฒฉ๋„ ํƒ์ง€ ๊ฐ€๋Šฅ
2. ์˜คํƒ๋ฅ  ๊ฐ์†Œ: ํ•™์Šต์„ ํ†ตํ•ด ์ •์ƒ ํŒจํ„ด๊ณผ ์•…์˜์  ํŒจํ„ด์„ ๋” ์ •ํ™•ํžˆ ๊ตฌ๋ถ„
3. ์ง€์†์  ๊ฐœ์„ : ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋กœ ์žฌํ•™์Šตํ•˜๋ฉด ์„ฑ๋Šฅ์ด ๊ณ„์† ํ–ฅ์ƒ๋จ

ํ•˜์ง€๋งŒ ML ๋ชจ๋ธ๋„ ์™„๋ฒฝํ•˜์ง€ ์•Š์•„. Prepared Statement์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ตœ์„ ์ด์•ผ! ๐ŸŽฏ

์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋” ๋งŽ์€ ํ•™์Šต ๋ฐ์ดํ„ฐ(์ˆ˜์ฒœ~์ˆ˜๋งŒ ๊ฐœ)๊ฐ€ ํ•„์š”ํ•ด. ๊ทธ๋ฆฌ๊ณ  ์ •๊ธฐ์ ์œผ๋กœ ๋ชจ๋ธ์„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ์ƒˆ๋กœ์šด ๊ณต๊ฒฉ ํŒจํ„ด์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ“ˆ

๐ŸŒ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฉํ™”๋ฒฝ(WAF) ๊ตฌํ˜„

์ด์ œ ์ข€ ๋” ์‹ค์ „์ ์ธ ๋‚ด์šฉ์œผ๋กœ ๋“ค์–ด๊ฐ€๋ณผ๊นŒ? ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฉํ™”๋ฒฝ(WAF)์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณด์ž! ๐Ÿ”ฅ

WAF๋Š” HTTP ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์„œ ์•…์˜์ ์ธ ํŒจํ„ด์„ ํƒ์ง€ํ•˜๊ณ  ์ฐจ๋‹จํ•˜๋Š” ์—ญํ• ์„ ํ•ด. Express.js๋กœ ๊ฐ„๋‹จํ•œ WAF ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ.

๐Ÿ“Œ Node.js/Express WAF ๋ฏธ๋“ค์›จ์–ด

const express = require('express');
const app = express();

/**
 * SQL Injection ๋ฐฉ์–ด WAF ๋ฏธ๋“ค์›จ์–ด
 */
class SQLInjectionWAF {
    constructor(options = {}) {
        this.options = {
            logAttacks: options.logAttacks !== false,
            blockMode: options.blockMode !== false,
            maxRiskScore: options.maxRiskScore || 5,
            ...options
        };
        
        // ์œ„ํ—˜ ํŒจํ„ด๊ณผ ์ ์ˆ˜
        this.riskPatterns = [
            { pattern: /(\bSELECT\b.*\bFROM\b)/gi, score: 3, name: 'SELECT_FROM' },
            { pattern: /(\bUNION\b.*\bSELECT\b)/gi, score: 5, name: 'UNION_SELECT' },
            { pattern: /(\bINSERT\b.*\bINTO\b)/gi, score: 4, name: 'INSERT_INTO' },
            { pattern: /(\bDELETE\b.*\bFROM\b)/gi, score: 5, name: 'DELETE_FROM' },
            { pattern: /(\bDROP\b.*\bTABLE\b)/gi, score: 10, name: 'DROP_TABLE' },
            { pattern: /(\bUPDATE\b.*\bSET\b)/gi, score: 4, name: 'UPDATE_SET' },
            { pattern: /(--|#|\/\*|\*\/)/g, score: 2, name: 'SQL_COMMENT' },
            { pattern: /(\bOR\b\s*['"]?\d+['"]?\s*=\s*['"]?\d+)/gi, score: 5, name: 'OR_EQUALS' },
            { pattern: /(\bAND\b\s*['"]?\d+['"]?\s*=\s*['"]?\d+)/gi, score: 5, name: 'AND_EQUALS' },
            { pattern: /('|\"|;|\\)/g, score: 1, name: 'SPECIAL_CHAR' },
            { pattern: /(\bxp_cmdshell\b)/gi, score: 10, name: 'XP_CMDSHELL' },
            { pattern: /(\bEXEC\b|\bEXECUTE\b)/gi, score: 4, name: 'EXEC' },
            { pattern: /(\bSLEEP\b|\bBENCHMARK\b)/gi, score: 3, name: 'TIME_BASED' },
            { pattern: /(\bINFORMATION_SCHEMA\b)/gi, score: 4, name: 'INFO_SCHEMA' },
        ];
        
        this.attackLog = [];
    }
    
    /**
     * ์ž…๋ ฅ๊ฐ’ ๊ฒ€์‚ฌ
     */
    analyzeInput(input) {
        let totalScore = 0;
        const detectedPatterns = [];
        
        if (typeof input !== 'string') {
            input = String(input);
        }
        
        this.riskPatterns.forEach(({ pattern, score, name }) => {
            const matches = input.match(pattern);
            if (matches) {
                totalScore += score * matches.length;
                detectedPatterns.push({
                    name,
                    score,
                    matches: matches.length
                });
            }
        });
        
        return {
            isMalicious: totalScore >= this.options.maxRiskScore,
            riskScore: totalScore,
            detectedPatterns
        };
    }
    
    /**
     * ์š”์ฒญ ์ „์ฒด ๊ฒ€์‚ฌ
     */
    scanRequest(req) {
        const results = {
            isMalicious: false,
            totalRiskScore: 0,
            findings: []
        };
        
        // Query Parameters ๊ฒ€์‚ฌ
        if (req.query && Object.keys(req.query).length > 0) {
            Object.entries(req.query).forEach(([key, value]) => {
                const analysis = this.analyzeInput(value);
                if (analysis.isMalicious) {
                    results.isMalicious = true;
                    results.findings.push({
                        location: 'query',
                        parameter: key,
                        value,
                        ...analysis
                    });
                }
                results.totalRiskScore += analysis.riskScore;
            });
        }
        
        // Body Parameters ๊ฒ€์‚ฌ
        if (req.body && Object.keys(req.body).length > 0) {
            Object.entries(req.body).forEach(([key, value]) => {
                const analysis = this.analyzeInput(value);
                if (analysis.isMalicious) {
                    results.isMalicious = true;
                    results.findings.push({
                        location: 'body',
                        parameter: key,
                        value,
                        ...analysis
                    });
                }
                results.totalRiskScore += analysis.riskScore;
            });
        }
        
        // URL Path ๊ฒ€์‚ฌ
        const pathAnalysis = this.analyzeInput(req.path);
        if (pathAnalysis.isMalicious) {
            results.isMalicious = true;
            results.findings.push({
                location: 'path',
                value: req.path,
                ...pathAnalysis
            });
        }
        results.totalRiskScore += pathAnalysis.riskScore;
        
        return results;
    }
    
    /**
     * ๊ณต๊ฒฉ ๋กœ๊น…
     */
    logAttack(req, scanResults) {
        const logEntry = {
            timestamp: new Date().toISOString(),
            ip: req.ip || req.connection.remoteAddress,
            method: req.method,
            url: req.originalUrl,
            userAgent: req.get('user-agent'),
            ...scanResults
        };
        
        this.attackLog.push(logEntry);
        
        console.log('๐Ÿšจ SQL Injection ๊ณต๊ฒฉ ํƒ์ง€!');
        console.log(`   IP: ${logEntry.ip}`);
        console.log(`   URL: ${logEntry.method} ${logEntry.url}`);
        console.log(`   ์œ„ํ—˜ ์ ์ˆ˜: ${logEntry.totalRiskScore}`);
        console.log(`   ํƒ์ง€ ํ•ญ๋ชฉ: ${logEntry.findings.length}๊ฐœ\n`);
    }
    
    /**
     * Express ๋ฏธ๋“ค์›จ์–ด
     */
    middleware() {
        return (req, res, next) => {
            const scanResults = this.scanRequest(req);
            
            if (scanResults.isMalicious) {
                if (this.options.logAttacks) {
                    this.logAttack(req, scanResults);
                }
                
                if (this.options.blockMode) {
                    return res.status(403).json({
                        error: 'Forbidden',
                        message: 'SQL Injection ๊ณต๊ฒฉ์ด ํƒ์ง€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
                        requestId: Date.now()
                    });
                }
            }
            
            // ์š”์ฒญ ๊ฐ์ฒด์— ์Šค์บ” ๊ฒฐ๊ณผ ์ถ”๊ฐ€
            req.sqlInjectionScan = scanResults;
            next();
        };
    }
    
    /**
     * ๊ณต๊ฒฉ ๋กœ๊ทธ ์กฐํšŒ
     */
    getAttackLog(limit = 10) {
        return this.attackLog.slice(-limit);
    }
}

// WAF ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
const waf = new SQLInjectionWAF({
    logAttacks: true,
    blockMode: true,
    maxRiskScore: 5
});

// Express ์„ค์ •
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// WAF ๋ฏธ๋“ค์›จ์–ด ์ ์šฉ
app.use(waf.middleware());

// ํ…Œ์ŠคํŠธ ๋ผ์šฐํŠธ
app.get('/search', (req, res) => {
    const searchTerm = req.query.q;
    res.json({
        message: '๊ฒ€์ƒ‰ ์™„๋ฃŒ',
        query: searchTerm,
        securityScan: req.sqlInjectionScan
    });
});

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    res.json({
        message: '๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ',
        username,
        securityScan: req.sqlInjectionScan
    });
});

// ๊ณต๊ฒฉ ๋กœ๊ทธ ์กฐํšŒ ์—”๋“œํฌ์ธํŠธ
app.get('/admin/attack-log', (req, res) => {
    res.json({
        attacks: waf.getAttackLog(20)
    });
});

// ์„œ๋ฒ„ ์‹œ์ž‘
const PORT = 3000;
app.listen(PORT, () => {
    console.log(`โœ… WAF ์„œ๋ฒ„ ์‹คํ–‰ ์ค‘: http://localhost:${PORT}`);
    console.log('๐Ÿ›ก๏ธ SQL Injection ๋ฐฉ์–ด ํ™œ์„ฑํ™”\n');
});

// ํ…Œ์ŠคํŠธ ์˜ˆ์ œ
console.log('๐Ÿ“ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•:');
console.log('์ •์ƒ ์š”์ฒญ: curl "http://localhost:3000/search?q=laptop"');
console.log('๊ณต๊ฒฉ ์‹œ๋„: curl "http://localhost:3000/search?q=admin%27%20OR%20%271%27=%271"');
console.log('๋กœ๊ทธ ํ™•์ธ: curl "http://localhost:3000/admin/attack-log"\n');
๐ŸŽฏ WAF์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ 1. ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง: ๋ชจ๋“  HTTP ์š”์ฒญ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฒ€์‚ฌ
2. ์œ„ํ—˜ ์ ์ˆ˜ ์‹œ์Šคํ…œ: ํŒจํ„ด๋ณ„๋กœ ์ ์ˆ˜๋ฅผ ๋ถ€์—ฌํ•˜์—ฌ ์œ„ํ—˜๋„ ํ‰๊ฐ€
3. ์ž๋™ ์ฐจ๋‹จ: ์ž„๊ณ„๊ฐ’์„ ์ดˆ๊ณผํ•˜๋ฉด ์š”์ฒญ์„ ์ž๋™์œผ๋กœ ์ฐจ๋‹จ
4. ๋กœ๊น…: ๋ชจ๋“  ๊ณต๊ฒฉ ์‹œ๋„๋ฅผ ๊ธฐ๋กํ•˜์—ฌ ๋ถ„์„ ๊ฐ€๋Šฅ
5. ์œ ์—ฐํ•œ ์„ค์ •: ์ฐจ๋‹จ ๋ชจ๋“œ, ๋กœ๊น… ์˜ต์…˜ ๋“ฑ์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•

์ด WAF๋Š” ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„์ด์ง€๋งŒ, ์‹ค์ œ ํ”„๋กœ๋•์…˜์—์„œ๋Š” ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•ด. ์˜ˆ๋ฅผ ๋“ค์–ด IP ๊ธฐ๋ฐ˜ ์ฐจ๋‹จ, Rate Limiting, ์ง€๋ฆฌ์  ํ•„ํ„ฐ๋ง ๋“ฑ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด. ๐ŸŒ

๐Ÿ”ง ์‹ค์ „ ํ†ตํ•ฉ ์†”๋ฃจ์…˜

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

๐Ÿ“Œ Python Flask ์™„์ „ ํ†ตํ•ฉ ์˜ˆ์ œ

from flask import Flask, request, jsonify
import mysql.connector
from mysql.connector import Error
import re
import hashlib
import time
from datetime import datetime
from functools import wraps

app = Flask(__name__)

class ComprehensiveSQLInjectionDefense:
    """
    ์ข…ํ•ฉ์ ์ธ SQL Injection ๋ฐฉ์–ด ์‹œ์Šคํ…œ
    """
    
    def __init__(self, db_config):
        self.db_config = db_config
        self.attack_log = []
        self.blocked_ips = {}
        
        # ํŒจํ„ด ๊ธฐ๋ฐ˜ ํƒ์ง€
        self.sql_patterns = [
            (r"(\bSELECT\b.*\bFROM\b)", 3, "SELECT_FROM"),
            (r"(\bUNION\b.*\bSELECT\b)", 5, "UNION_SELECT"),
            (r"(\bDROP\b.*\bTABLE\b)", 10, "DROP_TABLE"),
            (r"(--|\#|\/\*)", 2, "SQL_COMMENT"),
            (r"(\bOR\b.*=.*)", 4, "OR_CONDITION"),
            (r"('|\"|;)", 1, "SPECIAL_CHAR"),
        ]
    
    def get_db_connection(self):
        """์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ"""
        try:
            connection = mysql.connector.connect(**self.db_config)
            return connection
        except Error as e:
            print(f"โŒ DB ์—ฐ๊ฒฐ ์‹คํŒจ: {e}")
            return None
    
    def pattern_detection(self, user_input):
        """ํŒจํ„ด ๊ธฐ๋ฐ˜ ํƒ์ง€"""
        risk_score = 0
        detected = []
        
        for pattern, score, name in self.sql_patterns:
            if re.search(pattern, user_input, re.IGNORECASE):
                risk_score += score
                detected.append(name)
        
        return {
            'is_malicious': risk_score >= 5,
            'risk_score': risk_score,
            'patterns': detected
        }
    
    def sanitize_input(self, user_input):
        """์ž…๋ ฅ๊ฐ’ ์ •์ œ"""
        if not isinstance(user_input, str):
            user_input = str(user_input)
        
        # ์œ„ํ—˜ํ•œ ๋ฌธ์ž ์ œ๊ฑฐ
        sanitized = re.sub(r'[;\'"\\]', '', user_input)
        
        # ์—ฐ์†๋œ ๊ณต๋ฐฑ ์ œ๊ฑฐ
        sanitized = re.sub(r'\s+', ' ', sanitized)
        
        return sanitized.strip()
    
    def safe_query(self, query, params=None):
        """Prepared Statement๋ฅผ ์‚ฌ์šฉํ•œ ์•ˆ์ „ํ•œ ์ฟผ๋ฆฌ ์‹คํ–‰"""
        connection = self.get_db_connection()
        if not connection:
            return None
        
        try:
            cursor = connection.cursor(dictionary=True)
            cursor.execute(query, params or ())
            
            if query.strip().upper().startswith('SELECT'):
                result = cursor.fetchall()
            else:
                connection.commit()
                result = {'affected_rows': cursor.rowcount}
            
            cursor.close()
            connection.close()
            return result
            
        except Error as e:
            print(f"โŒ ์ฟผ๋ฆฌ ์‹คํ–‰ ์˜ค๋ฅ˜: {e}")
            if connection:
                connection.close()
            return None
    
    def log_attack(self, ip, endpoint, input_data, detection_result):
        """๊ณต๊ฒฉ ์‹œ๋„ ๋กœ๊น…"""
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'ip': ip,
            'endpoint': endpoint,
            'input': input_data,
            'detection': detection_result
        }
        self.attack_log.append(log_entry)
        
        # IP ์ฐจ๋‹จ ์นด์šดํŠธ ์ฆ๊ฐ€
        if ip not in self.blocked_ips:
            self.blocked_ips[ip] = {'count': 0, 'first_seen': time.time()}
        self.blocked_ips[ip]['count'] += 1
        
        print(f"๐Ÿšจ ๊ณต๊ฒฉ ํƒ์ง€: {ip} -> {endpoint}")
        print(f"   ์œ„ํ—˜ ์ ์ˆ˜: {detection_result['risk_score']}")
    
    def is_ip_blocked(self, ip):
        """IP ์ฐจ๋‹จ ์—ฌ๋ถ€ ํ™•์ธ"""
        if ip in self.blocked_ips:
            block_info = self.blocked_ips[ip]
            # 5๋ฒˆ ์ด์ƒ ๊ณต๊ฒฉ ์‹œ๋„ ์‹œ 1์‹œ๊ฐ„ ์ฐจ๋‹จ
            if block_info['count'] >= 5:
                elapsed = time.time() - block_info['first_seen']
                if elapsed < 3600:  # 1์‹œ๊ฐ„
                    return True
                else:
                    # ์ฐจ๋‹จ ํ•ด์ œ
                    del self.blocked_ips[ip]
        return False
    
    def validate_and_execute(self, endpoint, user_input, query_func):
        """์ž…๋ ฅ ๊ฒ€์ฆ ๋ฐ ์ฟผ๋ฆฌ ์‹คํ–‰ ํ†ตํ•ฉ"""
        client_ip = request.remote_addr
        
        # IP ์ฐจ๋‹จ ํ™•์ธ
        if self.is_ip_blocked(client_ip):
            return {
                'success': False,
                'error': 'IP๊ฐ€ ์ฐจ๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
                'blocked_until': '1์‹œ๊ฐ„'
            }, 403
        
        # ํŒจํ„ด ํƒ์ง€
        detection = self.pattern_detection(user_input)
        
        if detection['is_malicious']:
            self.log_attack(client_ip, endpoint, user_input, detection)
            return {
                'success': False,
                'error': 'SQL Injection ๊ณต๊ฒฉ์ด ํƒ์ง€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
                'risk_score': detection['risk_score']
            }, 403
        
        # ์•ˆ์ „ํ•œ ์ฟผ๋ฆฌ ์‹คํ–‰
        result = query_func()
        
        if result is not None:
            return {'success': True, 'data': result}, 200
        else:
            return {'success': False, 'error': '์ฟผ๋ฆฌ ์‹คํ–‰ ์‹คํŒจ'}, 500


# ๋ฐฉ์–ด ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”
defense = ComprehensiveSQLInjectionDefense({
    'host': 'localhost',
    'database': 'myapp',
    'user': 'dbuser',
    'password': 'dbpassword'
})


def require_validation(f):
    """๋ฐ์ฝ”๋ ˆ์ดํ„ฐ: ์ž๋™ ์ž…๋ ฅ ๊ฒ€์ฆ"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # ๋ชจ๋“  ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ
        for key, value in request.args.items():
            detection = defense.pattern_detection(str(value))
            if detection['is_malicious']:
                defense.log_attack(
                    request.remote_addr,
                    request.endpoint,
                    {key: value},
                    detection
                )
                return jsonify({
                    'error': 'SQL Injection ํƒ์ง€',
                    'parameter': key
                }), 403
        
        return f(*args, **kwargs)
    return decorated_function


# API ์—”๋“œํฌ์ธํŠธ๋“ค

@app.route('/api/login', methods=['POST'])
@require_validation
def login():
    """์•ˆ์ „ํ•œ ๋กœ๊ทธ์ธ"""
    data = request.get_json()
    username = data.get('username', '')
    password = data.get('password', '')
    
    # ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ
    password_hash = hashlib.sha256(password.encode()).hexdigest()
    
    def query():
        return defense.safe_query(
            "SELECT * FROM users WHERE username = %s AND password = %s",
            (username, password_hash)
        )
    
    result, status = defense.validate_and_execute(
        '/api/login',
        username,
        query
    )
    
    return jsonify(result), status


@app.route('/api/search', methods=['GET'])
@require_validation
def search():
    """์•ˆ์ „ํ•œ ๊ฒ€์ƒ‰"""
    search_term = request.args.get('q', '')
    
    def query():
        return defense.safe_query(
            "SELECT * FROM products WHERE name LIKE %s",
            (f"%{search_term}%",)
        )
    
    result, status = defense.validate_and_execute(
        '/api/search',
        search_term,
        query
    )
    
    return jsonify(result), status


@app.route('/api/user/<int:user_id>', methods=['GET'])
def get_user(user_id):
    """์•ˆ์ „ํ•œ ์‚ฌ์šฉ์ž ์กฐํšŒ (ID๋Š” ์ •์ˆ˜ํ˜•์ด๋ฏ€๋กœ ์•ˆ์ „)"""
    result = defense.safe_query(
        "SELECT id, username, email FROM users WHERE id = %s",
        (user_id,)
    )
    
    if result:
        return jsonify({'success': True, 'user': result[0]}), 200
    else:
        return jsonify({'success': False, 'error': '์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค'}), 404


@app.route('/admin/attack-log', methods=['GET'])
def attack_log():
    """๊ณต๊ฒฉ ๋กœ๊ทธ ์กฐํšŒ"""
    limit = request.args.get('limit', 20, type=int)
    return jsonify({
        'attacks': defense.attack_log[-limit:],
        'blocked_ips': defense.blocked_ips
    }), 200


@app.route('/admin/stats', methods=['GET'])
def stats():
    """๋ณด์•ˆ ํ†ต๊ณ„"""
    return jsonify({
        'total_attacks': len(defense.attack_log),
        'blocked_ips': len(defense.blocked_ips),
        'recent_attacks': defense.attack_log[-5:] if defense.attack_log else []
    }), 200


if __name__ == '__main__':
    print("๐Ÿ›ก๏ธ SQL Injection ๋ฐฉ์–ด ์‹œ์Šคํ…œ ์‹œ์ž‘")
    print("=" * 60)
    print("โœ… ํŒจํ„ด ๊ธฐ๋ฐ˜ ํƒ์ง€ ํ™œ์„ฑํ™”")
    print("โœ… Prepared Statement ์‚ฌ์šฉ")
    print("โœ… IP ์ฐจ๋‹จ ์‹œ์Šคํ…œ ํ™œ์„ฑํ™”")
    print("โœ… ๊ณต๊ฒฉ ๋กœ๊น… ํ™œ์„ฑํ™”")
    print("=" * 60 + "\n")
    
    app.run(debug=True, port=5000)</int:user_id>
๐ŸŽ‰ ํ†ตํ•ฉ ์†”๋ฃจ์…˜์˜ ํŠน์ง• 1. ๋‹ค์ธต ๋ฐฉ์–ด (Defense in Depth):
- ํŒจํ„ด ํƒ์ง€ (1์ฐจ ๋ฐฉ์–ด)
- ์ž…๋ ฅ๊ฐ’ ์ •์ œ (2์ฐจ ๋ฐฉ์–ด)
- Prepared Statement (3์ฐจ ๋ฐฉ์–ด)

2. ์ž๋™ํ™”๋œ ๋ณด์•ˆ:
- ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•œ ์ž๋™ ๊ฒ€์ฆ
- ๋ชจ๋“  ์—”๋“œํฌ์ธํŠธ์— ์ผ๊ด€๋œ ๋ณด์•ˆ ์ ์šฉ

3. ์ง€๋Šฅํ˜• ์ฐจ๋‹จ:
- IP ๊ธฐ๋ฐ˜ ์ž๋™ ์ฐจ๋‹จ
- ๊ณต๊ฒฉ ๋นˆ๋„์— ๋”ฐ๋ฅธ ์ฐจ๋“ฑ ๋Œ€์‘

4. ๋ชจ๋‹ˆํ„ฐ๋ง & ๋ถ„์„:
- ์‹ค์‹œ๊ฐ„ ๊ณต๊ฒฉ ๋กœ๊น…
- ํ†ต๊ณ„ ๋ฐ ๋ถ„์„ ๊ธฐ๋Šฅ

๐Ÿ“Š ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ์‹ค์ „ ํŒ

๋ณด์•ˆ์€ ์ค‘์š”ํ•˜์ง€๋งŒ, ์„ฑ๋Šฅ๋„ ๋ฌด์‹œํ•  ์ˆ˜ ์—†์–ด. ๋ณด์•ˆ ๊ฒ€์‚ฌ๊ฐ€ ๋„ˆ๋ฌด ๋ฌด๊ฑฐ์šฐ๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋‚˜๋น ์ง€๋‹ˆ๊นŒ! โšก

๐Ÿš€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต

์ „๋žต ์„ค๋ช… ํšจ๊ณผ
์บ์‹ฑ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ Redis ๋“ฑ์— ์บ์‹ฑ DB ๋ถ€ํ•˜ 50-90% ๊ฐ์†Œ
๋น„๋™๊ธฐ ๋กœ๊น… ๊ณต๊ฒฉ ๋กœ๊ทธ๋ฅผ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌ ์‘๋‹ต ์‹œ๊ฐ„ 20-30% ๊ฐœ์„ 
ํŒจํ„ด ์ปดํŒŒ์ผ ์ •๊ทœํ‘œํ˜„์‹์„ ๋ฏธ๋ฆฌ ์ปดํŒŒ์ผ ๊ฒ€์ฆ ์†๋„ 40% ํ–ฅ์ƒ
ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ์•ˆ์ „ํ•œ ์ž…๋ ฅ์€ ๋น ๋ฅธ ๊ฒฝ๋กœ๋กœ ์ฒ˜๋ฆฌ ์ „์ฒด ์ฒ˜๋ฆฌ๋Ÿ‰ 30% ์ฆ๊ฐ€
Connection Pool DB ์—ฐ๊ฒฐ์„ ์žฌ์‚ฌ์šฉ ์—ฐ๊ฒฐ ์˜ค๋ฒ„ํ—ค๋“œ 80% ๊ฐ์†Œ

๐Ÿ’ก ์‹ค์ „ ๊ฐœ๋ฐœ ํŒ

1. ๊ฐœ๋ฐœ ๋‹จ๊ณ„๋ณ„ ๋ณด์•ˆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ ์„ค๊ณ„ ๋‹จ๊ณ„:
โœ… ๋ชจ๋“  ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ์‹ ๋ขฐํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์›์น™ ์ˆ˜๋ฆฝ
โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ถŒํ•œ ์ตœ์†Œํ™” ์„ค๊ณ„
โœ… ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์— ๋ฏผ๊ฐํ•œ ์ •๋ณด ๋…ธ์ถœ ๋ฐฉ์ง€

๊ฐœ๋ฐœ ๋‹จ๊ณ„:
โœ… Prepared Statement ์‚ฌ์šฉ ๊ฐ•์ œ
โœ… ORM ์‚ฌ์šฉ ์‹œ์—๋„ Raw Query ๊ฒ€์ฆ
โœ… ์ž…๋ ฅ๊ฐ’ ํƒ€์ž… ๊ฒ€์ฆ (์ •์ˆ˜๋Š” ์ •์ˆ˜๋กœ, ์ด๋ฉ”์ผ์€ ์ด๋ฉ”์ผ ํ˜•์‹์œผ๋กœ)

ํ…Œ์ŠคํŠธ ๋‹จ๊ณ„:
โœ… ์ž๋™ํ™”๋œ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ ๋„๊ตฌ ํ™œ์šฉ (SQLMap ๋“ฑ)
โœ… ๋‹ค์–‘ํ•œ ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ
โœ… ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋กœ ๋ณด์•ˆ ๊ฒ€์‚ฌ์˜ ์„ฑ๋Šฅ ์˜ํ–ฅ ์ธก์ •

๋ฐฐํฌ ๋‹จ๊ณ„:
โœ… WAF ์„ค์ • ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง
โœ… ๋กœ๊ทธ ๋ถ„์„ ์‹œ์Šคํ…œ ๊ตฌ์ถ•
โœ… ์ •๊ธฐ์ ์ธ ๋ณด์•ˆ ์—…๋ฐ์ดํŠธ
โš ๏ธ ํ”ํ•œ ์‹ค์ˆ˜๋“ค 1. ORM์„ ๋งน์‹ ํ•˜๊ธฐ: ORM๋„ Raw Query๋ฅผ ํ—ˆ์šฉํ•˜๋ฉด ์ทจ์•ฝํ•ด์งˆ ์ˆ˜ ์žˆ์–ด
2. ํ”„๋ก ํŠธ์—”๋“œ ๊ฒ€์ฆ๋งŒ ํ•˜๊ธฐ: ํด๋ผ์ด์–ธํŠธ ์ธก ๊ฒ€์ฆ์€ ์‰ฝ๊ฒŒ ์šฐํšŒ ๊ฐ€๋Šฅ
3. ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋…ธ์ถœ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—๋Ÿฌ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ฃผ๋ฉด ์ •๋ณด ์œ ์ถœ
4. ํ…Œ์ด๋ธ”๋ช… ๋™์  ์ƒ์„ฑ: Prepared Statement๋กœ ํ…Œ์ด๋ธ”๋ช…์€ ๋ฐ”์ธ๋”ฉ ๋ถˆ๊ฐ€
5. ๋ณด์•ˆ ์—…๋ฐ์ดํŠธ ๋ฏธ๋ฃจ๊ธฐ: ์ƒˆ๋กœ์šด ๊ณต๊ฒฉ ๊ธฐ๋ฒ•์€ ๊ณ„์† ๋‚˜์˜ค๊ณ  ์žˆ์–ด

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํ”„๋กœ๊ทธ๋žจ ๊ฐœ๋ฐœ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ๋ฐฐ์šธ ๋•Œ๋„, ์ด๋Ÿฐ ๋ณด์•ˆ ์ง€์‹์€ ์ •๋ง ์ค‘์š”ํ•ด. ๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ๋งŒ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์•ˆ์ „ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ๋Šฅ๋ ฅ์ด ์ง„์งœ ์‹ค๋ ฅ์ด๊ฑฐ๋“ ! ๐Ÿ’Ž

๐Ÿงช ํ…Œ์ŠคํŠธ์™€ ๊ฒ€์ฆ

์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์œผ๋ฉด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ด์•ผ๊ฒ ์ง€? SQL Injection ๋ฐฉ์–ด๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ”ฌ

๐Ÿ“Œ ์ž๋™ํ™”๋œ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ

import unittest
import requests
from time import sleep

class SQLInjectionDefenseTest(unittest.TestCase):
    """
    SQL Injection ๋ฐฉ์–ด ์‹œ์Šคํ…œ ํ…Œ์ŠคํŠธ
    """
    
    BASE_URL = "http://localhost:5000"
    
    def setUp(self):
        """ํ…Œ์ŠคํŠธ ์ดˆ๊ธฐํ™”"""
        self.test_cases = {
            'basic_or': "admin' OR '1'='1",
            'comment': "admin'--",
            'union': "' UNION SELECT * FROM users --",
            'drop_table': "'; DROP TABLE users; --",
            'time_based': "' AND SLEEP(5) --",
            'stacked': "admin'; DELETE FROM users WHERE '1'='1",
            'encoded': "admin%27%20OR%20%271%27%3D%271",
            'normal': "john_doe",  # ์ •์ƒ ์ž…๋ ฅ
        }
    
    def test_login_endpoint(self):
        """๋กœ๊ทธ์ธ ์—”๋“œํฌ์ธํŠธ ํ…Œ์ŠคํŠธ"""
        print("\n๐Ÿงช ๋กœ๊ทธ์ธ ์—”๋“œํฌ์ธํŠธ ํ…Œ์ŠคํŠธ")
        print("=" * 60)
        
        for name, payload in self.test_cases.items():
            response = requests.post(
                f"{self.BASE_URL}/api/login",
                json={'username': payload, 'password': 'test123'}
            )
            
            if name == 'normal':
                # ์ •์ƒ ์ž…๋ ฅ์€ ํ†ต๊ณผํ•ด์•ผ ํ•จ
                self.assertIn(response.status_code, [200, 401])
                print(f"โœ… {name}: ์ •์ƒ ์ฒ˜๋ฆฌ (์ƒํƒœ: {response.status_code})")
            else:
                # ๊ณต๊ฒฉ์€ ์ฐจ๋‹จ๋˜์–ด์•ผ ํ•จ
                self.assertEqual(response.status_code, 403)
                print(f"๐Ÿ›ก๏ธ {name}: ์ฐจ๋‹จ๋จ (์ƒํƒœ: {response.status_code})")
            
            sleep(0.1)  # Rate limiting ๊ณ ๋ ค
    
    def test_search_endpoint(self):
        """๊ฒ€์ƒ‰ ์—”๋“œํฌ์ธํŠธ ํ…Œ์ŠคํŠธ"""
        print("\n๐Ÿงช ๊ฒ€์ƒ‰ ์—”๋“œํฌ์ธํŠธ ํ…Œ์ŠคํŠธ")
        print("=" * 60)
        
        for name, payload in self.test_cases.items():
            response = requests.get(
                f"{self.BASE_URL}/api/search",
                params={'q': payload}
            )
            
            if name == 'normal':
                self.assertEqual(response.status_code, 200)
                print(f"โœ… {name}: ์ •์ƒ ์ฒ˜๋ฆฌ")
            else:
                self.assertEqual(response.status_code, 403)
                print(f"๐Ÿ›ก๏ธ {name}: ์ฐจ๋‹จ๋จ")
            
            sleep(0.1)
    
    def test_ip_blocking(self):
        """IP ์ฐจ๋‹จ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ"""
        print("\n๐Ÿงช IP ์ฐจ๋‹จ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ")
        print("=" * 60)
        
        malicious_payload = "admin' OR '1'='1"
        
        # 5๋ฒˆ ๊ณต๊ฒฉ ์‹œ๋„
        for i in range(5):
            response = requests.post(
                f"{self.BASE_URL}/api/login",
                json={'username': malicious_payload, 'password': 'test'}
            )
            print(f"๊ณต๊ฒฉ ์‹œ๋„ {i+1}: ์ƒํƒœ {response.status_code}")
            sleep(0.2)
        
        # 6๋ฒˆ์งธ ์‹œ๋„๋Š” IP ์ฐจ๋‹จ๋˜์–ด์•ผ ํ•จ
        response = requests.post(
            f"{self.BASE_URL}/api/login",
            json={'username': 'normal_user', 'password': 'test'}
        )
        
        self.assertEqual(response.status_code, 403)
        self.assertIn('์ฐจ๋‹จ', response.json().get('error', ''))
        print("โœ… IP ์ฐจ๋‹จ ๊ธฐ๋Šฅ ์ •์ƒ ์ž‘๋™")
    
    def test_performance(self):
        """์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ"""
        print("\n๐Ÿงช ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ")
        print("=" * 60)
        
        import time
        
        start = time.time()
        for _ in range(100):
            requests.get(
                f"{self.BASE_URL}/api/search",
                params={'q': 'laptop'}
            )
        elapsed = time.time() - start
        
        avg_time = elapsed / 100
        print(f"100๊ฐœ ์š”์ฒญ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„: {elapsed:.2f}์ดˆ")
        print(f"ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„: {avg_time*1000:.2f}ms")
        
        # ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์ด 100ms ์ดํ•˜์—ฌ์•ผ ํ•จ
        self.assertLess(avg_time, 0.1)
        print("โœ… ์„ฑ๋Šฅ ๊ธฐ์ค€ ํ†ต๊ณผ")
    
    def test_false_positives(self):
        """์˜คํƒ(False Positive) ํ…Œ์ŠคํŠธ"""
        print("\n๐Ÿงช ์˜คํƒ ํ…Œ์ŠคํŠธ")
        print("=" * 60)
        
        # SQL ํ‚ค์›Œ๋“œ๋ฅผ ํฌํ•จํ•˜์ง€๋งŒ ์ •์ƒ์ ์ธ ์ž…๋ ฅ๋“ค
        legitimate_inputs = [
            "O'Reilly",  # ์ด๋ฆ„์— ์ž‘์€๋”ฐ์˜ดํ‘œ
            "SELECT brand laptop",  # ์ƒํ’ˆ๋ช…์— SELECT
            "email@union.com",  # ์ด๋ฉ”์ผ์— union
            "Drop shipping service",  # ์„œ๋น„์Šค๋ช…์— drop
        ]
        
        false_positives = 0
        for input_text in legitimate_inputs:
            response = requests.get(
                f"{self.BASE_URL}/api/search",
                params={'q': input_text}
            )
            
            if response.status_code == 403:
                false_positives += 1
                print(f"โš ๏ธ ์˜คํƒ: '{input_text}'")
            else:
                print(f"โœ… ์ •์ƒ ์ฒ˜๋ฆฌ: '{input_text}'")
        
        # ์˜คํƒ๋ฅ ์ด 25% ์ดํ•˜์—ฌ์•ผ ํ•จ
        false_positive_rate = false_positives / len(legitimate_inputs)
        self.assertLess(false_positive_rate, 0.25)
        print(f"\n์˜คํƒ๋ฅ : {false_positive_rate*100:.1f}%")


if __name__ == '__main__':
    print("๐Ÿš€ SQL Injection ๋ฐฉ์–ด ์‹œ์Šคํ…œ ํ…Œ์ŠคํŠธ ์‹œ์ž‘")
    print("=" * 60 + "\n")
    
    # ํ…Œ์ŠคํŠธ ์‹คํ–‰
    unittest.main(verbosity=2)
๐Ÿ’ก ํ…Œ์ŠคํŠธ ๋„๊ตฌ ์ถ”์ฒœ

1. SQLMap: ๊ฐ€์žฅ ์œ ๋ช…ํ•œ SQL Injection ํ…Œ์ŠคํŠธ ๋„๊ตฌ
sqlmap -u "http://example.com/search?q=test" --batch


2. Burp Suite: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ ํ”Œ๋žซํผ
3. OWASP ZAP: ๋ฌด๋ฃŒ ์˜คํ”ˆ์†Œ์Šค ๋ณด์•ˆ ์Šค์บ๋„ˆ
4. w3af: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ณต๊ฒฉ ๋ฐ ๊ฐ์‚ฌ ํ”„๋ ˆ์ž„์›Œํฌ

์ด๋Ÿฐ ๋„๊ตฌ๋“ค๋กœ ์ •๊ธฐ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๋ฉด ์ƒˆ๋กœ์šด ์ทจ์•ฝ์ ์„ ๋นจ๋ฆฌ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์–ด! ๐ŸŽฏ

๐ŸŒŸ ์‹ค์ œ ์‚ฌ๋ก€ ์—ฐ๊ตฌ

์ด๋ก ๋งŒ ์•Œ์•„์„œ๋Š” ๋ถ€์กฑํ•ด. ์‹ค์ œ ์‚ฌ๋ก€๋ฅผ ํ†ตํ•ด ๋ฐฐ์›Œ๋ณด์ž! ๐Ÿ˜Š

๐Ÿ“Œ ์‚ฌ๋ก€ 1: ์ „์ž์ƒ๊ฑฐ๋ž˜ ์‚ฌ์ดํŠธ

๐Ÿช ์ƒํ™ฉ
์ค‘์†Œ ์‡ผํ•‘๋ชฐ์—์„œ ์ƒํ’ˆ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์— SQL Injection ์ทจ์•ฝ์ ์ด ๋ฐœ๊ฒฌ๋จ. ๊ณต๊ฒฉ์ž๊ฐ€ ๊ณ ๊ฐ ์ •๋ณด ํ…Œ์ด๋ธ”์— ์ ‘๊ทผ ์‹œ๋„.

๐Ÿ” ๋ฌธ์ œ ์ฝ”๋“œ
query = f"SELECT * FROM products WHERE name LIKE '%{search_term}%'"


โœ… ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
1. Prepared Statement๋กœ ์ „ํ™˜
2. ์ž…๋ ฅ๊ฐ’ ๊ธธ์ด ์ œํ•œ (์ตœ๋Œ€ 50์ž)
3. ํŠน์ˆ˜๋ฌธ์ž ํ•„ํ„ฐ๋ง
4. WAF ๋„์ž…์œผ๋กœ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง

๐Ÿ“Š ๊ฒฐ๊ณผ
- ๊ณต๊ฒฉ ์‹œ๋„ 100% ์ฐจ๋‹จ
- ๊ฒ€์ƒ‰ ์„ฑ๋Šฅ 15% ํ–ฅ์ƒ (์ฟผ๋ฆฌ ์ตœ์ ํ™” ๋ถ€์ˆ˜ํšจ๊ณผ)
- ๊ณ ๊ฐ ์‹ ๋ขฐ๋„ ํšŒ๋ณต

๐Ÿ“Œ ์‚ฌ๋ก€ 2: ์†Œ์…œ ๋ฏธ๋””์–ด ํ”Œ๋žซํผ

๐Ÿ“ฑ ์ƒํ™ฉ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์—…๋ฐ์ดํŠธ ๊ธฐ๋Šฅ์—์„œ Second-Order SQL Injection ๋ฐœ์ƒ. ์ €์žฅ๋œ ์•…์˜์  ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚˜์ค‘์— ์‹คํ–‰๋จ.

๐Ÿ” ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค
1. ๊ณต๊ฒฉ์ž๊ฐ€ ํ”„๋กœํ•„ ์ด๋ฆ„์— admin'; DROP TABLE posts; -- ์ž…๋ ฅ
2. ๋ฐ์ดํ„ฐ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ €์žฅ๋จ
3. ๊ด€๋ฆฌ์ž๊ฐ€ ํ•ด๋‹น ํ”„๋กœํ•„์„ ์กฐํšŒํ•  ๋•Œ ์ฟผ๋ฆฌ ์‹คํ–‰
4. posts ํ…Œ์ด๋ธ” ์‚ญ์ œ ์‹œ๋„

โœ… ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
1. ์ž…๋ ฅ ์‹œ์ ๊ณผ ์ถœ๋ ฅ ์‹œ์  ๋ชจ๋‘์—์„œ ๊ฒ€์ฆ
2. ์ €์žฅ ์ „ ๋ฐ์ดํ„ฐ ์ •์ œ
3. ์ถœ๋ ฅ ์‹œ HTML ์ด์Šค์ผ€์ดํ•‘
4. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ถŒํ•œ ๋ถ„๋ฆฌ (์ฝ๊ธฐ/์“ฐ๊ธฐ ๊ณ„์ • ๋ถ„๋ฆฌ)

๐Ÿ“Š ๊ฒฐ๊ณผ
- Second-Order ๊ณต๊ฒฉ ์™„์ „ ์ฐจ๋‹จ
- XSS ๊ณต๊ฒฉ๋„ ํ•จ๊ป˜ ๋ฐฉ์–ด
- ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๋ณด์žฅ

์ด๋Ÿฐ ์‚ฌ๋ก€๋“ค์„ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, SQL Injection ๋ฐฉ์–ด๋Š” ๋‹จ์ˆœํžˆ ์ฝ”๋“œ ํ•œ ์ค„ ๋ฐ”๊พธ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์‹œ์Šคํ…œ ์ „์ฒด์˜ ๋ณด์•ˆ ์„ค๊ณ„๊ฐ€ ํ•„์š”ํ•ด. ๐Ÿ—๏ธ

๐ŸŽ“ ํ•™์Šต ๋ฆฌ์†Œ์Šค์™€ ์ปค๋ฎค๋‹ˆํ‹ฐ

SQL Injection ๋ฐฉ์–ด๋Š” ๊ณ„์† ๋ฐœ์ „ํ•˜๋Š” ๋ถ„์•ผ์•ผ. ์ง€์†์ ์œผ๋กœ ํ•™์Šตํ•˜๊ณ  ์ปค๋ฎค๋‹ˆํ‹ฐ์™€ ์†Œํ†ตํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด! ๐Ÿ“š

๐Ÿ“– ์ถ”์ฒœ ํ•™์Šต ์ž๋ฃŒ ๊ณต์‹ ๋ฌธ์„œ:
โ€ข OWASP SQL Injection Prevention Cheat Sheet
โ€ข CWE-89: SQL Injection
โ€ข NIST ๋ณด์•ˆ ๊ฐ€์ด๋“œ๋ผ์ธ

์˜จ๋ผ์ธ ๊ฐ•์˜:
โ€ข Udemy: Web Security & Bug Bounty
โ€ข Coursera: Cybersecurity Specialization
โ€ข HackerOne: Bug Bounty ์‹ค์Šต

์‹ค์Šต ํ”Œ๋žซํผ:
โ€ข DVWA (Damn Vulnerable Web Application)
โ€ข WebGoat (OWASP)
โ€ข HackTheBox
โ€ข TryHackMe

์ปค๋ฎค๋‹ˆํ‹ฐ:
โ€ข Stack Overflow Security ํƒœ๊ทธ
โ€ข Reddit r/netsec
โ€ข OWASP ๋กœ์ปฌ ์ฑ•ํ„ฐ
โ€ข ์žฌ๋Šฅ๋„ท ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ปค๋ฎค๋‹ˆํ‹ฐ ๐Ÿ’ก

์žฌ๋Šฅ๋„ท์—์„œ๋Š” ์ด๋Ÿฐ ๋ณด์•ˆ ์ง€์‹์„ ๊ฐ€์ง„ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์ด ํ™œ๋™ํ•˜๊ณ  ์žˆ์–ด. ํ”„๋กœ๊ทธ๋žจ ๊ฐœ๋ฐœ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๋ฉด์„œ ๋ณด์•ˆ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋„ ํ•จ๊ป˜ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์ง€! ๐Ÿค

๐Ÿ”ฎ ๋ฏธ๋ž˜ ํŠธ๋ Œ๋“œ์™€ ์ „๋ง

SQL Injection ๋ฐฉ์–ด ๊ธฐ์ˆ ๋„ ๊ณ„์† ์ง„ํ™”ํ•˜๊ณ  ์žˆ์–ด. ์•ž์œผ๋กœ์˜ ํŠธ๋ Œ๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž! ๐Ÿš€

๐ŸŒ ์ƒˆ๋กœ์šด ๋ฐฉ์–ด ๊ธฐ์ˆ  1. AI ๊ธฐ๋ฐ˜ ํƒ์ง€:
๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ์ด ์ƒˆ๋กœ์šด ๊ณต๊ฒฉ ํŒจํ„ด์„ ์ž๋™์œผ๋กœ ํ•™์Šตํ•˜๊ณ  ํƒ์ง€. ์˜คํƒ๋ฅ ์ด ๊ธฐ์กด ๋ฐฉ์‹๋ณด๋‹ค 70% ๋‚ฎ์•„์ง.

2. ๋Ÿฐํƒ€์ž„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ž๊ธฐ ๋ณดํ˜ธ(RASP):
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€์—์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณต๊ฒฉ์„ ํƒ์ง€ํ•˜๊ณ  ์ฐจ๋‹จ. WAF๋ณด๋‹ค ์ •ํ™•๋„๊ฐ€ ๋†’์Œ.

3. ๋ธ”๋ก์ฒด์ธ ๊ธฐ๋ฐ˜ ๋กœ๊น…:
๊ณต๊ฒฉ ๋กœ๊ทธ๋ฅผ ๋ธ”๋ก์ฒด์ธ์— ์ €์žฅํ•˜์—ฌ ๋ณ€์กฐ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฐ์‚ฌ ์ถ”์  ์ œ๊ณต.

4. ์ œ๋กœ ํŠธ๋Ÿฌ์ŠคํŠธ ์•„ํ‚คํ…์ฒ˜:
๋ชจ๋“  ์š”์ฒญ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹ ๋ขฐํ•˜์ง€ ์•Š๊ณ  ๊ฒ€์ฆ. ๋‚ด๋ถ€ ๊ณต๊ฒฉ๋„ ๋ฐฉ์–ด ๊ฐ€๋Šฅ.

5. ์ž๋™ํ™”๋œ ํŒจ์น˜ ์‹œ์Šคํ…œ:
์ทจ์•ฝ์  ๋ฐœ๊ฒฌ ์‹œ ์ž๋™์œผ๋กœ ํŒจ์น˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ ์šฉํ•˜๋Š” ์‹œ์Šคํ…œ.

ํŠนํžˆ AI์™€ ๋จธ์‹ ๋Ÿฌ๋‹์˜ ๋ฐœ์ „์œผ๋กœ Zero-Day ๊ณต๊ฒฉ(์•„์ง ์•Œ๋ ค์ง€์ง€ ์•Š์€ ์ƒˆ๋กœ์šด ๊ณต๊ฒฉ)๋„ ํƒ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ  ์žˆ์–ด. ์ •๋ง ์‹ ๋‚˜๋Š” ์‹œ๋Œ€์•ผ! ๐ŸŽ‰

โœจ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์–ด. ์šฐ๋ฆฌ๊ฐ€ ํ•จ๊ป˜ ์‚ดํŽด๋ณธ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณผ๊นŒ? ๐Ÿ“

๐ŸŽฏ ํ•ต์‹ฌ ์š”์•ฝ 1. SQL Injection์˜ ์œ„ํ—˜์„ฑ:
โ€ข ๊ฐ€์žฅ ํ”ํ•˜๊ณ  ์œ„ํ—˜ํ•œ ์›น ๊ณต๊ฒฉ ์ค‘ ํ•˜๋‚˜
โ€ข ๋ฐ์ดํ„ฐ ์œ ์ถœ, ์‚ญ์ œ, ๊ถŒํ•œ ํƒˆ์ทจ ๊ฐ€๋Šฅ
โ€ข OWASP Top 10 ์ƒ์œ„๊ถŒ

2. ๋ฐฉ์–ด ์ „๋žต:
โ€ข Prepared Statement (ํ•„์ˆ˜!)
โ€ข ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ๋ฐ ์ •์ œ
โ€ข ํŒจํ„ด ๊ธฐ๋ฐ˜ ํƒ์ง€
โ€ข WAF ๊ตฌ์ถ•
โ€ข ๋จธ์‹ ๋Ÿฌ๋‹ ํ™œ์šฉ

3. ์‹ค์ „ ๊ตฌํ˜„:
โ€ข ๋‹ค์ธต ๋ฐฉ์–ด ์‹œ์Šคํ…œ ๊ตฌ์ถ•
โ€ข ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ
โ€ข ์ง€์†์ ์ธ ๋ชจ๋‹ˆํ„ฐ๋ง
โ€ข ์ •๊ธฐ์ ์ธ ๋ณด์•ˆ ์—…๋ฐ์ดํŠธ

4. ์„ฑ๋Šฅ ์ตœ์ ํ™”:
โ€ข ์บ์‹ฑ ์ „๋žต
โ€ข ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
โ€ข Connection Pool
โ€ข ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹

SQL Injection ๋ฐฉ์–ด๋Š” ํ•œ ๋ฒˆ ๊ตฌํ˜„ํ•˜๊ณ  ๋๋‚˜๋Š” ๊ฒŒ ์•„๋‹ˆ์•ผ. ์ƒˆ๋กœ์šด ๊ณต๊ฒฉ ๊ธฐ๋ฒ•์ด ๊ณ„์† ๋‚˜์˜ค๊ณ  ์žˆ์œผ๋‹ˆ๊นŒ, ์ง€์†์ ์ธ ํ•™์Šต๊ณผ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•ด. ๐Ÿ”„

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

๐Ÿ’ก ๋งˆ์ง€๋ง‰ ์กฐ์–ธ ๋ณด์•ˆ์€ ์™„๋ฒฝํ•  ์ˆ˜ ์—†์–ด. ํ•˜์ง€๋งŒ ์ตœ์„ ์„ ๋‹คํ•ด ๋ฐฉ์–ดํ•˜๊ณ , ๋น ๋ฅด๊ฒŒ ๋Œ€์‘ํ•˜๋ฉฐ, ๊ณ„์† ๊ฐœ์„ ํ•˜๋Š” ์ž์„ธ๊ฐ€ ์ค‘์š”ํ•ด. ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋” ์•ˆ์ „ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐ ๋„์›€์ด ๋˜๊ธธ ๋ฐ”๋ผ! ๐Ÿ›ก๏ธ

๊ทธ๋ฆฌ๊ณ  ๊ธฐ์–ตํ•ด: "๋ณด์•ˆ์€ ์ œํ’ˆ์ด ์•„๋‹ˆ๋ผ ํ”„๋กœ์„ธ์Šค๋‹ค" - Bruce Schneier

์ž, ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„๋„ SQL Injection ๊ณต๊ฒฉ์„ ๋ง‰์•„๋‚ผ ์ค€๋น„๊ฐ€ ๋์–ด! ์•ˆ์ „ํ•˜๊ณ  ๋ฉ‹์ง„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด์ž! ํ™”์ดํŒ…! ๐Ÿš€โœจ

๋Œ“๊ธ€ ์ž‘์„ฑ

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

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.