์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ” SQL Injection ๊ณต๊ฒฉ ์›๋ฆฌ์™€ ๋ฐฉ์–ด ๊ธฐ๋ฒ•

๐Ÿ” SQL Injection ๊ณต๊ฒฉ ์›๋ฆฌ์™€ ๋ฐฉ์–ด ๊ธฐ๋ฒ•

ํ•ด์ปค๋“ค์ด ์ œ์ผ ์ฆ๊ฒจ์“ฐ๋Š” ๊ทธ ๊ณต๊ฒฉ, ์ œ๋Œ€๋กœ ํŒŒํ—ค์ณ๋ณด์ž

๐Ÿ›ก๏ธ ๋ณด์•ˆ ๐Ÿ’ป ํ”„๋กœ๊ทธ๋žจ๊ฐœ๋ฐœ โšก SQL Injection ๐Ÿ”’ ๋ฐฉ์–ด๊ธฐ๋ฒ•

์•ผ ์†”์งํžˆ ๋งํ•ด์„œ... SQL Injection์ด ๋ญ”์ง€ ๋ชจ๋ฅด๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„์ง๋„ ์žˆ๋‹ค๋Š” ๊ฑฐ ์•Œ์ง€? ๐Ÿ˜…
๊ทผ๋ฐ ๋” ๋ฌด์„œ์šด ๊ฑด, ์•Œ๋ฉด์„œ๋„ ๋ฐฉ์–ด ์•ˆ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ›จ์”ฌ ๋งŽ๋‹ค๋Š” ๊ฑฐ์ž„ใ…‹ใ…‹
OWASP(์˜คํ”ˆ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ ํ”„๋กœ์ ํŠธ)์—์„œ ๋งค๋…„ ๋ฐœํ‘œํ•˜๋Š” Top 10 ๋ณด์•ˆ ์ทจ์•ฝ์ ์—์„œ
SQL Injection์€ ๊ฑฐ์˜ ๋งค๋ฒˆ ์ƒ์œ„๊ถŒ์„ ์ฐจ์ง€ํ•˜๊ณ  ์žˆ์–ด. ์ง„์งœ ๋ ˆ์ „๋“œ๊ธ‰ ๊ณต๊ฒฉ์ž„.

์ด ๊ธ€์—์„œ๋Š” SQL Injection์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ป๊ฒŒ ๋ง‰๋Š”์ง€๋ฅผ
์‹ค์ œ ์ฝ”๋“œ ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์™„์ „ ํŒฉํŠธ ์œ„์ฃผ๋กœ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ. ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ ์•Œ์•„์•ผ ํ•˜๋Š” ๋‚ด์šฉ์ด๋‹ˆ๊นŒ
์ปคํ”ผ ํ•œ ์ž” ๋“ค๊ณ  ํŽธํ•˜๊ฒŒ ์ฝ์–ด๋ด โ˜•


SQL Injection ๊ณต๊ฒฉ ํ๋ฆ„๋„ ๐Ÿ˜ˆ ๊ณต๊ฒฉ์ž ์•…์„ฑ ์ž…๋ ฅ๊ฐ’ ์‚ฝ์ž… ๐ŸŒ ์›น ์„œ๋ฒ„ ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ์—†์ด SQL ์ฟผ๋ฆฌ ์ƒ์„ฑ ๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•…์„ฑ ์ฟผ๋ฆฌ ์‹คํ–‰! ๋ฐ์ดํ„ฐ ์œ ์ถœ/๋ณ€์กฐ ๐Ÿ’€ ํ”ผํ•ด ๋ฐœ์ƒ ๊ฐœ์ธ์ •๋ณด ์œ ์ถœ DB ์‚ญ์ œ/๋ณ€์กฐ ์•…์„ฑ ์ž…๋ ฅ๊ฐ’: ' OR '1'='1' -- ์‹คํ–‰ ์ฟผ๋ฆฌ: SELECT * FROM users WHERE id='' OR '1'='1' --' AND pw='...' โœ… ๋ฐฉ์–ด ์ ์šฉ ์‹œ ๐Ÿ˜ˆ ๊ณต๊ฒฉ์ž ์•…์„ฑ ์ž…๋ ฅ ์‹œ๋„ ๐Ÿ›ก๏ธ ์›น ์„œ๋ฒ„ Prepared Statement ์ž…๋ ฅ๊ฐ’ ๋ฌด๋ ฅํ™”! ๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋จ โœ… ๊ณต๊ฒฉ ์ฐจ๋‹จ! ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ ์™„๋ฃŒ ๋ฐฉ์–ด ์—†์Œ โ†’ ๊ณต๊ฒฉ ์„ฑ๊ณต | ๋ฐฉ์–ด ์ ์šฉ โ†’ ๊ณต๊ฒฉ ์ฐจ๋‹จ

๐Ÿค” SQL Injection์ด ๋ญ”๋ฐ?

SQL Injection(SQL ์ธ์ ์…˜)์€ ๊ณต๊ฒฉ์ž๊ฐ€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ž…๋ ฅ ํ•„๋“œ์— ์•…์˜์ ์ธ SQL ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•ด์„œ
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๊ณต๊ฒฉ ๊ธฐ๋ฒ•์ด์•ผ. 1998๋…„์— ์ฒ˜์Œ ๊ณต๊ฐœ์ ์œผ๋กœ ๋ฌธ์„œํ™”๋œ ์ดํ›„๋กœ
๋ฌด๋ ค 25๋…„ ๋„˜๊ฒŒ ํ˜„์—ญ์œผ๋กœ ํ™œ์•ฝ ์ค‘์ธ ๋ ˆ์ „๋“œ ๊ณต๊ฒฉ์ž„ ใ„ทใ„ท

๐Ÿ’ฌ ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ์ด๋Ÿฐ ๊ฑฐ์•ผ:

๋กœ๊ทธ์ธ ํผ์— ์•„์ด๋”” ์ž…๋ ฅํ•˜๋Š” ์นธ์ด ์žˆ์ž–์•„?
๊ฑฐ๊ธฐ์— ๊ทธ๋ƒฅ ์•„์ด๋”” ๋Œ€์‹  SQL ๋ช…๋ น์–ด๋ฅผ ๋„ฃ์–ด๋ฒ„๋ฆฌ๋Š” ๊ฑฐ์ž„.
์„œ๋ฒ„๊ฐ€ ๊ทธ๊ฑธ ๊ทธ๋Œ€๋กœ DB์— ์‹คํ–‰์‹œ์ผœ๋ฒ„๋ฆฌ๋ฉด... ๋. ํ•ดํ‚น ์„ฑ๊ณต ๐ŸŽฏ

์‹ค์ œ๋กœ ์–ผ๋งˆ๋‚˜ ์‹ฌ๊ฐํ•˜๋ƒ๋ฉด:

โš ๏ธ ์‹ค์ œ ํ”ผํ•ด ์‚ฌ๋ก€๋“ค โ€ข 2008๋…„ ํ•˜ํŠธ๋žœ๋“œ ํŽ˜์ด๋จผํŠธ ์‹œ์Šคํ…œ: SQL Injection์œผ๋กœ 1์–ต 3์ฒœ๋งŒ ๊ฑด ์ด์ƒ์˜ ์‹ ์šฉ์นด๋“œ ์ •๋ณด ์œ ์ถœ
โ€ข 2012๋…„ ์•ผํ›„ ๋ณด์ด์Šค: 45๋งŒ ๊ฐœ ๊ณ„์ • ์ •๋ณด ์œ ์ถœ
โ€ข 2015๋…„ ์˜๊ตญ ํ†ต์‹ ์‚ฌ TalkTalk: 15๋งŒ 7์ฒœ ๋ช… ๊ฐœ์ธ์ •๋ณด ์œ ์ถœ, ์•ฝ 400์–ต ์› ํ”ผํ•ด
โ€ข ๊ตญ๋‚ด ์‚ฌ๋ก€: ์ˆ˜๋งŽ์€ ์‡ผํ•‘๋ชฐ, ์ปค๋ฎค๋‹ˆํ‹ฐ ์‚ฌ์ดํŠธ๋“ค์ด SQL Injection์œผ๋กœ ํšŒ์› ์ •๋ณด ์œ ์ถœ

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


โš™๏ธ ๊ณต๊ฒฉ ์›๋ฆฌ: ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š” ๊ฑฐ์•ผ?

SQL Injection์˜ ํ•ต์‹ฌ ์›๋ฆฌ๋Š” "์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’์ด SQL ์ฟผ๋ฆฌ์˜ ์ผ๋ถ€๋กœ ์ง์ ‘ ์‚ฝ์ž…"๋˜๋Š” ๊ฑฐ์•ผ.
์ฝ”๋“œ๋กœ ๋ณด๋ฉด ๋ฐ”๋กœ ์ดํ•ด๋จ.

๐Ÿ˜ฑ ์ทจ์•ฝํ•œ ์ฝ”๋“œ ์˜ˆ์‹œ

PHP๋กœ ์ž‘์„ฑ๋œ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋ณด์ž:

// โŒ ์ทจ์•ฝํ•œ ์ฝ”๋“œ (์ ˆ๋Œ€ ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ!)
$id = $_POST['username'];
$pw = $_POST['password'];

$query = "SELECT * FROM users WHERE username='" . $id . "' AND password='" . $pw . "'";
$result = mysqli_query($conn, $query);

if(mysqli_num_rows($result) > 0) {
    echo "๋กœ๊ทธ์ธ ์„ฑ๊ณต!";
}

์ด ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์ด ๊ทธ๋Œ€๋กœ SQL ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์— ๋ถ™์–ด๋ฒ„๋ ค.
์—ฌ๊ธฐ์„œ ๊ณต๊ฒฉ์ž๊ฐ€ ์ด๋ ‡๊ฒŒ ์ž…๋ ฅํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

์•„์ด๋”” ์ž…๋ ฅ: admin' --
๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ: ์•„๋ฌด๊ฑฐ๋‚˜

๊ทธ๋Ÿฌ๋ฉด ์‹ค์ œ๋กœ ์‹คํ–‰๋˜๋Š” ์ฟผ๋ฆฌ๋Š”:

SELECT * FROM users WHERE username='admin' --' AND password='์•„๋ฌด๊ฑฐ๋‚˜'
๐Ÿ’ก ์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ! SQL์—์„œ --๋Š” ์ฃผ์„์„ ์˜๋ฏธํ•ด.
๊ทธ๋ž˜์„œ -- ๋’ค์— ์˜ค๋Š” AND password='์•„๋ฌด๊ฑฐ๋‚˜' ๋ถ€๋ถ„์ด ํ†ต์งธ๋กœ ๋ฌด์‹œ๋จ!
๊ฒฐ๊ณผ์ ์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ ์—†์ด admin ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ์ด ๋˜๋Š” ๊ฑฐ์•ผ. ๐Ÿ˜ฑ
๐ŸŽญ ๋‹ค์–‘ํ•œ ๊ณต๊ฒฉ ํŒจํ„ด๋“ค

SQL Injection์€ ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•๋งŒ ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ์•ผ. ์ง„์งœ ๋‹ค์–‘ํ•œ ๋ณ€ํ˜•์ด ์žˆ์–ด.

1
Classic SQL Injection (๊ณ ์ „์  ์ธ์ ์…˜)
์œ„์—์„œ ๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ ์ง์ ‘ SQL ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๋ฐฉ์‹.
' OR '1'='1, ' OR 1=1 -- ๊ฐ™์€ ํŒจํ„ด์ด ๋Œ€ํ‘œ์ ์ด์•ผ.
2
UNION-based SQL Injection
UNION ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•ด์„œ ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹.
' UNION SELECT username, password FROM users --
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์›๋ž˜ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์— ๋‹ค๋ฅธ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ๊ฐ€ ํ•ฉ์ณ์ ธ์„œ ๋‚˜์™€๋ฒ„๋ฆผ.
3
Blind SQL Injection (๋ธ”๋ผ์ธ๋“œ ์ธ์ ์…˜)
์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋‚˜ ์ง์ ‘์ ์ธ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ์ด ์—†์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹.
์ฐธ/๊ฑฐ์ง“ ์‘๋‹ต๋งŒ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ธกํ•ด๋‚˜๊ฐ€๋Š” ๋ฐฉ๋ฒ•์ด์•ผ.
' AND 1=1 -- (์ฐธ์ด๋ฉด ์ •์ƒ ์‘๋‹ต)
' AND 1=2 -- (๊ฑฐ์ง“์ด๋ฉด ๋‹ค๋ฅธ ์‘๋‹ต)
์ด ์ฐจ์ด๋กœ DB ๊ตฌ์กฐ๋ฅผ ํ•˜๋‚˜์”ฉ ํŒŒ์•…ํ•ด๋‚˜๊ฐ€๋Š” ๊ฑฐ์ž„. ๋А๋ฆฌ์ง€๋งŒ ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•.
4
Time-based Blind SQL Injection
์‘๋‹ต ์‹œ๊ฐ„์˜ ์ฐจ์ด๋กœ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๋Š” ๋ฐฉ์‹.
' AND SLEEP(5) -- ๊ฐ™์€ ๋ช…๋ น์œผ๋กœ DB๊ฐ€ 5์ดˆ ๋™์•ˆ ๋ฉˆ์ถ”๋ฉด
"์•„, ์ด ์กฐ๊ฑด์ด ์ฐธ์ด๊ตฌ๋‚˜"๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฑฐ์•ผ.
์ง„์งœ ์ง‘์š”ํ•œ ๊ณต๊ฒฉ ๋ฐฉ์‹์ž„ ใ„ทใ„ท
5
Error-based SQL Injection
์˜๋„์ ์œผ๋กœ SQL ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์„œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์— ๋‹ด๊ธด DB ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ๋ฐฉ์‹.
์—๋Ÿฌ ๋ฉ”์‹œ์ง€์— ํ…Œ์ด๋ธ”๋ช…, ์ปฌ๋Ÿผ๋ช…, ์‹ฌ์ง€์–ด ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๋…ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์–ด.
6
Stacked Queries (๋‹ค์ค‘ ์ฟผ๋ฆฌ ์ธ์ ์…˜)
์„ธ๋ฏธ์ฝœ๋ก (;)์„ ์ด์šฉํ•ด์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ SQL ๋ช…๋ น์„ ํ•œ ๋ฒˆ์— ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ์‹.
'; DROP TABLE users; --
์ด๊ฒŒ ์„ฑ๊ณตํ•˜๋ฉด... ํ…Œ์ด๋ธ”์ด ํ†ต์งธ๋กœ ๋‚ ์•„๊ฐ€๋ฒ„๋ฆผ ๐Ÿ’€
7
Out-of-band SQL Injection
DNS๋‚˜ HTTP ์š”์ฒญ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์™ธ๋ถ€ ์„œ๋ฒ„๋กœ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹.
๋„คํŠธ์›Œํฌ ์ฑ„๋„์„ ์ด์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋นผ๋‚ด๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•์ด์•ผ.
ํƒ์ง€๊ฐ€ ์–ด๋ ต๊ณ  ๋ฐฉํ™”๋ฒฝ์„ ์šฐํšŒํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ผ.

๐Ÿ” ์‹ค์ œ ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค: ์ด๋ ‡๊ฒŒ ํ„ธ๋ฆฐ๋‹ค

์ด๋ก ๋งŒ ๋ณด๋ฉด ๊ฐ์ด ์•ˆ ์˜ค๋‹ˆ๊นŒ, ์‹ค์ œ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ๋ณด์—ฌ์ค„๊ฒŒ.
์‡ผํ•‘๋ชฐ ์‚ฌ์ดํŠธ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž. ์ƒํ’ˆ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์ด ์žˆ๊ณ , URL์ด ์ด๋Ÿฐ ์‹์ด์•ผ:

https://shop.example.com/product?id=123

์„œ๋ฒ„ ์ฝ”๋“œ๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ์–ด:

// โŒ ์ทจ์•ฝํ•œ ์ฝ”๋“œ
$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = " . $id;
$result = mysqli_query($conn, $query);
๐Ÿ•ต๏ธ ๊ณต๊ฒฉ์ž์˜ ๋‹จ๊ณ„๋ณ„ ์ ‘๊ทผ
1
์ทจ์•ฝ์  ํƒ์ง€
URL์— ์ž‘์€๋”ฐ์˜ดํ‘œ๋ฅผ ๋„ฃ์–ด๋ด: ?id=123'
SQL ์—๋Ÿฌ๊ฐ€ ๋œจ๋ฉด? ์ทจ์•ฝํ•˜๋‹ค๋Š” ์‹ ํ˜ธ! ๐Ÿšจ
2
์ปฌ๋Ÿผ ์ˆ˜ ํŒŒ์•…
?id=123 ORDER BY 1--
?id=123 ORDER BY 2--
์—๋Ÿฌ๊ฐ€ ์•ˆ ๋‚˜๋Š” ์ตœ๋Œ€ ์ˆซ์ž๋กœ ์ปฌ๋Ÿผ ์ˆ˜๋ฅผ ํŒŒ์•…ํ•จ.
3
DB ์ •๋ณด ์ˆ˜์ง‘
?id=-1 UNION SELECT 1,database(),3--
ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„์„ ํ™”๋ฉด์— ์ถœ๋ ฅ์‹œํ‚ด.
4
ํ…Œ์ด๋ธ” ๋ชฉ๋ก ์กฐํšŒ
?id=-1 UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schema=database()--
๋ชจ๋“  ํ…Œ์ด๋ธ” ์ด๋ฆ„์„ ๊ฐ€์ ธ์˜ด. users ํ…Œ์ด๋ธ” ๋ฐœ๊ฒฌ!
5
๋ฐ์ดํ„ฐ ํƒˆ์ทจ
?id=-1 UNION SELECT 1,concat(username,':',password),3 FROM users--
๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์ ธ์˜ด. ๊ฒŒ์ž„ ์˜ค๋ฒ„ ๐Ÿ’€
โš ๏ธ ์ด ์‹œ๋‚˜๋ฆฌ์˜ค์˜ ๋ฌด์„œ์šด ์  ์œ„ ๊ณผ์ •์ด ์ž๋™ํ™” ํˆด(SQLMap ๊ฐ™์€ ๊ฑฐ)์„ ์“ฐ๋ฉด ๋ช‡ ๋ถ„ ์•ˆ์— ์™„๋ฃŒ๋ผ.
SQLMap์€ ์˜คํ”ˆ์†Œ์Šค ๋„๊ตฌ๋ผ ๋ˆ„๊ตฌ๋‚˜ ๋ฌด๋ฃŒ๋กœ ์“ธ ์ˆ˜ ์žˆ์–ด.
์ฆ‰, ์ „๋ฌธ ํ•ด์ปค๊ฐ€ ์•„๋‹ˆ์–ด๋„ SQL Injection ๊ณต๊ฒฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฑฐ์ž„. ๐Ÿ˜ฐ

๐Ÿ›ก๏ธ SQL Injection ๋ฐฉ์–ด ๊ธฐ๋ฒ• ํ•œ๋ˆˆ์— ๋ณด๊ธฐ SQL Injection ๋ฐฉ์–ด ์ „๋žต ๐Ÿ”’ Prepared Statement ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋ฐฉ์–ด โœ… ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹ ํ—ˆ์šฉ๋œ ๊ฐ’๋งŒ ํ†ต๊ณผ ๐Ÿ”‘ ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ ์ ์šฉ DB ๊ณ„์ • ๊ถŒํ•œ ์ œํ•œ ๐Ÿšซ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ˆจ๊ธฐ๊ธฐ ์ •๋ณด ๋…ธ์ถœ ์ฐจ๋‹จ ๐Ÿ”ฅ WAF ์ ์šฉ ์›น ๋ฐฉํ™”๋ฒฝ ํŒจํ„ด ๊ธฐ๋ฐ˜ ์ฐจ๋‹จ ๐Ÿ“Š ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง ์ด์ƒ ํƒ์ง€ ์‹ค์‹œ๊ฐ„ ๊ฐ์‹œ

๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ธฐ๋ฒ• 1: Prepared Statement (๊ฐ€์žฅ ์ค‘์š”!)

SQL Injection ๋ฐฉ์–ด์˜ ํ•ต์‹ฌ ์ค‘์˜ ํ•ต์‹ฌ์ด์•ผ.
Prepared Statement(์ค€๋น„๋œ ๊ตฌ๋ฌธ)๋Š” SQL ์ฟผ๋ฆฌ์˜ ๊ตฌ์กฐ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•ด์„œ ์ฒ˜๋ฆฌํ•ด.
์ด๊ฒŒ ์™œ ํšจ๊ณผ์ ์ด๋ƒ๋ฉด, ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’์ด ์•„๋ฌด๋ฆฌ SQL ๋ช…๋ น์–ด์ฒ˜๋Ÿผ ์ƒ๊ฒจ๋„
๊ทธ๋ƒฅ ๋ฌธ์ž์—ด ๋ฐ์ดํ„ฐ๋กœ๋งŒ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์ด์•ผ.

๐Ÿ“ ์–ธ์–ด๋ณ„ Prepared Statement ๊ตฌํ˜„

PHP (PDO ๋ฐฉ์‹):

// โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - PDO Prepared Statement
$pdo = new PDO("mysql:host=localhost;dbname=mydb", $user, $pass);

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$result = $stmt->fetchAll();
// โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - PDO Named Parameter
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute([':username' => $username, ':password' => $password]);

PHP (MySQLi ๋ฐฉ์‹):

// โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - MySQLi Prepared Statement
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();

Java (JDBC ๋ฐฉ์‹):

// โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - Java JDBC
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();

Python (sqlite3 ๋ฐฉ์‹):

# โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - Python sqlite3
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?", 
               (username, password))

# โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - Python MySQL Connector
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", 
               (username, password))

Node.js (mysql2 ๋ฐฉ์‹):

// โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - Node.js mysql2
const [rows] = await connection.execute(
  'SELECT * FROM users WHERE username = ? AND password = ?',
  [username, password]
);
โœ… Prepared Statement๊ฐ€ ์•ˆ์ „ํ•œ ์ด์œ  SQL ์ฟผ๋ฆฌ ๊ตฌ์กฐ๊ฐ€ ๋จผ์ € DB ์„œ๋ฒ„์— ์ปดํŒŒ์ผ๋ผ.
๊ทธ ๋‹ค์Œ์— ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋ผ.
DB ์„œ๋ฒ„๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ ˆ๋Œ€ SQL ๋ช…๋ น์–ด๋กœ ํ•ด์„ํ•˜์ง€ ์•Š์•„.
๊ทธ๋ƒฅ ๋ฐ์ดํ„ฐ ๊ฐ’์œผ๋กœ๋งŒ ์ฒ˜๋ฆฌํ•จ. ์™„๋ฒฝํ•œ ๋ถ„๋ฆฌ! ๐ŸŽฏ

๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ธฐ๋ฒ• 2: ORM ์‚ฌ์šฉ

ORM(Object-Relational Mapping)์„ ์‚ฌ์šฉํ•˜๋ฉด SQL์„ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋ผ.
๋Œ€๋ถ€๋ถ„์˜ ORM์€ ๋‚ด๋ถ€์ ์œผ๋กœ Prepared Statement๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—
๊ธฐ๋ณธ์ ์œผ๋กœ SQL Injection์— ์•ˆ์ „ํ•ด.

Python Django ORM:

# โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - Django ORM
from django.contrib.auth.models import User

# ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ Prepared Statement ์‚ฌ์šฉ
user = User.objects.filter(username=username, password=password).first()

# โŒ ์ด๊ฑด ์œ„ํ—˜! raw() ์‚ฌ์šฉ ์‹œ ์ง์ ‘ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ ํ•„์š”
# User.objects.raw("SELECT * FROM users WHERE username='" + username + "'")

Python SQLAlchemy:

# โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - SQLAlchemy ORM
from sqlalchemy.orm import Session

user = session.query(User).filter(
    User.username == username,
    User.password == password
).first()

Java Spring JPA:

// โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - Spring JPA
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsernameAndPassword(String username, String password);
}
๐Ÿ’ก ORM ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ ORM์„ ์จ๋„ raw(), execute() ๊ฐ™์€ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ ์‹คํ–‰ ๋ฉ”์„œ๋“œ๋ฅผ ์“ธ ๋•Œ๋Š”
๋ฐ˜๋“œ์‹œ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ•ด์•ผ ํ•ด!
ORM์ด๋ผ๊ณ  ๋ฌด์กฐ๊ฑด ์•ˆ์ „ํ•œ ๊ฑด ์•„๋‹˜. ์ง์ ‘ SQL ๋ฌธ์ž์—ด ์กฐํ•ฉํ•˜๋ฉด ๋˜‘๊ฐ™์ด ์œ„ํ—˜ํ•ด.

๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ธฐ๋ฒ• 3: ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ๋ฐ ์ด์Šค์ผ€์ดํ•‘

Prepared Statement๋ฅผ ์“ฐ๋Š” ๊ฒŒ ์ตœ์„ ์ด์ง€๋งŒ, ์ถ”๊ฐ€์ ์ธ ๋ฐฉ์–ด์ธต์œผ๋กœ ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ๋„ ํ•ด์ค˜์•ผ ํ•ด.
์ด๊ฑด ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์ด ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ์•ˆ์ „ํ•ด.

โœ… ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹ (๊ถŒ์žฅ)
// โœ… ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹ - ํ—ˆ์šฉ๋œ ๊ฐ’๋งŒ ํ†ต๊ณผ
function validateOrderBy($column) {
    $allowed_columns = ['id', 'name', 'price', 'created_at'];
    
    if (in_array($column, $allowed_columns)) {
        return $column;  // ํ—ˆ์šฉ๋œ ์ปฌ๋Ÿผ๋ช…๋งŒ ๋ฐ˜ํ™˜
    }
    
    return 'id';  // ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
$orderBy = validateOrderBy($_GET['sort']);
$query = "SELECT * FROM products ORDER BY " . $orderBy;  // ์•ˆ์ „!
โš ๏ธ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹ (๋น„๊ถŒ์žฅ)
// โŒ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹ - ์œ„ํ—˜ํ•œ ๋ฌธ์ž ์ œ๊ฑฐ (์™„๋ฒฝํ•˜์ง€ ์•Š์Œ!)
function blacklistFilter($input) {
    $dangerous = ["'", '"', ';', '--', '/*', '*/', 'DROP', 'DELETE', 'INSERT'];
    return str_ireplace($dangerous, '', $input);
}
// ์ด ๋ฐฉ์‹์€ ์šฐํšŒ ๊ฐ€๋Šฅ! ๊ถŒ์žฅํ•˜์ง€ ์•Š์Œ
โš ๏ธ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์˜ ๋ฌธ์ œ์  ๊ณต๊ฒฉ์ž๋“ค์€ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋ฅผ ์šฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์–ด.
์˜ˆ๋ฅผ ๋“ค์–ด SELSELECTECT์ฒ˜๋Ÿผ ์ค‘๊ฐ„์— ํ•„ํ„ฐ๋ง๋  ๋‹จ์–ด๋ฅผ ๋„ฃ์œผ๋ฉด
ํ•„ํ„ฐ๋ง ํ›„์— SELECT๊ฐ€ ๋˜๋Š” ์‹์œผ๋กœ ์šฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•ด.
๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋งŒ์œผ๋กœ๋Š” ์ ˆ๋Œ€ ์™„๋ฒฝํ•œ ๋ฐฉ์–ด๊ฐ€ ์•ˆ ๋จ!
๐Ÿ”ค ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ
// โœ… ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ
function validateId($id) {
    // ์ˆซ์ž๋งŒ ํ—ˆ์šฉ
    if (!is_numeric($id) || $id <= 0) {
        throw new InvalidArgumentException("์œ ํšจํ•˜์ง€ ์•Š์€ ID์ž…๋‹ˆ๋‹ค.");
    }
    return (int)$id;  // ์ •์ˆ˜๋กœ ํ˜•๋ณ€ํ™˜
}

// PHP์—์„œ ์ •์ˆ˜ํ˜•์œผ๋กœ ๊ฐ•์ œ ๋ณ€ํ™˜
$id = (int)$_GET['id'];  // ๋ฌธ์ž์—ด์ด ๋“ค์–ด์™€๋„ ์ •์ˆ˜๋กœ ๋ณ€ํ™˜๋จ

// ์ด๋ฉ”์ผ ํ˜•์‹ ๊ฒ€์ฆ
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if (!$email) {
    die("์œ ํšจํ•˜์ง€ ์•Š์€ ์ด๋ฉ”์ผ ํ˜•์‹์ž…๋‹ˆ๋‹ค.");
}

๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ธฐ๋ฒ• 4: ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ (Principle of Least Privilege)

์ด๊ฑด SQL Injection์ด ์„ฑ๊ณตํ–ˆ์„ ๋•Œ์˜ ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ฐฉ์–ด ์ „๋žต์ด์•ผ.
์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‚ฌ์šฉํ•˜๋Š” DB ๊ณ„์ •์— ๊ผญ ํ•„์š”ํ•œ ๊ถŒํ•œ๋งŒ ๋ถ€์—ฌํ•˜๋Š” ๊ฑฐ์•ผ.

-- โœ… ์ตœ์†Œ ๊ถŒํ•œ DB ๊ณ„์ • ์„ค์ • ์˜ˆ์‹œ (MySQL)

-- ์ฝ๊ธฐ ์ „์šฉ ๊ณ„์ • (์กฐํšŒ๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ)
CREATE USER 'webapp_read'@'localhost' IDENTIFIED BY '๊ฐ•๋ ฅํ•œ๋น„๋ฐ€๋ฒˆํ˜ธ';
GRANT SELECT ON mydb.* TO 'webapp_read'@'localhost';

-- ์ผ๋ฐ˜ ์›น์•ฑ ๊ณ„์ • (CRUD๋งŒ ํ—ˆ์šฉ)
CREATE USER 'webapp_user'@'localhost' IDENTIFIED BY '๊ฐ•๋ ฅํ•œ๋น„๋ฐ€๋ฒˆํ˜ธ';
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'webapp_user'@'localhost';

-- โŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์•ˆ ๋จ! (๋ชจ๋“  ๊ถŒํ•œ ๋ถ€์—ฌ)
-- GRANT ALL PRIVILEGES ON *.* TO 'webapp_user'@'localhost';
๐Ÿ’ก ์ตœ์†Œ ๊ถŒํ•œ ์›์น™์˜ ํšจ๊ณผ โ€ข DROP TABLE ๊ณต๊ฒฉ โ†’ DB ๊ณ„์ •์— DROP ๊ถŒํ•œ ์—†์œผ๋ฉด ์‹คํŒจ!
โ€ข CREATE USER ๊ณต๊ฒฉ โ†’ ๊ถŒํ•œ ์—†์œผ๋ฉด ์‹คํŒจ!
โ€ข ๋‹ค๋ฅธ DB ์ ‘๊ทผ ์‹œ๋„ โ†’ ํ•ด๋‹น DB ๊ถŒํ•œ ์—†์œผ๋ฉด ์‹คํŒจ!
SQL Injection์ด ์„ฑ๊ณตํ•ด๋„ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒŒ ์ œํ•œ๋ผ. ํ”ผํ•ด ์ตœ์†Œํ™”!

๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ธฐ๋ฒ• 5: ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๊ด€๋ฆฌ

SQL ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋ฉด ๊ณต๊ฒฉ์ž์—๊ฒŒ ์—„์ฒญ๋‚œ ์ •๋ณด๋ฅผ ์ฃผ๋Š” ๊ฑฐ์•ผ.
DB ์ข…๋ฅ˜, ํ…Œ์ด๋ธ” ๊ตฌ์กฐ, ์ฟผ๋ฆฌ ๋‚ด์šฉ ๋“ฑ์ด ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์–ด.

// โŒ ์œ„ํ—˜ํ•œ ์ฝ”๋“œ - ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ
try {
    $result = $pdo->query($query);
} catch (PDOException $e) {
    echo "์—๋Ÿฌ: " . $e->getMessage();  // DB ๊ตฌ์กฐ ๋…ธ์ถœ!
}

// โœ… ์•ˆ์ „ํ•œ ์ฝ”๋“œ - ์—๋Ÿฌ ๋กœ๊น… + ์ผ๋ฐ˜ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ
try {
    $result = $pdo->query($query);
} catch (PDOException $e) {
    // ์—๋Ÿฌ๋Š” ์„œ๋ฒ„ ๋กœ๊ทธ์—๋งŒ ๊ธฐ๋ก
    error_log("DB Error: " . $e->getMessage());
    
    // ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฉ”์‹œ์ง€๋งŒ
    echo "์„œ๋น„์Šค ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.";
}

PHP ์„ค์ •์—์„œ๋„ ์—๋Ÿฌ ํ‘œ์‹œ ๋„๊ธฐ:

// php.ini ๋˜๋Š” ์ฝ”๋“œ์—์„œ ์„ค์ •
ini_set('display_errors', 0);      // ์—๋Ÿฌ ํ™”๋ฉด ํ‘œ์‹œ ๋„๊ธฐ
ini_set('log_errors', 1);          // ์—๋Ÿฌ ๋กœ๊ทธ ํŒŒ์ผ์— ๊ธฐ๋ก
ini_set('error_log', '/var/log/php_errors.log');  // ๋กœ๊ทธ ํŒŒ์ผ ๊ฒฝ๋กœ
๐Ÿ’ก ๊ฐœ๋ฐœ ํ™˜๊ฒฝ vs ์šด์˜ ํ™˜๊ฒฝ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ค˜๋„ ๋˜์ง€๋งŒ,
์šด์˜(Production) ํ™˜๊ฒฝ์—์„œ๋Š” ์ ˆ๋Œ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœํ•˜๋ฉด ์•ˆ ๋ผ!
ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ๊ตฌ๋ถ„ํ•ด์„œ ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์ข‹์•„.

๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ธฐ๋ฒ• 6: WAF (์›น ๋ฐฉํ™”๋ฒฝ)

WAF(Web Application Firewall)๋Š” HTTP ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์„ํ•ด์„œ
SQL Injection ํŒจํ„ด์„ ๊ฐ์ง€ํ•˜๊ณ  ์ฐจ๋‹จํ•˜๋Š” ๋ณด์•ˆ ์žฅ์น˜์•ผ.

๐Ÿ”ฅ ์ฃผ์š” WAF ์†”๋ฃจ์…˜๋“ค
1
ModSecurity (์˜คํ”ˆ์†Œ์Šค)
Apache, Nginx, IIS์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์˜คํ”ˆ์†Œ์Šค WAF.
OWASP Core Rule Set(CRS)๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ•๋ ฅํ•œ ๋ฐฉ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•ด.
๋ฌด๋ฃŒ๋ผ์„œ ์ค‘์†Œ ๊ทœ๋ชจ ์„œ๋น„์Šค์— ๋งŽ์ด ์‚ฌ์šฉ๋จ.
2
AWS WAF
AWS ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ด€๋ฆฌํ˜• WAF.
SQL Injection, XSS ๋“ฑ ๋‹ค์–‘ํ•œ ๊ณต๊ฒฉ ํŒจํ„ด์„ ์ž๋™์œผ๋กœ ์ฐจ๋‹จ.
AWS ์ธํ”„๋ผ๋ฅผ ์“ฐ๊ณ  ์žˆ๋‹ค๋ฉด ์‰ฝ๊ฒŒ ์ ์šฉ ๊ฐ€๋Šฅ.
3
Cloudflare WAF
CDN๊ณผ ํ•จ๊ป˜ ์ œ๊ณต๋˜๋Š” WAF ์„œ๋น„์Šค.
์ „ ์„ธ๊ณ„ ํŠธ๋ž˜ํ”ฝ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๊ฐ•๋ ฅํ•œ ์œ„ํ˜‘ ์ธํ…”๋ฆฌ์ „์Šค.
์„ค์ •์ด ๊ฐ„ํŽธํ•˜๊ณ  ์„ฑ๋Šฅ ์˜ํ–ฅ์ด ์ ์–ด.
โš ๏ธ WAF์˜ ํ•œ๊ณ„ WAF๋Š” ์ถ”๊ฐ€์ ์ธ ๋ฐฉ์–ด์ธต์ด์ง€ ์™„๋ฒฝํ•œ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ˆ์•ผ!
์ •๊ตํ•œ ๊ณต๊ฒฉ์ž๋Š” WAF๋ฅผ ์šฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์–ด.
WAF๋งŒ ๋ฏฟ๊ณ  ์ฝ”๋“œ ๋ ˆ๋ฒจ ๋ฐฉ์–ด๋ฅผ ์†Œํ™€ํžˆ ํ•˜๋ฉด ์•ˆ ๋จ.
Prepared Statement + ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ + WAF๋ฅผ ํ•จ๊ป˜ ์จ์•ผ ํ•ด.

๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ธฐ๋ฒ• 7: ์ €์žฅ ํ”„๋กœ์‹œ์ € (Stored Procedure)

์ €์žฅ ํ”„๋กœ์‹œ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด SQL ๋กœ์ง์ด DB ์„œ๋ฒ„์— ๋ฏธ๋ฆฌ ์ €์žฅ๋˜์–ด ์žˆ์–ด์„œ
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ง์ ‘ SQL์„ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋ผ.

-- โœ… MySQL ์ €์žฅ ํ”„๋กœ์‹œ์ € ์ƒ์„ฑ
DELIMITER //
CREATE PROCEDURE GetUser(IN p_username VARCHAR(50), IN p_password VARCHAR(255))
BEGIN
    SELECT id, username, email 
    FROM users 
    WHERE username = p_username AND password = p_password;
END //
DELIMITER ;

-- ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ˜ธ์ถœ
CALL GetUser('ํ™๊ธธ๋™', 'hashedpassword');
// PHP์—์„œ ์ €์žฅ ํ”„๋กœ์‹œ์ € ํ˜ธ์ถœ
$stmt = $pdo->prepare("CALL GetUser(?, ?)");
$stmt->execute([$username, $password]);
$result = $stmt->fetchAll();
๐Ÿ’ก ์ €์žฅ ํ”„๋กœ์‹œ์ €์˜ ์žฅ์  โ€ข SQL ๋กœ์ง์ด DB์— ์บก์Аํ™”๋˜์–ด ์žˆ์–ด์„œ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด ์ˆ˜์ • ๊ฐ€๋Šฅ
โ€ข ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด SQL Injection ๋ฐฉ์–ด
โ€ข DB ์„œ๋ฒ„์—์„œ ๋ฏธ๋ฆฌ ์ปดํŒŒ์ผ๋˜์–ด ์„ฑ๋Šฅ ํ–ฅ์ƒ
โ€ข ๋‹จ, ์ž˜๋ชป ๊ตฌํ˜„ํ•˜๋ฉด ์ €์žฅ ํ”„๋กœ์‹œ์ € ๋‚ด๋ถ€์—์„œ๋„ SQL Injection ๋ฐœ์ƒ ๊ฐ€๋Šฅ!

๐Ÿ” ํƒ์ง€ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง

๋ฐฉ์–ด๋งŒํผ ์ค‘์š”ํ•œ ๊ฒŒ ํƒ์ง€์•ผ. ๊ณต๊ฒฉ์ด ๋“ค์–ด์˜ค๊ณ  ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๋ฉด ๋Œ€์‘๋„ ๋ชป ํ•˜์ž–์•„.

๐Ÿ“Š ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง
# Nginx ๋กœ๊ทธ์—์„œ SQL Injection ํŒจํ„ด ํƒ์ง€
grep -i "union\|select\|insert\|drop\|delete\|update\|exec\|script" /var/log/nginx/access.log

# ํŠน์ • IP์˜ ๋น„์ •์ƒ์ ์ธ ์š”์ฒญ ์ˆ˜ ํ™•์ธ
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
๐Ÿšจ IDS/IPS ํ™œ์šฉ

Snort, Suricata ๊ฐ™์€ ๋„คํŠธ์›Œํฌ ์นจ์ž… ํƒ์ง€ ์‹œ์Šคํ…œ(IDS)์„ ํ™œ์šฉํ•˜๋ฉด
SQL Injection ํŒจํ„ด์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€ํ•˜๊ณ  ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ์–ด.

๐Ÿ“ˆ ์ด์ƒ ํƒ์ง€ ์ง€ํ‘œ๋“ค
1
๋น„์ •์ƒ์ ์ธ ์—๋Ÿฌ์œจ ์ฆ๊ฐ€
SQL ์—๋Ÿฌ๊ฐ€ ๊ฐ‘์ž๊ธฐ ๋งŽ์ด ๋ฐœ์ƒํ•˜๋ฉด ๊ณต๊ฒฉ ์‹œ๋„ ์‹ ํ˜ธ์ผ ์ˆ˜ ์žˆ์–ด.
2
ํŠน์ • IP์—์„œ ๋ฐ˜๋ณต์ ์ธ ์š”์ฒญ
๊ฐ™์€ IP์—์„œ ์งง์€ ์‹œ๊ฐ„์— ์ˆ˜๋ฐฑ~์ˆ˜์ฒœ ๋ฒˆ ์š”์ฒญ์ด ์˜ค๋ฉด ์ž๋™ํ™” ๊ณต๊ฒฉ ๊ฐ€๋Šฅ์„ฑ.
3
URL ํŒŒ๋ผ๋ฏธํ„ฐ์— ํŠน์ˆ˜๋ฌธ์ž ํฌํ•จ
', --, ;, UNION, SELECT ๋“ฑ์ด URL์— ํฌํ•จ๋œ ์š”์ฒญ.
4
์‘๋‹ต ์‹œ๊ฐ„ ์ด์ƒ
Time-based Blind SQL Injection ๊ณต๊ฒฉ ์‹œ ์‘๋‹ต ์‹œ๊ฐ„์ด ๋น„์ •์ƒ์ ์œผ๋กœ ๊ธธ์–ด์ง.

๐Ÿงช ๋ณด์•ˆ ํ…Œ์ŠคํŠธ: ๋‚ด ์ฝ”๋“œ๊ฐ€ ์•ˆ์ „ํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๋ฒ•

๊ฐœ๋ฐœ ์™„๋ฃŒ ํ›„์—๋Š” ๋ฐ˜๋“œ์‹œ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด์•ผ ํ•ด.
์ทจ์•ฝ์ ์„ ์ง์ ‘ ์ฐพ์•„๋ณด๋Š” ๊ฒŒ ์ตœ์„ ์ด์•ผ.

๐Ÿ”ง ์ž๋™ํ™” ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋“ค
1
SQLMap
๊ฐ€์žฅ ์œ ๋ช…ํ•œ ์˜คํ”ˆ์†Œ์Šค SQL Injection ์ž๋™ํ™” ๋„๊ตฌ.
sqlmap -u "http://target.com/page?id=1" --dbs
โš ๏ธ ๋ฐ˜๋“œ์‹œ ๋ณธ์ธ ์†Œ์œ ์˜ ์„œ๋ฒ„์—์„œ๋งŒ ํ…Œ์ŠคํŠธํ•  ๊ฒƒ! ํƒ€์ธ ์„œ๋ฒ„์— ์‚ฌ์šฉํ•˜๋ฉด ๋ถˆ๋ฒ•!
2
OWASP ZAP (Zed Attack Proxy)
OWASP์—์„œ ๋งŒ๋“  ๋ฌด๋ฃŒ ์›น ๋ณด์•ˆ ์Šค์บ๋„ˆ.
SQL Injection์„ ํฌํ•จํ•œ ๋‹ค์–‘ํ•œ ์ทจ์•ฝ์ ์„ ์ž๋™์œผ๋กœ ์Šค์บ”ํ•ด์ค˜.
3
Burp Suite
์›น ๋ณด์•ˆ ์ „๋ฌธ๊ฐ€๋“ค์ด ๋งŽ์ด ์“ฐ๋Š” ๋„๊ตฌ.
Community Edition์€ ๋ฌด๋ฃŒ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.
HTTP ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์„œ ์ˆ˜์ •ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐ ์ตœ์ ํ™”๋˜์–ด ์žˆ์–ด.
4
DVWA (Damn Vulnerable Web Application)
์˜๋„์ ์œผ๋กœ ์ทจ์•ฝํ•˜๊ฒŒ ๋งŒ๋“  ์—ฐ์Šต์šฉ ์›น ์•ฑ.
SQL Injection ๊ณต๊ฒฉ๊ณผ ๋ฐฉ์–ด๋ฅผ ์ง์ ‘ ์‹ค์Šตํ•ด๋ณผ ์ˆ˜ ์žˆ์–ด.
Docker๋กœ ์‰ฝ๊ฒŒ ์„ค์น˜ ๊ฐ€๋Šฅ: docker run -d -p 80:80 vulnerables/web-dvwa
๐Ÿ’ก ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ๋„ ๋ณด์•ˆ ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์–ด! ์ง์ ‘ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ต๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ์žฌ๋Šฅ ๊ณต์œ  ํ”Œ๋žซํผ์—์„œ
์›น ๋ณด์•ˆ ์ „๋ฌธ๊ฐ€์—๊ฒŒ ์ทจ์•ฝ์  ์ ๊ฒ€ ์„œ๋น„์Šค๋ฅผ ์˜๋ขฐํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ.
์ „๋ฌธ๊ฐ€๊ฐ€ ์ง์ ‘ ๋ชจ์˜ ํ•ดํ‚น(Penetration Testing)์„ ํ•ด์ค˜์„œ ์ทจ์•ฝ์ ์„ ์ฐพ์•„์คŒ.

๐Ÿ“‹ SQL Injection ๋ฐฉ์–ด ์ฒดํฌ๋ฆฌ์ŠคํŠธ

๊ฐœ๋ฐœ ์™„๋ฃŒ ํ›„ ์ด ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋กœ ์ ๊ฒ€ํ•ด๋ด! โœ…

โœ“
๋ชจ๋“  DB ์ฟผ๋ฆฌ์— Prepared Statement ๋˜๋Š” Parameterized Query ์‚ฌ์šฉ
๋™์  SQL ๋ฌธ์ž์—ด ์กฐํ•ฉ ๋ฐฉ์‹ ์™„์ „ ์ œ๊ฑฐ
โœ“
๋ชจ๋“  ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’์— ๋Œ€ํ•œ ํƒ€์ž… ๋ฐ ํ˜•์‹ ๊ฒ€์ฆ
์ˆซ์ž๋Š” ์ˆซ์ž๋กœ, ์ด๋ฉ”์ผ์€ ์ด๋ฉ”์ผ ํ˜•์‹์œผ๋กœ ๊ฒ€์ฆ
โœ“
DB ๊ณ„์ • ์ตœ์†Œ ๊ถŒํ•œ ์„ค์ •
์›น์•ฑ DB ๊ณ„์ •์— DROP, CREATE ๋“ฑ ๋ถˆํ•„์š”ํ•œ ๊ถŒํ•œ ์ œ๊ฑฐ
โœ“
์šด์˜ ํ™˜๊ฒฝ์—์„œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ˆจ๊ธฐ๊ธฐ
์—๋Ÿฌ๋Š” ๋กœ๊ทธ ํŒŒ์ผ์—๋งŒ ๊ธฐ๋ก, ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ์ผ๋ฐ˜ ๋ฉ”์‹œ์ง€๋งŒ ํ‘œ์‹œ
โœ“
WAF ์ ์šฉ
ModSecurity, AWS WAF, Cloudflare WAF ๋“ฑ ์ ์šฉ
โœ“
์ •๊ธฐ์ ์ธ ๋ณด์•ˆ ์Šค์บ” ์‹ค์‹œ
OWASP ZAP, Burp Suite ๋“ฑ์œผ๋กœ ์ฃผ๊ธฐ์  ์ทจ์•ฝ์  ์ ๊ฒ€
โœ“
DB ์ ‘๊ทผ ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง
๋น„์ •์ƒ์ ์ธ ์ฟผ๋ฆฌ ํŒจํ„ด ์‹ค์‹œ๊ฐ„ ๊ฐ์ง€ ๋ฐ ์•Œ๋ฆผ ์„ค์ •
โœ“
DB ๋ฐ ์›น ์„œ๋ฒ„ ์ตœ์‹  ๋ฒ„์ „ ์œ ์ง€
์•Œ๋ ค์ง„ ์ทจ์•ฝ์  ํŒจ์น˜ ์ ์šฉ
โœ“
์ค‘์š” ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”
๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” bcrypt, Argon2 ๋“ฑ ๊ฐ•๋ ฅํ•œ ํ•ด์‹œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ์ €์žฅ
โœ“
์ •๊ธฐ์ ์ธ ๋ฐฑ์—…
SQL Injection์œผ๋กœ ๋ฐ์ดํ„ฐ ์‚ญ์ œ/๋ณ€์กฐ ์‹œ ๋ณต๊ตฌ ๊ฐ€๋Šฅํ•˜๋„๋ก ๋ฐฑ์—… ์œ ์ง€

๐ŸŽฏ ์‹ค์ „ ํŒ: ํ”„๋ ˆ์ž„์›Œํฌ๋ณ„ ๋ณด์•ˆ ์„ค์ •
๐Ÿ˜ Laravel (PHP)
// โœ… Laravel Eloquent ORM - ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ˆ์ „
$user = User::where('username', $username)
            ->where('password', $password)
            ->first();

// โœ… Laravel Query Builder - ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ ์ž๋™
$user = DB::table('users')
          ->where('username', $username)
          ->where('password', $password)
          ->first();

// โœ… Raw Query ์‚ฌ์šฉ ์‹œ ๋ฐ”์ธ๋”ฉ ํ•„์ˆ˜
$user = DB::select('SELECT * FROM users WHERE username = ? AND password = ?', 
                   [$username, $password]);
๐Ÿ Django (Python)
# โœ… Django ORM - ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ˆ์ „
from django.contrib.auth.models import User
user = User.objects.get(username=username)

# โœ… Django Raw SQL - ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ ์‚ฌ์šฉ
from django.db import connection
with connection.cursor() as cursor:
    cursor.execute("SELECT * FROM users WHERE username = %s", [username])
    row = cursor.fetchone()

# settings.py ๋ณด์•ˆ ์„ค์ •
DEBUG = False  # ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋ฐ˜๋“œ์‹œ False
ALLOWED_HOSTS = ['yourdomain.com']  # ํ—ˆ์šฉ ๋„๋ฉ”์ธ ๋ช…์‹œ
โ˜• Spring Boot (Java)
// โœ… Spring Data JPA - ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ˆ์ „
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // ๋ฉ”์„œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ฟผ๋ฆฌ ์ž๋™ ์ƒ์„ฑ (์•ˆ์ „)
    Optional<User> findByUsername(String username);
    
    // @Query ์‚ฌ์šฉ ์‹œ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ ํ•„์ˆ˜
    @Query("SELECT u FROM User u WHERE u.username = :username")
    Optional<User> findByUsernameQuery(@Param("username") String username);
}

// application.properties ๋ณด์•ˆ ์„ค์ •
spring.jpa.show-sql=false  // ์šด์˜ ํ™˜๊ฒฝ์—์„œ SQL ๋กœ๊ทธ ๋„๊ธฐ
๐ŸŸข Express.js (Node.js)
// โœ… Sequelize ORM - ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ˆ์ „
const user = await User.findOne({
    where: {
        username: username,
        password: password
    }
});

// โœ… mysql2 ์ง์ ‘ ์‚ฌ์šฉ ์‹œ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ
const [rows] = await pool.execute(
    'SELECT * FROM users WHERE username = ? AND password = ?',
    [username, password]
);

// Helmet.js๋กœ ๋ณด์•ˆ ํ—ค๋” ์„ค์ •
const helmet = require('helmet');
app.use(helmet());

๐ŸŒ Second-Order SQL Injection: ๋” ๊ต๋ฌ˜ํ•œ ๊ณต๊ฒฉ

์ผ๋ฐ˜์ ์ธ SQL Injection์€ ์ž…๋ ฅ ์ฆ‰์‹œ ๊ณต๊ฒฉ์ด ์‹คํ–‰๋˜๋Š”๋ฐ,
Second-Order SQL Injection์€ ์ข€ ๋” ๊ต๋ฌ˜ํ•ด. ๐Ÿ˜ˆ

๐Ÿ’ฌ Second-Order SQL Injection์ด๋ž€?

1๋‹จ๊ณ„: ์•…์„ฑ ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅํ•  ๋•Œ๋Š” ์ด์Šค์ผ€์ดํ•‘์ด ์ ์šฉ๋ผ์„œ ์•ˆ์ „ํ•ด ๋ณด์ž„
2๋‹จ๊ณ„: ๋‚˜์ค‘์— ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊บผ๋‚ด์„œ SQL์— ์‚ฌ์šฉํ•  ๋•Œ ๊ณต๊ฒฉ์ด ์‹คํ–‰๋จ

์ฆ‰, ์ €์žฅํ•  ๋•Œ ๋ฐฉ์–ดํ–ˆ๋‹ค๊ณ  ์•ˆ์‹ฌํ•˜๋ฉด ์•ˆ ๋œ๋‹ค๋Š” ๊ฑฐ์•ผ!
// ์‹œ๋‚˜๋ฆฌ์˜ค: ํšŒ์›๊ฐ€์ž… ์‹œ ์•…์„ฑ ์‚ฌ์šฉ์ž๋ช… ์ €์žฅ
// ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ username: admin'--

// 1๋‹จ๊ณ„: ์ €์žฅ ์‹œ ์ด์Šค์ผ€์ดํ•‘ ์ ์šฉ (์•ˆ์ „ํ•ด ๋ณด์ž„)
$username = mysqli_real_escape_string($conn, $_POST['username']);
// $username = "admin'--" โ†’ ์ €์žฅ๋จ

// 2๋‹จ๊ณ„: ๋‚˜์ค‘์— ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๊ธฐ๋Šฅ์—์„œ ๊บผ๋‚ด์„œ ์‚ฌ์šฉ
$username = $row['username'];  // DB์—์„œ ๊บผ๋‚ธ ๊ฐ’: admin'--
// ์ด์Šค์ผ€์ดํ•‘ ์—†์ด SQL์— ์ง์ ‘ ์‚ฌ์šฉ!
$query = "UPDATE users SET password='$newpw' WHERE username='$username'";
// ์‹คํ–‰๋˜๋Š” ์ฟผ๋ฆฌ: UPDATE users SET password='...' WHERE username='admin'--'
// admin ๊ณ„์ •์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ณ€๊ฒฝ๋จ!
โœ… Second-Order SQL Injection ๋ฐฉ์–ด๋ฒ• ํ•ด๊ฒฐ์ฑ…์€ ๊ฐ„๋‹จํ•ด: DB์—์„œ ๊บผ๋‚ธ ๋ฐ์ดํ„ฐ๋„ SQL์— ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ Prepared Statement ์‚ฌ์šฉ!
"DB์—์„œ ์˜จ ๋ฐ์ดํ„ฐ๋‹ˆ๊นŒ ์•ˆ์ „ํ•˜๊ฒ ์ง€"๋ผ๋Š” ์ƒ๊ฐ์€ ๊ธˆ๋ฌผ์ด์•ผ.
๋ชจ๋“  SQL ์ฟผ๋ฆฌ์— ์ผ๊ด€๋˜๊ฒŒ Prepared Statement๋ฅผ ์ ์šฉํ•˜๋ฉด ์ด ๋ฌธ์ œ๋„ ํ•ด๊ฒฐ๋จ.

๐Ÿ“š ๋งˆ๋ฌด๋ฆฌ: ๋ณด์•ˆ์€ ์Šต๊ด€์ด์•ผ

SQL Injection์€ ์˜ค๋ž˜๋œ ๊ณต๊ฒฉ์ด์ง€๋งŒ ์—ฌ์ „ํžˆ ํ˜„์—ญ์ด์•ผ.
OWASP Top 10์—์„œ 2021๋…„ ๊ธฐ์ค€์œผ๋กœ "Injection" ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ 3์œ„๋ฅผ ์ฐจ์ง€ํ•˜๊ณ  ์žˆ์–ด.
(2017๋…„๊นŒ์ง€๋Š” 1์œ„์˜€์Œ. ์—ฌ์ „ํžˆ ์ƒ์œ„๊ถŒ ์œ ์ง€ ์ค‘ ใ„ทใ„ท)

๊ทผ๋ฐ ์‚ฌ์‹ค ๋ฐฉ์–ด ๋ฐฉ๋ฒ•์€ ๋ช…ํ™•ํ•ด. ๋ณต์žกํ•˜์ง€๋„ ์•Š์•„.

โœ… ํ•ต์‹ฌ ์š”์•ฝ ๐Ÿฅ‡ 1์ˆœ์œ„: Prepared Statement / Parameterized Query - ์ด๊ฒƒ๋งŒ ์ œ๋Œ€๋กœ ์จ๋„ 90% ํ•ด๊ฒฐ
๐Ÿฅˆ 2์ˆœ์œ„: ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ - ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์œผ๋กœ ์ถ”๊ฐ€ ๋ฐฉ์–ด
๐Ÿฅ‰ 3์ˆœ์œ„: ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ - ํ”ผํ•ด ์ตœ์†Œํ™”
๐Ÿ… ์ถ”๊ฐ€: WAF, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๊ด€๋ฆฌ, ๋ชจ๋‹ˆํ„ฐ๋ง - ๋‹ค์ธต ๋ฐฉ์–ด ์™„์„ฑ

๊ฐœ๋ฐœํ•  ๋•Œ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ณด์•ˆ์„ ๊ณ ๋ คํ•˜๋Š” "Security by Design" ๋งˆ์ธ๋“œ์…‹์ด ์ค‘์š”ํ•ด.
๋‚˜์ค‘์— ๋ณด์•ˆ ํŒจ์น˜ํ•˜๋Š” ๊ฒŒ ์ฒ˜์Œ๋ถ€ํ„ฐ ์•ˆ์ „ํ•˜๊ฒŒ ์งœ๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ํž˜๋“ค๊ณ  ๋น„์šฉ๋„ ๋งŽ์ด ๋“ค์–ด.

๐Ÿ’ฌ ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•œ๋งˆ๋””:

"๋‚ด ์‚ฌ์ดํŠธ๋Š” ์ž‘์•„์„œ ํ•ดํ‚น ์•ˆ ๋‹นํ•ด"๋ผ๋Š” ์ƒ๊ฐ์€ ๋ฒ„๋ ค!
์ž๋™ํ™” ํˆด์€ ์ธํ„ฐ๋„ท์˜ ๋ชจ๋“  ์‚ฌ์ดํŠธ๋ฅผ ์Šค์บ”ํ•˜๊ฑฐ๋“ .
๊ทœ๋ชจ์™€ ์ƒ๊ด€์—†์ด ์ทจ์•ฝ์ ์ด ์žˆ์œผ๋ฉด ํ„ธ๋ ค.

์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์ฒ˜๋Ÿผ ๋‹ค์–‘ํ•œ ์‚ฌ๋žŒ๋“ค์ด ์ด์šฉํ•˜๋Š” ํ”Œ๋žซํผ์ผ์ˆ˜๋ก
๋ณด์•ˆ์ด ๋”์šฑ ์ค‘์š”ํ•ด. ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์ง€ํ‚ค๋Š” ๊ฑด ๊ฐœ๋ฐœ์ž์˜ ์ฑ…์ž„์ด์•ผ! ๐Ÿ’ช
๐Ÿ’ก ๋” ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? โ€ข OWASP ๊ณต์‹ ๋ฌธ์„œ: owasp.org - SQL Injection Prevention Cheat Sheet
โ€ข PortSwigger Web Security Academy: ๋ฌด๋ฃŒ SQL Injection ์‹ค์Šต ํ™˜๊ฒฝ ์ œ๊ณต
โ€ข HackTheBox, TryHackMe: ์‹ค์ œ ํ•ดํ‚น ์‹ค์Šต ํ”Œ๋žซํผ (ํ•ฉ๋ฒ•์ ์ธ ํ™˜๊ฒฝ์—์„œ!)
โ€ข DVWA: ๋กœ์ปฌ์—์„œ ์ง์ ‘ ๊ณต๊ฒฉ/๋ฐฉ์–ด ์‹ค์Šต ๊ฐ€๋Šฅ
๋Œ“๊ธ€ ์ž‘์„ฑ

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

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

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