[Security] Secure Coding(6-2) - SQL Injection
๐ ์ํ์ด ์ฝ๋ฉ ์์ ์ ๋ฆฌ
SQL Injection
๐SQL Injection: ์น์ฑ์์ ์ ๋ ฅ ๋ฐ์ DB๋ก ์ ๋ฌํ๋ ์ ์์ ์ธ ์ฟผ๋ฆฌ(CRUD)๋ฅผ ๋ณ์กฐ, ์ฝ์ ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ถ๋ฒ์ ์ธ ๋ฐ์ดํฐ ์ด๋, ์์คํ ๋ช ๋ น ์ํ ๋ฑ ๋น์ ์์ ์ธ ๋ฐ์ดํฐ ๋ฒ ์ด์ค์ ์ ๊ทผํ๋ ๊ธฐ๋ฒ
- ๋ชจ๋ ์ข ๋ฅ์ DBMS(Database Management System)์ ์ ์ฉ ๊ฐ๋ฅํ ๊ณต๊ฒฉ ๊ธฐ๋ฒ์ด๋ฉฐ, ์ง์์ ์ผ๋ก ๋ฐ์ ๋๊ณ ์์
โํผํด๋ด์ฉ:
- ์ ์ฑ ์คํฌ๋ฆฝํธ ์คํ, ์ธ๋ถ ํ๋ก๊ทธ๋จ ์ฌ์ฉ๊ฐ๋ฅ, DB ์ ๋ณด ์ด๋,์ถ๊ฐ,์์ ,์ญ์ ๊ฐ๋ฅ
- ํ๋ก์์ ๋ฅผ ํตํ ์ด์์ฒด์ ๋ช ๋ น์ด ์ํ, ๋ถ๋ฒ ๋ก๊ทธ์ธ ๋ฑ์ ์นจํด ์ฌ๊ณ ๊ฐ ๋ฐ์ ๊ฐ๋ฅ
SQL Injection ๋ฐ์ ์์ธ
- ๋์ ์ฟผ๋ฆฌ์์ ์ฌ์ฉ์ ์
๋ ฅ ๊ฒ์ฆ ๋ถ์กฑ
- ํผ, URL ํ๋ผ๋ฏธํฐ, API ์์ฒญ ๋ฑ ๋ชจ๋ ์ธ๋ถ ์ ๋ ฅ์ ๋ํด ๊ฒ์ฆ ๋ก์ง์ด ์๋ ๊ฒฝ์ฐ ๋ฐ์
- ์
๋ ฅ ๊ฐ ๋ด ํน์๋ฌธ์(
'
,"
,;
๋ฑ)๋ฅผ ์ฒ๋ฆฌํ์ง ์์ SQL ๊ตฌ๋ฌธ์ด ์๋์น ์๊ฒ ๋ณ๊ฒฝ๋จ
1
2
-- userInput์ admin' OR '1'='1 ์ ์ฝ์
ํ๋ฉด ํญ์ ์ฐธ์ด ๋์ด ์ธ์ฆ ์ฐํ๋จ
SELECT * FROM users WHERE username = '" + userInput + "';
- ์๋ฌ ๋ฉ์ธ์ง ๋ฐ ์ ๋ณด ๋
ธ์ถ
- ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ด๋ถ ์๋ฌ ๋ฉ์์ง ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๋ณด๋ฅผ ๋ ธ์ถํ ๊ฒฝ์ฐ, ๊ณต๊ฒฉ์๊ฐ ์ด๋ฅผ ๋ถ์ํ์ฌ ์ถ๊ฐ ๊ณต๊ฒฉ์ ์ค๊ณํ ์ ์์.
- ์๋ฌ ๋ฉ์์ง์ ํฌํจ๋ ๋ด์ฉ์ผ๋ก ๋ค์ํ ์ ๋ณด ํ๋ ๊ฐ๋ฅ
- โ SQL ๊ตฌ๋ฌธ ์ค๋ฅ, ํ ์ด๋ธ๋ช , ์ปฌ๋ผ ๋ช , DB ๋ฒ์ ๋ฑ
1
2
-- userInput์ 1' AND db_name() = 0-- ์ฝ์
ํ๋ฉด ์๋ฌ ๋ฉ์์ง์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ฆ์ด ๋๋ฌ๋จ.
SELECT * FROM users WHERE usernamne = '" + userInput + "';
- ๊ถํ ๋ฐ ๋ณด์ ์ค์ ๋ฏธํก
- ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉํ๋ DB ๊ณ์ ์ด ๋ถํ์ํ ๊ถํ(์:
DROP
,ALTER
,EXECUTE
๋ฑ)์ ๊ฐ์ง๊ณ ์์ ๊ฒฝ์ฐ, ๊ณต๊ฒฉ์๊ฐ ์ด๋ฅผ ์ ์ฉ ๊ฐ๋ฅ - ์ทจ์ฝํ ์ ์ฅ ํ๋ก์์ ๋ฅผ ํตํด ์์คํ ๋ช ๋ น์ด ์คํ์ด๋ ์ถ๊ฐ ๊ถํ ์์น ๊ณต๊ฒฉ ๊ฐ๋ฅ
- ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉํ๋ DB ๊ณ์ ์ด ๋ถํ์ํ ๊ถํ(์:
SQL Injection ๊ณต๊ฒฉ ๋ฐฉ์
1
2
3
4
<form action="login.jsp">
ID : <input type="text" name="id" />
Password : <input type="password" name="password" />
</form>
์์ html์์์ id์ password ๊ฐ์ด ์๋ฒ๋ก ๋ณด๋ด์ง๋ฉด ๋ค์ java์ sql์ ๋ฌธ์์ด๋ก ์ ์ฅ๋๋ค.
1
2
String sql = "select * from member where id = '" + request.getParameter("id") + "' " + "and password = '" + request.getParameter("password") + "' ";
statement.executeQuery(sql);
์ ๋ ฅ ์์
- ์ ์ ์์ฒญ:
login.jsp?id=gildong&password=gdhong
- ๊ณต๊ฒฉ ์ฝ๋:
login.jsp?id=a' or 'a'='a&password=a' or 'a'='a
login.jsp?id=admin' -- &password=x
login.jsp?id=admin' # &password=x
Error-based SQL Injection
๐Error-based SQL Injection: ์ถ๊ฐ ๊ณต๊ฒฉ์ ํ์ํ ์ ๋ณด๊ฐ ์ค๋ฅ ๋ฉ์์ง์ ํฌํจ๋์ด ๋ ธ์ถ๋ ์ ์๋๋ก ์ ๋ ฅ ๊ฐ์ ์กฐ์
๐ก๊ณต๊ฒฉ ์์:
'
: 1๊ฐ์ ๋ฐ์ดํ โ ์ธ์ ์ ๊ฐ๋ฅ ์ฌ๋ถ ๋ฐ ์๋ฌ ๋ฉ์์ง ๋ด์ฉ ํ์ธ์ด ๊ฐ๋ฅ' having 1 = 1 --
โ ์ค๋ฅ์์ ํ ์ด๋ธ๋ช , ์ปฌ๋ผ ๋ช ํ์ธ์ด ๊ฐ๋ฅ' and db_name() = 1 --
โ ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ด๋ฆ ํ์ธ์ด ๊ฐ๋ฅ- ์ค๋ฅ๋ฉ์ธ์ง:
Conversion failed when converting the nvarchar value 'database_xmoonanx' to data type int.
- ์ค๋ฅ๋ฉ์ธ์ง:
' and 1 = (select @@version) --
โ ํ์ฌ ์ค์น๋ SQL Server์ ์์คํ ๋ฐ ๋น๋ ์ ๋ณด ํ์ธ์ด ๊ฐ๋ฅ
Union-based SQL Injection
๐Union-based SQL Injection: UNION ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ ์๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์ ๊ณต๊ฒฉ์๊ฐ ์ํ๋ ์ ๋ณด๋ฅผ ํจ๊ป ์ถ๋ ฅ์ํค๋ ๊ธฐ๋ฒ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- ์ฒซ๋ฒ์งธ ์์ ๋ฐ์ดํ๋ ๋ฌด์
-- 1๋จ๊ณ: ์ปฌ๋ผ ์ ํ์ธ
' admin' union select 1,2,3,4,5,6 #
-- 2๋จ๊ณ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฒ์ ํ์ธ
' admin' union select version(),2,3,4,5,6
-- 3๋จ๊ณ: ์คํค๋ง ์ ๋ณด ์ถ์ถ
' admin' union select schema_name,2,3,4,5,6 from information_schema.schemata #
-- 4๋จ๊ณ: ํ
์ด๋ธ ๋ชฉ๋ก ํ์ธ
' admin' union select group_concat(table_name),2,3,4,5,6 from information_schema.tables where table_schema=database() #
-- 5๋จ๊ณ: ์ฌ์ฉ์ ๊ณ์ ์ ๋ณด ํ์ทจ
' admin' union select idx,userid,userpw,username,5,6 from board_member #
- ๊ณต๊ฒฉ์๋ค์ด DB ๋ด๋ถ๊ตฌ์กฐ๋ฅผ ํ์ฉํด์ ๊ณต๊ฒฉํ๋ ค๋ฉด DBMS ์์คํ ํ ์ด๋ธ์ ๊ตฌ์กฐ๋ฅผ ํ์ ํด์ผํ๋ค.
- DB๊ฐ ๋์ํ๋ ๋์ ์ฌ์ฉํ๋ ํ ์ด๋ธ์ด๋ฏ๋ก ๋ง๋ ๊ฒ๋ ์ด๋ ต๋ค.
- ๋ง์ ์ ํ๋ฆฌ์ผ์ด์ ๋ค์ด ์ด๋ฐ ์์คํ ํ ์ด๋ธ์ ํ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋ง๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์ํฉ์ด ์๋๋ผ๋ฉด ์ผ๋ฐ ์ฌ์ฉ์๋ค์ ๊ถํ์ ๋ง๋๊ฒ ์ข๋ค.
Blind SQL Injection
๐Blind SQL Injection: ์ง์ ์ ์ธ ๊ฒฐ๊ณผ ์ถ๋ ฅ ์์ด ์๋ฒ์ ๋ฐ์ ์ฐจ์ด๋ฅผ ์ด์ฉํ์ฌ ์ ๋ณด๋ฅผ ํ ๊ธ์์ฉ ์ถ์ถํ๋ ๊ธฐ๋ฒ
- ์ฐธ์ธ ์กฐ๊ฑด(์ฟผ๋ฆฌ ์คํ ๊ฒฐ๊ณผ๊ฐ ์กด์ฌ)๊ณผ ๊ฑฐ์ง์ธ ์กฐ๊ฑด(์ฟผ๋ฆฌ ์คํ ๊ฒฐ๊ณผ๊ฐ ์์)์ ๋ฒ๊ฐ์ ๊ฐ๋ฉด์ ์ ๋ ฅ๋๋๋ก ์ ๋ ฅ ๊ฐ์ ์กฐ์
Content-based
๋ฐฉ์๊ณผ Time-based
๋ฐฉ์์ด ์์
- Content-based: ์กฐ๊ฑด๋ฌธ์ ์ฐธ/๊ฑฐ์ง์ ๋ฐ๋ฅธ ํ์ด์ง ๋ด์ฉ ์ฐจ์ด๋ฅผ ์ด์ฉ
1
2
3
4
5
6
-- ์ฒซ๋ฒ์งธ ์์ ๋ฐ์ดํ๋ ๋ฌด์
-- a~z๊น์ง ์ฒซ ๋ฒ์งธ ๊ธ์๊ฐ 'a'์ธ์ง ํ์ธ
' Data' and substring(user_name(1),1,1)='a' --
-- ๋ ๋ฒ์งธ ๊ธ์ ํ์ธ
' Data' and substring(user_name(1),2,1)='d' --
- Time-based: ์กฐ๊ฑด๋ฌธ์ ๋ฐ๋ฅธ ์๋ต ์๊ฐ ์ฐจ์ด๋ฅผ ์ด์ฉ
1
2
3
-- ์กฐ๊ฑด์ด ์ฐธ์ด๋ฉด 5์ด ์ง์ฐ
' Data' and substring(user_name(1),1,1)='a' waitfor delay '0:0:5'
-- ์ง์์ ์ผ๋ก ๋ฐ๋ณต. ๊ณต๊ฒฉ์๊ฐ์ด ๋ง์ด ๊ฑธ๋ฆฌ๋ฏ๋ก ํด์ ์ํด ์๋ํ ๊ณต๊ฒฉ์ ์๋ํ๋ค.
SQL Injection ์ง๋จ ๋ฐฉ๋ฒ
์ฝ๋ ๋ฆฌ๋ทฐ
๐์ฝ๋ ๋ฆฌ๋ทฐ: ๊ฐ๋ฐ์๊ฐ ์์ฑํ ์์ค ์ฝ๋๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ๊ฒํ ํ์ฌ ๋ฒ๊ทธ ๋ฐ ๋ณด์ ์ทจ์ฝ์ ์ ์ฐพ์๋ด๋ ํ๋ก์ธ์ค
- SQL ์ธ์ ์ ๊ณผ ๊ฐ์ ์ทจ์ฝ์ ์ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ ์ ์์
๐กSQL ์ธ์ ์ ์ทจ์ฝ์ ์ ์ฃผ์ ์์ธ
- ๋์ SQL ์ฌ์ฉ: ์ฌ์ฉ์ ์ ๋ ฅ ๊ฐ์ ๋ฌธ์์ด๋ก ์ง์ ์ฟผ๋ฆฌ์ ์ฝ์
- ์ ๋ ฅ ๊ฐ ๊ฒ์ฆ ๋ฏธํก: ์ฌ์ฉ์ ์ ๋ ฅ์ ๋ํ ํํฐ๋ง ๋๋ ์ด์ค์ผ์ดํ์ด ๋ถ์กฑ
- ์๋ชป๋ ๊ถํ ์ค์ : ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ ๊ถํ์ด ๊ณผ๋ํ๊ฒ ๋ถ์ฌ๋จ
โ ์ฝ๋ ๋ฆฌ๋ทฐ ์ค์์ฑ:
- ๋ณด์ ์ทจ์ฝ์ ์ด ์ ํ ์ถ์ ์ ๋จ๊ณ์์ ์ฌ์ ์ ์ ๊ฑฐ๋จ
- ํ ๋ด ๋ณด์ ์ธ์ ์ ๊ณ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก ๊ณต์
- ์๋ํ ๋๊ตฌ์ ์๋ ๋ฆฌ๋ทฐ๋ฅผ ๋ณํํ์ฌ ์ทจ์ฝ์ ํ์ง์จ ํฅ์
์ฝ๋ ๋ฆฌ๋ทฐ ๋ฐฉ๋ฒ
- ๋์ SQL ๊ตฌ๋ฌธ ์ฌ์ฉ ์ฌ๋ถ ์ ๊ฒ
- ์ฌ์ฉ์ ์ ๋ ฅ ๊ฐ์ ์ง์ ์ฟผ๋ฆฌ ๋ฌธ์์ด์ ๊ฒฐํฉํ๋ ๊ฒฝ์ฐ
1
2
3
4
5
6
7
8
9
public User findUserByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
try {
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class));
}
catch (EmptyResultDataAccessException e) {
return null;
}
}
- ์
๋ ฅ ๊ฐ ๊ฒ์ฆ ๋ฐ ์ด์ค์ผ์ดํ ์ ๊ฒ
- ์ฌ์ฉ์ ์ ๋ ฅ ๊ฐ์ ๋ํด ํ์ดํธ๋ฆฌ์คํธ ๊ธฐ๋ฐ์ ์ ํจ์ฑ ๊ฒ์ฆ ์ํ
- ํน์๋ฌธ์๋ ์ํ ๋ฌธ์์ ๋ํ ์ด์ค์ผ์ดํ ์ ์ฉ ์ฌ๋ถ ํ์ธ
์๋ํ๋ ์ง๋จ ๋๊ตฌ ํ์ฉ
์ ์ ๋ถ์ ๋๊ตฌ
- ์์ค ์ฝ๋๋ฅผ ๋ถ์ํ์ฌ ๋ณด์ ์ทจ์ฝ์ (์: ๋์ SQL, ์
๋ ฅ๊ฐ ์ฒ๋ฆฌ ๋ฏธํก ๋ฑ)์ ์๋์ผ๋ก ํ์ง
- ์:
SonarQube
,Checkmarx
,Fortify
- ์:
๋์ ๋ถ์ ๋๊ตฌ
- ์คํ ์ค์ธ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ํด ์ค์ ๊ณต๊ฒฉ ์๋๋ฆฌ์ค(์: OWASP ZAP)๋ฅผ ์ ์ฉํด ์ทจ์ฝ์ ๊ฒ์ฆ
- ์:
OWASP ZAP
- ์:
- ์๋ํ ๋๊ตฌ์ ํ์์ฑ
- ๊ด๋ฒ์ํ ์ฝ๋ ๊ฒ์ฆ: ์๋ ๋ฆฌ๋ทฐ๋ง์ผ๋ก๋ ๋์น๊ธฐ ์ฌ์ด ์ทจ์ฝ์ ์ ์ฒด๊ณ์ ์ผ๋ก ํ์ง
- ๋ฐ๋ณต์ ๊ฒ์ฌ: CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉํ์ฌ ์ฝ๋ ๋ณ๊ฒฝ ์๋ง๋ค ์๋ ์ ๊ฒ
- SQL ์ธ์ ์
์ทจ์ฝ์ ํ์ง ์ญํ
- ์ฌ์ฉ์ ์ ๋ ฅ๊ณผ ๋์ SQL ๊ตฌ๋ฌธ ์กฐํฉ์ ์๋์ผ๋ก ์ค์บํ์ฌ ์ํ ํจํด์ ์๋ณ
- ์ทจ์ฝํ ์ฝ๋ ํจํด(์: ๋ฌธ์์ด ๊ฒฐํฉ์ผ๋ก SQL ์์ฑ)์ ๋ณด๊ณ ์๋ก ์ ๊ณต
์ํ์ด ์ฝ๋ฉ ์ ์ฉ
ํ๋ ์์ํฌ๋ณ SQL Injection ๋ฐฉ์ด ๋ฐฉ๋ฒ
JDBC Statement ๋ฐฉ์
PreparedStatement
์ setXXX()๋ฉ์๋๋ก ์ธ๋ถ ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ฟผ๋ฆฌ๋ฌธ์ ์ค์ ํ์ฌ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๊ฐ ๋ณ์กฐ๋๋ ๊ฒ์ ๋ฐฉ์ง- ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์ผ๋ก SQL๊ณผ ๋ฐ์ดํฐ ๋ถ๋ฆฌ
1
2
3
4
5
6
// โ์์ ํ์ง ์์ ์ฝ๋
String query = "SELECT * FROM users WHERE userid ='"+ userid + "'" +
"AND password='" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
1
2
3
4
5
6
// โ
์์ ํ ์ฝ๋
String query = "SELECT * FROM users WHERE userid=? AND password=?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, userid);
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();
Hibernate Query Language (HQL)
- ๊ฐ์ฒด ์งํฅ ์ฟผ๋ฆฌ ์ธ์ด๋ก SQL ์ง์ ์กฐ์ ๋ฐฉ์ง
- ์์น ๊ธฐ๋ฐ(
?0
) ๋ฐ ์ด๋ฆ ๊ธฐ๋ฐ(:param
) ํ๋ผ๋ฏธํฐ
1
2
3
// โ์์ ํ์ง ์์ ์ฝ๋
List results = session.createQuery(
"from Orders as orders where orders.id = " + currentOrder.getId()).list();
1
2
3
// โ
์์ ํ ์ฝ๋
Query hqlQuery = session.createQuery("from Orders as orders where orders.id = ?0");
List results = hqlQuery.setString(0, "123-456-7890").list();
MyBatis ๋ฐฉ์
#{}
๊ตฌ๋ฌธ์ผ๋ก ์๋ PreparedStatement ์์ฑ- ๋์ SQL์์๋ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ ์ ์ง
1
2
3
4
<!-- โ์์ ํ์ง ์์ ์ฝ๋ -->
<select id="getPerson" parameterType="String" resultType="org.application.vo.Person">
SELECT * FROM PERSON WHERE NAME LIKE '${name}'
</select>
1
2
3
4
<!-- โ
์์ ํ ์ฝ๋ -->
<select id="getPerson" parameterType="String" resultType="org.application.vo.Person">
SELECT * FROM PERSON WHERE NAME = #{name}
</select>
Java Persistence API(JPA) ๋ฐฉ์
- JPQL(Java Persistence Query Language) ์ฌ์ฉ
- ์ํฐํฐ ๊ธฐ๋ฐ ์ฟผ๋ฆฌ๋ก SQL ์ง์ ์กฐ์ ๋ฐฉ์ง
1
2
// โ์์ ํ์ง ์์ ์ฝ๋
List results = entityManager.createQuery("Select order from Orders order where order.id = " + orderId).getResultList();
1
2
3
// โ
์์ ํ ์ฝ๋
Query jpqlQuery = entityManager.createQuery("Select order from Orders order where order.id= ?1");
List results = jpqlQuery.setParameter(1, "123-456-7890").getResultList();
์ ๋ ฅ ๊ฐ ํํฐ๋ง
ํน์ ๋ฌธ์ ์ธ์ฝ๋ฉ์ ํตํด ์ํ ๋ฌธ์๋ฅผ ์์ ํ ํํ๋ก ๋ณํ
์ ๋ ฅ ๊ฐ ๊ฒ์ฆ
SQLMap์ ํ์ฉํ ๊ฒ์ฆ ํ ์คํธ ๐SQLMap: SQL ์ธ์ ์ ์ทจ์ฝ์ ์ ์๋์ผ๋ก ํ์งํ๊ณ ์ ์ฉํ๋ ์คํ์์ค ์นจํฌ ํ ์คํธ ๋๊ตฌ
SQLMap์ ํ์ฉํ์ฌ, ๊ตฌํ๋ ์ ๋ ฅ๊ฐ ๊ฒ์ฆ์ด ์ ๋๋ก ์๋ํ๋์ง ํ ์คํธ
๊ฒ์ฆ ํ ์คํธ ์์
1
sqlmap -u "http://example.com/search?keyword=test" --risk=3 --level=5 --batch
-u
: ํ๊ฒ URL ์ง์ (GET ํ๋ผ๋ฏธํฐ ํฌํจ)--batch
: ๋ชจ๋ ์ง๋ฌธ์ ์๋์ผ๋ก ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ--risk [1-3]
: ์ํ ์์ค ์ค์ (๋์์๋ก ๋ค์ํ ํ์ด๋ก๋ ์๋)--level [1-5]
: ํ ์คํธ ๊ฐ๋ ์กฐ์ (๋์์๋ก ๊ฒ์ฌํ๋ ํ๋ผ๋ฏธํฐ์ ํ์ด๋ก๋๊ฐ ๋ง์์ง)--dump
: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์ฉ์ ๋คํ (ํ ์คํธ ๋ชฉ์ ์์๋ง ์ฌ์ฉ, ์ด์ ํ๊ฒฝ์์๋ ์ฃผ์ํด์ผ ํจ)
โ ํ ์คํธ ๊ฒฐ๊ณผ ํ๋จ:
"Parameter: keyword (GET) appears to be 'safe'"
์ถ๋ ฅ ์ ์ฑ๊ณต์ ์ผ๋ก ๋ฐฉ์ด๋์์- SQL์ธ์ ์ ๊ด๋ จ ์ทจ์ฝ์ ๊ฒฝ๊ณ ๊ฐ ์ถ๋ ฅ๋๋ฉด, ์ ๋ ฅ ๊ฐ ๊ฒ์ฆ ๋ก์ง์ ๋ณด์ํด์ผ ํจ
๋ณด์ ๋ก๊ทธ ๊ธฐ๋ก ๋ฐ ๋ชจ๋ํฐ๋ง
ํ์์ฑ
- ๐จ์กฐ๊ธฐ ํ์ง
- SQL ์ธ์ ์ ๊ณต๊ฒฉ์ ์ค์ฌ์ผ๋ก ๋น์ ์์ ์ธ ์ฟผ๋ฆฌ ํจํด์ด๋ ์ค๋ฅ ๋ก๊ทธ๋ฅผ ํ์งํ์ฌ ์ค์๊ฐ์ผ๋ก ๊ณต๊ฒฉ ์งํ๋ฅผ ๋น ๋ฅด๊ฒ ํ์
- ๐์ฌํ ๋ถ์
- ๊ณต๊ฒฉ ๋ฐ์ ์ ๋ก๊ทธ๋ฅผ ํตํด ์์ธ ๋ถ์ ๋ฐ ๋์ ์ ๋ต ์๋ฆฝ
- ๋ฒ์ ๋์ ๋ฐ ๋ณด์ ๊ฐ์ฌ ์๋ฃ๋ก ํ์ฉ (์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ: ์ซ์, ๋ ์ง, ์๋ณ์ ๋ฑ ์ฟผ๋ฆฌ์์ ์ฌ์ฉ๋๋ ๋ชจ๋ ๊ฐ)
๋ก๊ทธ ๊ธฐ๋ก ๋์
- ๐ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ฒญ ๋ก๊ทธ: ์ ๋ ฅ ๊ฐ, URL, IP ๋ฑ
- ๐๏ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฌ ๋ก๊ทธ: SQL ๋ฌธ๋ฒ ์ค๋ฅ, ํ์ ๋ณํ ์ค๋ฅ ๋ฑ
- ๐ ์ธ์ฆ/์ธ๊ฐ ๊ด๋ จ ์ด๋ฒคํธ: ๋ก๊ทธ์ธ ์คํจ, ๊ถํ ์์น ์๋ ๋ฑ
โ ๏ธ ๋ก๊ทธ ๊ธฐ๋ก์ ๊ณ ๋ ค์ฌํญ
- ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ๋ฐ ์
๋ ฅ๊ฐ
- ์ฌ์ฉ์ ์ ๋ ฅ๊ฐ, ํนํ ํน์๋ฌธ์๋ SQL ์์ฝ์ด ํฌํจ ์ฌ๋ถ ๊ธฐ๋ก
- ์๋ฌ ๋ฐ ์์ธ ์ฒ๋ฆฌ ๋ก๊ทธ
- SQL ์คํ ์ค๋ฅ, ๊ตฌ๋ฌธ ์ค๋ฅ, ํ์ ๋ณํ ์ค๋ฅ ๋ฑ ์์ธ ์๋ฌ ๋ฉ์์ง ๊ธฐ๋ก
- ์ฌ์ฉ์ ๋ฐ ์์ฒญ ์ ๋ณด
- ์์ฒญํ IP, timestamp, User Agent ๋ฑ ๊ณต๊ฒฉ ์ถ์ ์ ์ ์ฉํ ์ ๋ณด ๊ธฐ๋ก
๋ฏผ๊ฐ ์ ๋ณด(์: ๋น๋ฐ๋ฒํธ, ๊ฐ์ธ์ ๋ณด)๋ ๋ก๊ทธ์ ๊ธฐ๋กํ์ง ์๋๋ก ์ฃผ์
๋ชจ๋ํฐ๋ง ๋๊ตฌ ๋ฐ ๊ธฐ์
- SIEM(๋ณด์์ ๋ณด ๋ฐ ์ด๋ฒคํธ ๊ด๋ฆฌ) ์์คํ
- Splunk, ELK Stack (Elasticsearch, Logstash, Kibana), Graylog ๋ฑ์ ํ์ฉํ์ฌ ๋ก๊ทธ ์ง๊ณ ๋ฐ ์ค์๊ฐ ๋ถ์
- ์๋ฆผ ๋ฐ ์๋ ๋์
- ํน์ ํจํด(์: SQL ๊ตฌ๋ฌธ ์ค๋ฅ, ์ด์ํ ํ๋ผ๋ฏธํฐ ๊ฐ) ๊ฐ์ง ์ ๊ด๋ฆฌ์์๊ฒ ์๋ฆผ ๋ฐ์ก
- ์ผ์ ๊ธฐ์ค ์ด๊ณผ ์ ์๋์ผ๋ก ๋ฐฉ์ด ์กฐ์น(์: IP ์ฐจ๋จ) ์ํ