SQL Injection์ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฐ์ด SQL ์ฟผ๋ฆฌ์ ๊ทธ๋๋ก ํฌํจ๋์ด, ์ํ์ง ์๋ SQL ๋ช ๋ น์ด ์คํ๋๊ฒ ๋ง๋๋ ๊ณต๊ฒฉ ๊ธฐ๋ฒ์ ๋๋ค. ์ด๋ ์ ๋ ฅ๊ฐ์ด ์ ๋๋ก ๊ฒ์ฆ๋์ง ์์ ๋ ๋ฐ์ํ๋ฉฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณด์์ ํฐ ์ํ์ด ๋ฉ๋๋ค.
์ธ์ฆ ์ฐํ๋ SQL Injection์ ์ด์ฉํด ๋ก๊ทธ์ธ ์ ์ฐจ๋ฅผ ์ฐํํ๋ ๊ณต๊ฒฉ์ ๋๋ค. ๋ณด์์ด ์ทจ์ฝํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์์ฃผ ๋ฐ์ํ๋ฉฐ, ์ฌ์ฉ์ ์ธ์ฆ ๊ณผ์ ์ ์ทจ์ฝ์ ์ ์ ์ฉํฉ๋๋ค.
์ ์์ ์ธ ๋ก๊ทธ์ธ ๊ณผ์
SELECT * FROM USER WHERE ID = "abc" AND PASSWORD = "1234";
ID
๊ฐ "abc"์ด๊ณ PASSWORD
๊ฐ "1234"์ธ ์ฌ์ฉ์๋ฅผ ๊ฒ์ํ์ฌ, ์ผ์นํ๋ ํ์ด ์์ผ๋ฉด ๋ก๊ทธ์ธ์ ํ์ฉํฉ๋๋ค. SQL Injection์ ํตํ ๊ณต๊ฒฉ
1234; DELETE FROM USER WHERE ID = "1";
SELECT * FROM USER WHERE ID = "abc" AND PASSWORD = "1234"; DELETE FROM USER WHERE ID = "1";
DELETE
๋ช
๋ น์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ค์ ๋ก ์ํฅ์ ๋ฏธ์ณ ๋ฐ์ดํฐ ์์ค์ด๋ ์์คํ
๋ง๋น ๊ฐ์ ์ฌ๊ฐํ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ ์ ์์ต๋๋ค. OR ์ ์ ์ด์ฉํ ๋ฌด์กฐ๊ฑด ์ฐธ(True) ๋ง๋ค๊ธฐ
' OR '1' = '1
SELECT * FROM USER WHERE ID = "abc" AND PASSWORD = '' OR '1' = '1';
WHERE
์ ์ ํฌํจ๋ OR '1' = '1'
์กฐ๊ฑด์ด ํญ์ ์ฐธ์ด ๋์ด ๋น๋ฐ๋ฒํธ๊ฐ ํ๋ ค๋ ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ ์ ์์ต๋๋ค. ๋ฐ์ดํฐ ๋ ธ์ถ์ ์์คํ ์์ ๋ฐ์ํ๋ ์๋ฌ ๋ฉ์์ง๋ฅผ ํตํด ๊ณต๊ฒฉ์๊ฐ ๋ด๋ถ ์ ๋ณด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์กฐ๋ฅผ ํ์ ํ๋ ์ทจ์ฝ์ ์ ๋๋ค. ์ฃผ๋ก ์์ธํ ์๋ฌ ๋ฉ์์ง๋ฅผ ํ์ํ๋ ์์คํ ์์ ๋ฐ์ํ๋ฉฐ, ๊ณต๊ฒฉ์๋ ์ด๋ฅผ ์ด์ฉํด ํดํน์ ํ์ํ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ต๋๋ค.
์๋ฌ ๋ฉ์์ง์ ์ญํ
๊ณต๊ฒฉ์์ ์ ๊ทผ ๋ฐฉ๋ฒ
์์: URL ์ฟผ๋ฆฌ ์คํธ๋ง์ ํตํ ๊ณต๊ฒฉ
http://example.com/products?id=10
http://example.com/products?id=10' OR '1'='1
SQL Error: syntax error near 'OR' in query
์๋ฌ ๊ธฐ๋ฐ SQL Injection
Table 'database.users' doesn't exist
์ ๊ฐ์ ์ ๋ณด๊ฐ ํ์๋๋ฉด, ๊ณต๊ฒฉ์๋ ํ
์ด๋ธ ์ด๋ฆ์ด users
์์ ์ ์ ์์ต๋๋ค. ํน์๋ฌธ์ ๊ฒ์ฌ๋ ๋ก๊ทธ์ธ์ด๋ ๋ฐ์ดํฐ ์ ๋ ฅ ์, ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฐ์ ์ ์์ ์ธ ํน์๋ฌธ์๊ฐ ํฌํจ๋์ด ์๋์ง ๊ฒ์ฆํ์ฌ SQL Injection, XSS ๋ฑ ๋ณด์ ๊ณต๊ฒฉ์ ์ฌ์ ์ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
'
, ;
, --
, <
, >
, "
๋ฑ์ ํน์๋ฌธ์๋ SQL ๊ตฌ๋ฌธ์ด๋ HTML์์ ์๋์น ์์ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ ์ ์์ต๋๋ค. ๊ฒ์ฆ ๋ก์ง ์ถ๊ฐ
ํน์๋ฌธ์ ๊ฒ์ถ ๋ฐ ์ฐจ๋จ
๋ฏธ๋ฆฌ ์ค์ ํ ํน์๋ฌธ์ ๋ฆฌ์คํธ๋ก ์ ๋ ฅ๋ ๊ฐ์ ํน์ ๋ฌธ์๊ฐ ํฌํจ๋์ด ์๋์ง ๊ฒ์ฌํฉ๋๋ค.
์ฐจ๋จํ ํน์๋ฌธ์ ์:
'
, "
, ;
, --
, #
<
, >
, &
, "
์ ๊ท์ ๊ฒ์ฌ ์์:
import re
# ์
๋ ฅ๊ฐ์์ ํน์๋ฌธ์ ๊ฒ์ฌ
def check_special_characters(input_value):
# ํน์๋ฌธ์ ํจํด ์ค์
pattern = re.compile(r'[\'\";--<>]')
# ํน์๋ฌธ์ ํฌํจ ์ฌ๋ถ ํ์ธ
if pattern.search(input_value):
return False # ํน์๋ฌธ์ ํฌํจ ์ ์ฐจ๋จ
return True # ํน์๋ฌธ์ ๋ฏธํฌํจ ์ ํต๊ณผ
# ์์
user_input = "admin' OR '1'='1"
if not check_special_characters(user_input):
print("ํน์๋ฌธ์๊ฐ ํฌํจ๋์ด ์์ฒญ์ด ์ฐจ๋จ๋์์ต๋๋ค.")
else:
print("์ ์์ ์ธ ์
๋ ฅ๊ฐ์
๋๋ค.")
์ด ์ฝ๋๋ '
, "
, ;
, --
, <
, >
๋ฑ์ ํน์๋ฌธ์๋ฅผ ๊ฒ์ฌํ์ฌ ํฌํจ๋ ๊ฒฝ์ฐ ์์ฒญ์ ์ฐจ๋จํฉ๋๋ค.
์ ๋ ฅ๊ฐ ํํฐ๋ง ๋ฐ ์ด์ค์ผ์ดํ(Escaping)
'
โ \'
, <
โ <
๋ก ๋ณ๊ฒฝํ์ฌ ์ฝ๋ ์คํ์ ๋ง์ต๋๋ค. ํ์ดํธ๋ฆฌ์คํธ ์ ๊ทผ
_
, -
, @
๋ฑ)๋ง ํ์ฉํ๋๋ก ์ค์ ํฉ๋๋ค. SQL ์๋ฒ์์ ์๋ฌ ๋ฉ์์ง๋ฅผ ๊ฐ์ถ๋ ๊ฒ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ณด์์ ์ํ ์ค์ํ ๋ฐฉ๋ฒ์ ๋๋ค. ์ผ๋ฐ ์ฌ์ฉ์๊ฐ ๋ด๋ถ ๊ตฌ์กฐ๋ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ์์ง ๋ชปํ๊ฒ ํ๋ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ ์ค ํ๋๊ฐ VIEW(๋ทฐ)๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค.
์๋ณธ ํ ์ด๋ธ์ ๋ํ ์ง์ ์ ๊ทผ ์ฐจ๋จ
VIEW๋ฅผ ํตํด ๋ฐ์ดํฐ ์ ๊ณต
USER_DETAILS
ํ
์ด๋ธ์ ํ์ฑ ์ฌ์ฉ์ ์ ๋ณด๋ง ์ ๊ณตํ๋ VIEW ์์ฑ: CREATE VIEW USER_VIEW AS
SELECT USERNAME, EMAIL
FROM USER_DETAILS
WHERE ACTIVE = 1;
VIEW์์์ ์๋ฌ ์ฒ๋ฆฌ
์ ๊ทผ ๊ถํ ์ค์
์ผ๋ฐ ์ฌ์ฉ์๋ VIEW์ ๋ํ SELECT
๊ถํ๋ง ๊ฐ๊ณ , ์๋ณธ ํ
์ด๋ธ์๋ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ์ค์ ํฉ๋๋ค.
์์ ๊ถํ ์ค์ :
-- ์ฌ์ฉ์์๊ฒ ๋ทฐ์ ๋ํ SELECT ๊ถํ ๋ถ์ฌ
GRANT SELECT ON USER_VIEW TO ์ผ๋ฐ์ฌ์ฉ์;
-- ์ฌ์ฉ์์๊ฒ ์๋ณธ ํ
์ด๋ธ์ ๋ํ ์ ๊ทผ ์ฐจ๋จ
REVOKE ALL ON USER_DETAILS FROM ์ผ๋ฐ์ฌ์ฉ์;
PreparedStatement๋ SQL Injection ๊ณต๊ฒฉ์ ๋ง๊ธฐ ์ํ ์ค์ํ ๋ณด์ ๊ธฐ๋ฒ์ผ๋ก, ์ฟผ๋ฆฌ์์ ์ฌ์ฉ์ ์ ๋ ฅ์ ์์ ํ๊ฒ ์ฒ๋ฆฌํ๊ณ ํน์๋ฌธ์๋ฅผ ์๋์ผ๋ก ์ด์ค์ผ์ดํ(escaping)ํด์ฃผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์์ ํ ์ฟผ๋ฆฌ ์ ๋ฌ์ด ๊ฐ๋ฅํด์ง๋๋ค.
?
)๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ SQL ๋ช
๋ น์
๋๋ค. ?
๋ก ํ์ํ๊ณ , ์คํ ์ ์ค์ ๊ฐ์ด ์์ ํ๊ฒ ๋ฐ์ธ๋ฉ๋ฉ๋๋ค. ํน์๋ฌธ์ ์๋ ์ด์ค์ผ์ดํ
์ฟผ๋ฆฌ์ ๋ฐ์ดํฐ์ ๋ถ๋ฆฌ
์ทจ์ฝํ Statement ์ฌ์ฉ ์ (์ํ)
// Statement๋ฅผ ์ด์ฉํ ์ฟผ๋ฆฌ (์ทจ์ฝ)
String userInput = "abc' OR '1'='1";
String query = "SELECT * FROM USER WHERE USERNAME = '" + userInput + "';";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
' OR '1'='1
๊ณผ ๊ฐ์ ๊ณต๊ฒฉ ๊ตฌ๋ฌธ์ ํตํด ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ์ ์๊ฒ ๋ฉ๋๋ค.์์ ํ PreparedStatement ์ฌ์ฉ ์
?
๋ก ์ ๋ฌ์ธ์๋ฅผ ๋ฐ์ ์ฟผ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ์ปดํ์ผํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ๋ฐ์ธ๋ฉํฉ๋๋ค. // PreparedStatement๋ฅผ ์ด์ฉํ ์ฟผ๋ฆฌ (์์ )
String userInput = "abc' OR '1'='1"; // ๊ณต๊ฒฉ์ ์ธ ์
๋ ฅ๊ฐ
String query = "SELECT * FROM USER WHERE USERNAME = ?;";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, userInput); // ์
๋ ฅ ๊ฐ์ ์์ ํ๊ฒ ๋ฐ์ธ๋ฉ
ResultSet rs = pstmt.executeQuery();
pstmt.setString(1, userInput);
์ ์
๋ ฅ๊ฐ์ ์ฒซ ๋ฒ์งธ ?
์๋ฆฌ์ ๋ฐ์ธ๋ฉํ๊ณ , ํน์๋ฌธ์๋ ์์ ํ๊ฒ ์ฒ๋ฆฌํ์ฌ SQL ๊ตฌ๋ฌธ ๋ณ์กฐ๋ฅผ ๋ฐฉ์งํฉ๋๋ค. ๊ทธ๋๋ก SQL๋ฌธ์ด statement์ ๊ฐ์ด ์ด์ด์ง๋๊ฑฐ ๊ฐ์ง๋ง ์ค์ ๋ก๋ ํน์๋ฌธ์๊ฐ ์ด์ค์ผ์ดํ ์ฒ๋ฆฌ๋์ด ์ ์ฒด๊ฐ ๋ฌธ์์ด ์ทจ๊ธ์ด ๋๊ธฐ ๋๋ฌธ์ ์ํ๋๋๋ก ์๋๋์ง ์๋๋ค.(๋ฌธ์์ด ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ์ํ๋ ๊ฐ์ ์ถ๋ ฅ์ ์๋์ง๋ง ๊ณต๊ฒฉ์ ๋ฌด๋ ฅํ์ํฌ ์ ์๋ค.)?
์ ์์น์ ๊ฐ์๋ฅผ ์ ํํ ๋ง์ถฐ์ผ ํฉ๋๋ค.