Ver3.0 ๐ก๏ธ SQL Injection ๊ณต๊ฒฉ ํ์ง ์์ค์ฝ๋ ์๋ฒฝ ๊ฐ์ด๋

๐ก๏ธ SQL Injection ๊ณต๊ฒฉ ํ์ง ์์ค์ฝ๋ ์๋ฒฝ ๊ฐ์ด๋
ํด์ปค๋ค์ ๊ฐ์ฅ ์ฌ๋ํ๋ ๊ณต๊ฒฉ๋ฒ์ ๋ง์๋ด๋ ์ค์ ์ฝ๋ ๋๊ณต๊ฐ!
๐ฏ SQL Injection, ์ ์ด๋ ๊ฒ ๋ฌด์์ด ๊ฑธ๊น?
์๋ ! ์ค๋์ ์น ๋ณด์์์ ๊ฐ์ฅ ์ ๋ช ๋์ ๊ณต๊ฒฉ ์ค ํ๋์ธ SQL Injection์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊ฒ. ๐
์์ํด๋ด. ๋๊ฐ ์ด์ํ๋ ์น์ฌ์ดํธ์ ๋๊ตฐ๊ฐ ๋ก๊ทธ์ธํ๋ ค๊ณ ํ๋๋ฐ, ์์ด๋ ์
๋ ฅ๋์ ' OR '1'='1 ๊ฐ์ ์ด์ํ ๋ฌธ์์ด์ ๋ฃ์์ด. ๊ทธ๋ฐ๋ฐ ๊ฐ์๊ธฐ ๊ด๋ฆฌ์ ๊ถํ์ผ๋ก ๋ก๊ทธ์ธ์ด ๋์ด๋ฒ๋ฆฐ๋ค๋ฉด? ๐ฑ ์ด๊ฒ ๋ฐ๋ก SQL Injection ๊ณต๊ฒฉ์ด์ผ.
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 ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธํ ์ ์๊ฒ ๋ผ. ๐จ
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")
๐ 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()
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");
?>
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")
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');
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์ฐจ ๋ฐฉ์ด)
- ์ ๋ ฅ๊ฐ ์ ์ (2์ฐจ ๋ฐฉ์ด)
- Prepared Statement (3์ฐจ ๋ฐฉ์ด)
2. ์๋ํ๋ ๋ณด์:
- ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ํตํ ์๋ ๊ฒ์ฆ
- ๋ชจ๋ ์๋ํฌ์ธํธ์ ์ผ๊ด๋ ๋ณด์ ์ ์ฉ
3. ์ง๋ฅํ ์ฐจ๋จ:
- IP ๊ธฐ๋ฐ ์๋ ์ฐจ๋จ
- ๊ณต๊ฒฉ ๋น๋์ ๋ฐ๋ฅธ ์ฐจ๋ฑ ๋์
4. ๋ชจ๋ํฐ๋ง & ๋ถ์:
- ์ค์๊ฐ ๊ณต๊ฒฉ ๋ก๊น
- ํต๊ณ ๋ฐ ๋ถ์ ๊ธฐ๋ฅ
๐ ์ฑ๋ฅ ์ต์ ํ์ ์ค์ ํ
๋ณด์์ ์ค์ํ์ง๋ง, ์ฑ๋ฅ๋ ๋ฌด์ํ ์ ์์ด. ๋ณด์ ๊ฒ์ฌ๊ฐ ๋๋ฌด ๋ฌด๊ฑฐ์ฐ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ๋๋น ์ง๋๊น! โก
๐ ์ฑ๋ฅ ์ต์ ํ ์ ๋ต
| ์ ๋ต | ์ค๋ช | ํจ๊ณผ |
|---|---|---|
| ์บ์ฑ | ์์ฃผ ์ฌ์ฉ๋๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ Redis ๋ฑ์ ์บ์ฑ | DB ๋ถํ 50-90% ๊ฐ์ |
| ๋น๋๊ธฐ ๋ก๊น | ๊ณต๊ฒฉ ๋ก๊ทธ๋ฅผ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌ | ์๋ต ์๊ฐ 20-30% ๊ฐ์ |
| ํจํด ์ปดํ์ผ | ์ ๊ทํํ์์ ๋ฏธ๋ฆฌ ์ปดํ์ผ | ๊ฒ์ฆ ์๋ 40% ํฅ์ |
| ํ์ดํธ๋ฆฌ์คํธ | ์์ ํ ์ ๋ ฅ์ ๋น ๋ฅธ ๊ฒฝ๋ก๋ก ์ฒ๋ฆฌ | ์ ์ฒด ์ฒ๋ฆฌ๋ 30% ์ฆ๊ฐ |
| Connection Pool | DB ์ฐ๊ฒฐ์ ์ฌ์ฌ์ฉ | ์ฐ๊ฒฐ ์ค๋ฒํค๋ 80% ๊ฐ์ |
๐ก ์ค์ ๊ฐ๋ฐ ํ
โ ๋ชจ๋ ์ฌ์ฉ์ ์ ๋ ฅ์ ์ ๋ขฐํ์ง ์๋๋ค๋ ์์น ์๋ฆฝ
โ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ถํ ์ต์ํ ์ค๊ณ
โ ์๋ฌ ๋ฉ์์ง์ ๋ฏผ๊ฐํ ์ ๋ณด ๋ ธ์ถ ๋ฐฉ์ง
๊ฐ๋ฐ ๋จ๊ณ:
โ Prepared Statement ์ฌ์ฉ ๊ฐ์
โ ORM ์ฌ์ฉ ์์๋ Raw Query ๊ฒ์ฆ
โ ์ ๋ ฅ๊ฐ ํ์ ๊ฒ์ฆ (์ ์๋ ์ ์๋ก, ์ด๋ฉ์ผ์ ์ด๋ฉ์ผ ํ์์ผ๋ก)
ํ ์คํธ ๋จ๊ณ:
โ ์๋ํ๋ ๋ณด์ ํ ์คํธ ๋๊ตฌ ํ์ฉ (SQLMap ๋ฑ)
โ ๋ค์ํ ๊ณต๊ฒฉ ์๋๋ฆฌ์ค ํ ์คํธ
โ ๋ถํ ํ ์คํธ๋ก ๋ณด์ ๊ฒ์ฌ์ ์ฑ๋ฅ ์ํฅ ์ธก์
๋ฐฐํฌ ๋จ๊ณ:
โ WAF ์ค์ ๋ฐ ๋ชจ๋ํฐ๋ง
โ ๋ก๊ทธ ๋ถ์ ์์คํ ๊ตฌ์ถ
โ ์ ๊ธฐ์ ์ธ ๋ณด์ ์ ๋ฐ์ดํธ
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" --batch2. 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: ์์ ๋ฏธ๋์ด ํ๋ซํผ
๐ ๊ณต๊ฒฉ ์๋๋ฆฌ์ค
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 ๋ฐฉ์ด ๊ธฐ์ ๋ ๊ณ์ ์งํํ๊ณ ์์ด. ์์ผ๋ก์ ํธ๋ ๋๋ฅผ ์ดํด๋ณด์! ๐
๋ฅ๋ฌ๋ ๋ชจ๋ธ์ด ์๋ก์ด ๊ณต๊ฒฉ ํจํด์ ์๋์ผ๋ก ํ์ตํ๊ณ ํ์ง. ์คํ๋ฅ ์ด ๊ธฐ์กด ๋ฐฉ์๋ณด๋ค 70% ๋ฎ์์ง.
2. ๋ฐํ์ ์ ํ๋ฆฌ์ผ์ด์ ์๊ธฐ ๋ณดํธ(RASP):
์ ํ๋ฆฌ์ผ์ด์ ๋ด๋ถ์์ ์ค์๊ฐ์ผ๋ก ๊ณต๊ฒฉ์ ํ์งํ๊ณ ์ฐจ๋จ. WAF๋ณด๋ค ์ ํ๋๊ฐ ๋์.
3. ๋ธ๋ก์ฒด์ธ ๊ธฐ๋ฐ ๋ก๊น :
๊ณต๊ฒฉ ๋ก๊ทธ๋ฅผ ๋ธ๋ก์ฒด์ธ์ ์ ์ฅํ์ฌ ๋ณ์กฐ ๋ถ๊ฐ๋ฅํ ๊ฐ์ฌ ์ถ์ ์ ๊ณต.
4. ์ ๋ก ํธ๋ฌ์คํธ ์ํคํ ์ฒ:
๋ชจ๋ ์์ฒญ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๋ขฐํ์ง ์๊ณ ๊ฒ์ฆ. ๋ด๋ถ ๊ณต๊ฒฉ๋ ๋ฐฉ์ด ๊ฐ๋ฅ.
5. ์๋ํ๋ ํจ์น ์์คํ :
์ทจ์ฝ์ ๋ฐ๊ฒฌ ์ ์๋์ผ๋ก ํจ์น๋ฅผ ์์ฑํ๊ณ ์ ์ฉํ๋ ์์คํ .
ํนํ AI์ ๋จธ์ ๋ฌ๋์ ๋ฐ์ ์ผ๋ก Zero-Day ๊ณต๊ฒฉ(์์ง ์๋ ค์ง์ง ์์ ์๋ก์ด ๊ณต๊ฒฉ)๋ ํ์งํ ์ ์๊ฒ ๋๊ณ ์์ด. ์ ๋ง ์ ๋๋ ์๋์ผ! ๐
โจ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์์ด. ์ฐ๋ฆฌ๊ฐ ํจ๊ป ์ดํด๋ณธ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณผ๊น? ๐
โข ๊ฐ์ฅ ํํ๊ณ ์ํํ ์น ๊ณต๊ฒฉ ์ค ํ๋
โข ๋ฐ์ดํฐ ์ ์ถ, ์ญ์ , ๊ถํ ํ์ทจ ๊ฐ๋ฅ
โข OWASP Top 10 ์์๊ถ
2. ๋ฐฉ์ด ์ ๋ต:
โข Prepared Statement (ํ์!)
โข ์ ๋ ฅ๊ฐ ๊ฒ์ฆ ๋ฐ ์ ์
โข ํจํด ๊ธฐ๋ฐ ํ์ง
โข WAF ๊ตฌ์ถ
โข ๋จธ์ ๋ฌ๋ ํ์ฉ
3. ์ค์ ๊ตฌํ:
โข ๋ค์ธต ๋ฐฉ์ด ์์คํ ๊ตฌ์ถ
โข ์๋ํ๋ ํ ์คํธ
โข ์ง์์ ์ธ ๋ชจ๋ํฐ๋ง
โข ์ ๊ธฐ์ ์ธ ๋ณด์ ์ ๋ฐ์ดํธ
4. ์ฑ๋ฅ ์ต์ ํ:
โข ์บ์ฑ ์ ๋ต
โข ๋น๋๊ธฐ ์ฒ๋ฆฌ
โข Connection Pool
โข ํ์ดํธ๋ฆฌ์คํธ ๋ฐฉ์
SQL Injection ๋ฐฉ์ด๋ ํ ๋ฒ ๊ตฌํํ๊ณ ๋๋๋ ๊ฒ ์๋์ผ. ์๋ก์ด ๊ณต๊ฒฉ ๊ธฐ๋ฒ์ด ๊ณ์ ๋์ค๊ณ ์์ผ๋๊น, ์ง์์ ์ธ ํ์ต๊ณผ ์ ๋ฐ์ดํธ๊ฐ ํ์ํด. ๐
ํ๋ก๊ทธ๋จ ๊ฐ๋ฐ์ ํ ๋๋ ๊ธฐ๋ฅ ๊ตฌํ๋งํผ์ด๋ ๋ณด์์ด ์ค์ํด. ํนํ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ์๋น์ค๋ผ๋ฉด ๋๋์ฑ! ์ค๋ ๋ฐฐ์ด ๋ด์ฉ๋ค์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๊ณ , ์ฌ๋ฅ๋ท ๊ฐ์ ์ปค๋ฎค๋ํฐ์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ๊ฒฝํ์ ๊ณต์ ํด๋ณด๋ ๊ฒ๋ ์ข์ ๊ฑฐ์ผ. ๐ช
๊ทธ๋ฆฌ๊ณ ๊ธฐ์ตํด: "๋ณด์์ ์ ํ์ด ์๋๋ผ ํ๋ก์ธ์ค๋ค" - Bruce Schneier
์, ์ด์ ์ฌ๋ฌ๋ถ๋ SQL Injection ๊ณต๊ฒฉ์ ๋ง์๋ผ ์ค๋น๊ฐ ๋์ด! ์์ ํ๊ณ ๋ฉ์ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค์ด๋ณด์! ํ์ดํ ! ๐โจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.