
PHP ์จ๋ผ์ธ ๊ฐ์
>PHP - ์ค๊ธ
๐ PHP ์ค๊ธ - 1์ฃผ์ฐจ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ธฐ์ด ๋ฐ MySQL ์ฐ๋ (PDO) - 04 SQL Injection ๋ฐฉ์ง๋ฅผ ์ํ Prepared Statement
![]() |
ํ์ | 10.0 | ๋ผ์ด์ผ์ค | free |
---|---|---|---|---|
์ฌ์ฉ์ํ์ | 10.0 | ์ด์์ฒด์ | ||
๋ค์ด๋ก๋ | 1 | ํ์ผํฌ๊ธฐ | 0 | |
์ ์์ฌ | LUZENSOFT | ๋ฑ๋ก์ผ | 2025-07-12 12:38:25 | |
์กฐํ์ | 39 |
๐ PHP ์ค๊ธ - 1์ฃผ์ฐจ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ธฐ์ด ๋ฐ MySQL ์ฐ๋ (PDO) - 04 SQL Injection ๋ฐฉ์ง๋ฅผ ์ํ Prepared Statement
#SQL ์ธ์ ์ (Injection)์ด๋ ๋ฌด์์ธ๊ฐ?
์๋ ํ์ธ์! #PHP ์ค๊ธ ๊ณผ์ ์ ๋ค ๋ฒ์งธ ์๊ฐ์ ๋๋ค. ์ง๋ ์๊ฐ์๋ #PDO๋ฅผ ์ด์ฉํ #๋ฐ์ดํฐ๋ฒ ์ด์ค #์ฐ๊ฒฐ ๋ฐฉ๋ฒ์ ๋ํด ์์ธํ ์์๋ดค์ฃ . ์ค๋์ ์น #๋ณด์์์ ๊ฐ์ฅ ์ค์ํ๊ณ ๊ธฐ๋ณธ์ ์ธ ๋ถ๋ถ์ธ #SQL #์ธ์ ์ ์ ๋ํด ์ดํดํ๊ณ , ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ํต์ฌ ๊ธฐ์ ์ธ #Prepared #Statement์ ๋ํด ํ์ตํ ๊ฑฐ์์.
SQL ์ธ์ ์ ์ #์น #์ ํ๋ฆฌ์ผ์ด์ ์ #๋ณด์ #์ทจ์ฝ์ ์ ์ด์ฉํ ๊ณต๊ฒฉ ๊ธฐ๋ฒ ์ค ํ๋์ ๋๋ค. ๊ณต๊ฒฉ์๋ ์น ํ์ด์ง์ ์ ๋ ฅ ํ๋(์: ๋ก๊ทธ์ธ ํผ, ๊ฒ์์ฐฝ)์ #์ ์์ ์ธ #SQL #์ฝ๋ ์กฐ๊ฐ์ ์ฝ์ ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์กฐ์ํ๊ฑฐ๋, #๋ฏผ๊ฐํ #์ ๋ณด๋ฅผ #ํ์ทจํ๊ฑฐ๋, ์ฌ์ง์ด #๋ฐ์ดํฐ๋ฅผ #ํ๊ดดํ ์๋ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅ๋ฐ์ ๋ก๊ทธ์ธํ๋ ํ์ด์ง๊ฐ ์๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค. ๋ง์ฝ ๊ฐ๋ฐ์๊ฐ ์ฌ์ฉ์ ์ ๋ ฅ์ ์ ๋๋ก ๊ฒ์ฆํ์ง ์๊ณ SQL ์ฟผ๋ฆฌ์ ๊ทธ๋๋ก ์ฝ์ ํ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
์๋ ์๋๋ ์ฟผ๋ฆฌ: SELECT * FROM users WHERE username = '์ฌ์ฉ์์
๋ ฅ_์์ด๋' AND password = '์ฌ์ฉ์์
๋ ฅ_๋น๋ฐ๋ฒํธ'
๊ณต๊ฒฉ์๊ฐ ' OR '1'='1' --'๋ฅผ ์ฌ์ฉ์์
๋ ฅ_๋น๋ฐ๋ฒํธ์ ์ฝ์
ํ ๊ฒฝ์ฐ: SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1' --'
์ ์ฟผ๋ฆฌ์์ --
๋ SQL์์ ์ฃผ์์ ์๋ฏธํฉ๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด password = '' OR '1'='1'
๋ถ๋ถ์ ํญ์ ์ฐธ(True)์ด ๋์ด, ๊ณต๊ฒฉ์๋ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ชฐ๋ผ๋ admin
๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธํ ์ ์๊ฒ ๋ฉ๋๋ค. ์ด๋ ๋งค์ฐ ์ํํ ๋ณด์ ๊ตฌ๋ฉ์ด ๋ฉ๋๋ค.
Prepared Statement๊ฐ SQL ์ธ์ ์ ์ ๋ง๋ ์๋ฆฌ
#์ค๋น๋ #๊ตฌ๋ฌธ(Prepared Statement)์ SQL ์ธ์ ์ ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๋ ๊ฐ์ฅ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ์ ๋๋ค. PDO๋ Prepared Statement ๊ธฐ๋ฅ์ ๊ฐ๋ ฅํ๊ฒ ์ง์ํฉ๋๋ค.
Prepared Statement์ ์๋ ์๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ฟผ๋ฆฌ ๊ตฌ์กฐ์ ๋ฐ์ดํฐ ๋ถ๋ฆฌ: SQL ์ฟผ๋ฆฌ์ ๊ตฌ์กฐ(ํ ํ๋ฆฟ)์ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋ฐ์ดํฐ(๋งค๊ฐ๋ณ์)๋ฅผ ๋ถ๋ฆฌํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๋ฌํฉ๋๋ค.
์:
SELECT * FROM users WHERE username = :username AND password = :password
์ฌ๊ธฐ์
:username
๊ณผ:password
๋ #ํ๋ ์ด์คํ๋(placeholder)์ ๋๋ค.
์ฟผ๋ฆฌ ์ปดํ์ผ: ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ํ๋ ์ด์คํ๋๊ฐ ํฌํจ๋ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋จผ์ ๋ฐ์์ ์ปดํ์ผ(์ค๋น)ํฉ๋๋ค. ์ด ๋จ๊ณ์์๋ ์์ง ์ฌ์ฉ์ ๋ฐ์ดํฐ๊ฐ ํฌํจ๋์ง ์์์ผ๋ฏ๋ก, ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ง ํ์ ํ๊ณ ์ต์ ํํฉ๋๋ค.
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ฐ ์คํ: ๋์ค์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณ๋๋ก ์ ์กํ๊ณ , ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ด ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ์ปดํ์ผ๋ ์ฟผ๋ฆฌ ๊ตฌ์กฐ์ '๋ฐ์ธ๋ฉ'ํ์ฌ ์คํํฉ๋๋ค. ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์์ํ ๋ฐ์ดํฐ ๊ฐ์ผ๋ก๋ง ์ธ์ํ๋ฉฐ, ์ด๋ค ๊ฒฝ์ฐ์๋ ์คํ ๊ฐ๋ฅํ SQL ์ฝ๋๋ก ํด์ํ์ง ์์ต๋๋ค.
์ด๋ฌํ ๋ถ๋ฆฌ ๋๋ถ์, ๊ณต๊ฒฉ์๊ฐ ์ ๋ ฅ ํ๋์ ์๋ฌด๋ฆฌ ์ ์์ ์ธ SQL ์ฝ๋๋ฅผ ์ฝ์ ํด๋, ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ด๋ฅผ ๋จ์ํ ํ ์คํธ ๊ฐ์ผ๋ก๋ง ์ธ์ํ๊ณ SQL ๋ช ๋ น์ด๋ก ์คํํ์ง ์๊ฒ ๋ฉ๋๋ค.
#PDO๋ฅผ ์ด์ฉํ Prepared Statement ์ฌ์ฉ๋ฒ
PDO์์ Prepared Statement๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๋จ๊ณ๋ก ๋๋ ์ ์์ต๋๋ค: #์ค๋น(Prepare)์ #์คํ(Execute).
1. #์ค๋น (Prepare)
PDO
๊ฐ์ฒด์ prepare()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ SQL ์ฟผ๋ฆฌ ํ
ํ๋ฆฟ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณด๋
๋๋ค. prepare()
๋ฉ์๋๋ PDOStatement
๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. ์ด ๊ฐ์ฒด๊ฐ ์ค์ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
ํ๋ ์ด์คํ๋๋ ๋ ๊ฐ์ง ๋ฐฉ์ ์ค ํ๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
์ด๋ฆ ์๋ ํ๋ ์ด์คํ๋ (Named Placeholders):
:์ปฌ๋ผ๋ช
๋๋:๋ณ์นญ
๊ณผ ๊ฐ์ด ์ฝ๋ก (:
) ๋ค์ ์ด๋ฆ์ ๋ถ์ ๋๋ค. ๊ฐ๋ ์ฑ์ด ์ข๊ณ , ๋งค๊ฐ๋ณ์๊ฐ ๋ง์ ๋ ์ ์ฉํฉ๋๋ค. (๊ถ์ฅ)PHP
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
๋ฌผ์ํ ํ๋ ์ด์คํ๋ (Question Mark Placeholders):
?
๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋งค๊ฐ๋ณ์ ์์์ ์์กดํ๋ฏ๋ก ์์๊ฐ ๊ผฌ์ด์ง ์๋๋ก ์ฃผ์ํด์ผ ํฉ๋๋ค.PHP
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
2. #๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ (Bind Data)
PDOStatement
๊ฐ์ฒด์๋ ๋ฐ์ดํฐ๋ฅผ ํ๋ ์ด์คํ๋์ ๋ฐ์ธ๋ฉํ๋ ์ฌ๋ฌ ๋ฉ์๋๊ฐ ์์ต๋๋ค.
bindValue(placeholder, value, [data_type])
:๋งค๊ฐ๋ณ์ ๊ฐ์ ํ๋ ์ด์คํ๋์ ๋ฐ์ธ๋ฉํฉ๋๋ค. ํ ๋ฒ ๋ฐ์ธ๋ฉํ๋ฉด ๊ฐ์ด ๊ณ ์ ๋ฉ๋๋ค.
data_type
์PDO::PARAM_STR
(๋ฌธ์์ด),PDO::PARAM_INT
(์ ์) ๋ฑ์ผ๋ก ๋ช ์ํ ์ ์์ผ๋ฉฐ, ์๋ต ์ PDO๊ฐ ์๋์ผ๋ก ์ถ๋ก ํ์ง๋ง ๋ช ์ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
PHP
// ์ด๋ฆ ์๋ ํ๋ ์ด์คํ๋์ ๊ฒฝ์ฐ $stmt->bindValue(':username', $inputUsername, PDO::PARAM_STR); $stmt->bindValue(':password', $inputPassword, PDO::PARAM_STR); // ๋ฌผ์ํ ํ๋ ์ด์คํ๋์ ๊ฒฝ์ฐ (1๋ถํฐ ์์ํ๋ ์ธ๋ฑ์ค) $stmt->bindValue(1, $inputUsername, PDO::PARAM_STR); $stmt->bindValue(2, $inputPassword, PDO::PARAM_STR);
bindParam(placeholder, variable, [data_type])
:๋งค๊ฐ๋ณ์๋ฅผ ๋ณ์ ์ฐธ์กฐ์ ๋ฐ์ธ๋ฉํฉ๋๋ค.
execute()
ํธ์ถ ์์ ์ ๋ณ์์ ํ์ฌ ๊ฐ์ ์ฌ์ฉํฉ๋๋ค. ๋์ผํ ์ฟผ๋ฆฌ๋ฅผ ์ฌ๋ฌ ๋ฒ ์คํํ ๋ ํจ์จ์ ์ ๋๋ค.
PHP
$user = 'some_user'; $pass = 'some_pass'; $stmt->bindParam(':username', $user, PDO::PARAM_STR); $stmt->bindParam(':password', $pass, PDO::PARAM_STR); $stmt->execute(); // $user์ $pass์ ํ์ฌ ๊ฐ์ด ์ฌ์ฉ๋จ $user = 'another_user'; $pass = 'another_pass'; $stmt->execute(); // $user์ $pass์ ์๋ก์ด ๊ฐ์ด ์ฌ์ฉ๋จ
3. #์คํ (Execute)
๋ชจ๋ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์ธ๋ฉํ ํ, execute()
๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ค๋น๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํฉ๋๋ค.
execute()
: ๋ฐ์ธ๋ฉ๋ ๊ฐ์ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์คํํฉ๋๋ค.PHP
$stmt->execute();
execute(array $input_parameters)
:bindValue()
๋bindParam()
์ ์ฌ์ฉํ์ง ์๊ณ ,execute()
๋ฉ์๋์ ์ธ์๋ก ๋ฐฐ์ด์ ์ ๋ฌํ์ฌ ํ ๋ฒ์ ๋ชจ๋ ํ๋ ์ด์คํ๋์ ๊ฐ์ ๋ฐ์ธ๋ฉํ๊ณ ์คํํ ์๋ ์์ต๋๋ค. ์ด ๋ฐฉ๋ฒ์ด ๊ฐ์ฅ ๊ฐ๊ฒฐํ๊ณ ๋ง์ด ์ฌ์ฉ๋ฉ๋๋ค.PHP
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password"); $stmt->execute([ ':username' => $inputUsername, ':password' => $inputPassword ]);
๋ฌผ์ํ ํ๋ ์ด์คํ๋์ ๊ฒฝ์ฐ, ๋ฐฐ์ด์ ์์๋๋ก ๋ฐ์ธ๋ฉ๋ฉ๋๋ค.
PHP
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?"); $stmt->execute([$inputUsername, $inputPassword]);
#Prepared Statement ์ค์ต ์์
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ฝ๋๋ ์ด์ ํฌ์คํ ์ ์ฐธ๊ณ ํ๋ฉฐ, ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ์์๋ก Prepared Statement๋ฅผ ์ ์ฉํด ๋ด ์๋ค.
PHP
<?php
// DB_Connect.php (์ด์ ํฌ์คํ
์์ ๋ง๋ DB ์ฐ๊ฒฐ ์ฝ๋๋ฅผ ํฌํจํ๋ค๊ณ ๊ฐ์ )
require 'DB_Connect.php'; // $pdo ๊ฐ์ฒด๊ฐ ์ฌ๊ธฐ์ ์ ์๋์ด ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($username) || empty($password)) {
echo "์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ชจ๋ ์
๋ ฅํด์ฃผ์ธ์.". "<br>";
} else {
try {
// 1. ์ฟผ๋ฆฌ ์ค๋น (ํ๋ ์ด์คํ๋ ์ฌ์ฉ)
// SQL ์ธ์ ์
๋ฐฉ์ง๋ฅผ ์ํด ์ฌ์ฉ์ ์
๋ ฅ์ ์ง์ ์ฟผ๋ฆฌ์ ๋ฃ์ง ์์ต๋๋ค.
$sql = "SELECT id, username FROM users WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);
// 2. ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ฐ ์คํ
// execute() ๋ฉ์๋์ ๋ฐฐ์ด๋ก ๋งค๊ฐ๋ณ์ ์ ๋ฌ
$stmt->execute([
':username' => $username,
':password' => $password // ์ค์ ์ฑ์์๋ ๋น๋ฐ๋ฒํธ๋ฅผ ํด์ฑํด์ ๋น๊ตํด์ผ ํจ!
]);
// 3. ๊ฒฐ๊ณผ ๊ฐ์ ธ์ค๊ธฐ
$user = $stmt->fetch();
if ($user) {
echo "๋ก๊ทธ์ธ ์ฑ๊ณต! ํ์ํฉ๋๋ค, " . htmlspecialchars($user['username']) . "!". "<br>";
// ์ธ์
์์ ๋ฐ ์ฌ์ฉ์ ์ ๋ณด ์ ์ฅ ๋ฑ์ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
} else {
echo "๋ก๊ทธ์ธ ์คํจ: ์ฌ์ฉ์ ์ด๋ฆ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค.". "<br>";
}
} catch (PDOException $e) {
echo "๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค๋ฅ: " . $e->getMessage(). "<br>";
// ์ค์ ์๋น์ค์์๋ ์ค๋ฅ๋ฅผ ๋ก๊น
ํ๊ณ ์ฌ์ฉ์์๊ฒ๋ ์ผ๋ฐ์ ์ธ ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ค๋๋ค.
}
}
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>๋ก๊ทธ์ธ ์์ </title>
</head>
<body>
<h2>๋ก๊ทธ์ธ</h2>
<form method="POST" action="">
<label for="username">์ฌ์ฉ์ ์ด๋ฆ:</label><br>
<input type="text" id="username" name="username" required><br><br>
<label for="password">๋น๋ฐ๋ฒํธ:</label><br>
<input type="password" id="password" name="password" required><br><br>
<input type="submit" value="๋ก๊ทธ์ธ">
</form>
</body>
</html>
์ฐธ๊ณ : ์ ์์ ๋ ๋ณด์ ๊ฐํ๋ฅผ ์ํด ์ค์ ๋น๋ฐ๋ฒํธ๋ฅผ ํด์ฑํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ณ , ๋ก๊ทธ์ธ ์์๋ ์
๋ ฅ๋ ๋น๋ฐ๋ฒํธ๋ฅผ ํด์ฑํ์ฌ ๋น๊ตํ๋ ๊ณผ์ ์ด ์ถ๊ฐ๋์ด์ผ ํฉ๋๋ค. #password_hash()
์ #password_verify()
ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ํ์ค์
๋๋ค.
๊ฒฐ๋ก ๋ฐ ๋ค์ ์ฃผ์ฐจ ์๊ณ
์ด๋ฒ ํฌ์คํ ์์๋ #SQL #์ธ์ ์ ์ ์ํ์ฑ์ ๋ช ํํ ์ดํดํ๊ณ , ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ํ์์ ์ธ ๊ธฐ์ ์ธ #Prepared #Statement์ ์๋ฆฌ์ #PDO์์์ ์ฌ์ฉ๋ฒ์ ์ตํ์ต๋๋ค. #์ฟผ๋ฆฌ #๊ตฌ์กฐ์ #๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ฒ๋ฆฌํจ์ผ๋ก์จ, ์ฌ์ฉ์ ์ ๋ ฅ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ช ๋ น์ด๋ก ์ค์๋ํ๋ ๊ฒ์ ์์ฒ์ ์ผ๋ก ์ฐจ๋จํ ์ ์์์ ๋ฐฐ์ ์ต๋๋ค.
Prepared Statement๋ PDO๋ฅผ ์ฌ์ฉํ๋ ๊ฐ์ฅ ํฐ ์ด์ ์ค ํ๋์ด๋ฉฐ, ์ฌ๋ฌ๋ถ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ #๋ณด์ ์ํ์ผ๋ก๋ถํฐ ์งํค๋ ์ฒซ๊ฑธ์์ ๋๋ค. ํญ์ ์ฌ์ฉ์ ์ ๋ ฅ์ ๋ฐ๋ ๋ชจ๋ SQL ์ฟผ๋ฆฌ์ Prepared Statement๋ฅผ ์ ์ฉํ๋ ์ต๊ด์ ๋ค์ด์ธ์.