์น ์ดํ๋ฆฌ์ผ์ด์ ์์ ์ ๋ ฅ ๋ฐ์ดํฐ์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฆ์ ํ์ง ์์ ๊ฒฝ์ฐ, ๊ณต๊ฒฉ์๊ฐ SQL๋ฌธ์ ์ฝ์ ํด DB๋ก๋ถํฐ ์ ๋ณด ์ด๋/์กฐ์์ด ๊ฐ๋ฅํด์ง๋ ๋ณด์ ์ฝ์
[์ถ์ฒ] JAVA ์ํ์ด์ฝ๋ฉ ๊ฐ์ด๋ P.3
- ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฐ์ ํํฐ๋ง ์์ด ๋๊ฒจ ๋ฐ์ ์ทจ์ฝํ ์น ์ดํ๋ฆฌ์ผ์ด์ ์ ๋์ ์ฟผ๋ฆฌ(Dynamic Query) ์์ฑ
- ์๋ํ์ง ์์ ์ฟผ๋ฆฌ ์์ฑ์ผ๋ก ์ธํ ์ ๋ณด์ ์ถ ๊ฐ๋ฅ
preparedStatement
ํด๋์ค & ํ์ ๋ฉ์๋executeQuery()
,execute()
,executeUpdate()
์ฌ์ฉ- ์ ๋ ฅ๊ฐ ํํฐ๋ง ํ ์ฌ์ฉ (ex. SQL ๊ตฌ๋ฌธ ์ ํ, ํน์ ๋ฌธ์ ์ ํ, ๊ธธ์ด ์ ํ ๋ฑ)
์ธ๋ถ๋ก๋ถํฐ tableName๊ณผ name์ ๊ฐ์ ๋ฐ์์ SQL ์ฟผ๋ฆฌ ์์ฑ
name ๊ฐ์ผ๋ก name' OR 'a'='a ์ ๋ ฅํด ์กฐ์๋ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๋ฌธ์์ด ์ ๋ฌ
PreparedStatement stmt = null;
try {
โฆโฆ
// ์ธ๋ถ ํ๊ฒฝ์์ tablename๊ณผ name ์
๋ ฅ ๋ฐ์
String tableName = props.getProperty("jdbc.tableName");
String name = props.getProperty(" jdbc.name" );
String query = "SELECT * FROM " + tableName + " WHERE Name =" + name;
// ์ฌ์ฉ์ ์
๋ ฅ ๋ฐ์ดํฐ ๊ทธ๋๋ก SQL์ ๋ฐ์
// ์ฌ์ฉ์๊ฐ name์ ์
๋ ฅํด ํ์ธ์ ์ ๋ณด๋ฅผ ์ด๋ํ๋ SQL ๊ฐ๋ฅ
stmt = con.prepareStatement(query);
rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
โฆโฆ
while (rs.next()) { โฆโฆ }
dos.writeBytes(printStr);
} catch (SQLException sqle) { โฆโฆ }
finally { โฆโฆ }
โฆโฆ
์์ ํ ์ฝ๋(java) :
์ธ๋ถ์์ ์ธ์๋ฅผ ๋ฐ๋ preparedStatement
๊ฐ์ฒด๋ฅผ ์์ ์คํธ๋ง์ผ๋ก ์์ฑ
setXXX
๋ฉ์๋๋ก ์ธ์ ๋ถ๋ถ ์ค์
-> ์ธ๋ถ์ ์
๋ ฅ์ด ์ฟผ๋ฆฌ๋ฌธ์ ๊ตฌ์กฐ ๋ฐ๊พธ๋ ๊ฒ ๋ฐฉ์ง
โฆโฆ
PreparedStatement stmt = null;
try {
โฆโฆ
String tableName = props.getProperty("jdbc.tableName");
String name = props.getProperty("jdbc.name");
// preparedStatement๋ฅผ ํตํ ๋์ ์ฟผ๋ฆฌ ์์ฑ ๋ฐฉ์ง
String query = "SELECT * FROM ? WHERE Name = ? " ;
stmt = con.prepareStatement(query);
// setXXX ๋ฉ์๋๋ฅผ ํตํด ์ฌ์ฉ์ ์
๋ ฅ ๋ฐ์ดํฐ ์ค์
stmt.setString(1, tableName);
stmt.setString(2, name);
rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
String printStr = "";
while (rs.next()) { โฆโฆ }
dos.writeBytes(printStr);
} catch (SQLException sqle) { โฆโฆ }
finally { โฆโฆ }
โฆโฆ
#include <stdlib.h>
#include <sql.h>
void Sql_process(SQLHSTMT sqlh)
{
char *query = getenv("query_string");
SQLExecDirect(sqlh, query, SQL_NTS);
}
์์ ํ ์ฝ๋(C) :
์ธ์ํ๋ ์ฟผ๋ฆฌ ์ฌ์ฉํด ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๋ณ๊ฒฝ ๋ฐฉ์ง
#include <sql.h>
void Sql_process(SQLHSTMT sqlh)
{
char *query_items = "SELECT * FROM items";
SQLExecDirect(sqlh, query_items, SQL_NTS);
}
http request๋ก๋ถํฐ ์ฌ์ฉ์ ID์ ์ํธ ์ถ์ถํด SQL ์ฟผ๋ฆฌ ์์ฑ
์ฌ์ฉ์ ID๋ฅผ guest' OR ' a'='a'-- ๋ก ์ค์ ํ์ฌ ์์ฑ๋ ์ฟผ๋ฆฌ:
SELECT * FROM members WHERE userId = 'guest' OR 'a'='a'-- AND password = ''
WHERE์ ํญ์ ์ฐธ -> ์ฌ๋ฐ๋ฅธ ์ํธ๊ฐ ์๋์ฌ๋ ์ฌ์ฉ์ ์ ๋ณด ์กฐํ/์ด๋ ๊ฐ๋ฅ
โฆโฆ
public class SqlInjectionSample extends HttpServlet
{
private final String GET_USER_INFO_CMD = "get_user_info";
private Connection con;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException
{
String command = request.getParameter("command");
if (command.equals(GET_USER_INFO_CMD))
{
Statement stmt = con.createStatement();
String userId = request.getParameter("user_id");
String password = request.getParameter("password");
String query = "SELECT * FROM members WHERE username '" + userId +
"' AND password = '" + password + "'";
stmt.executeUpdate(query);
}
โฆโฆ
}
โฆโฆ
}
์์ ํ ์ฝ๋(java): makeSecureString ์ ์ฉ
SQL ๊ตฌ๋ฌธ ์์ฑ ์ , ์ธ๋ถ๋ก๋ถํฐ ์
๋ ฅ๋ ID์ ์ํธ๋ฅผ makeSecureString
๋ฉ์๋๋ฅผ ํตํด DB ์ฟผ๋ฆฌ ์ฌ์ฉ์ ์์ ํ ํํ๋ก ๋ณ๊ฒฝ
์ ์
- 3๊ฐ์ง ์ ํ ์กฐ๊ฑด ์ ์ฉํด ์ผ๋ฐ ๋ฌธ์์ด์ ์ฟผ๋ฆฌ ์ธ์๋ก ์ฌ์ฉ ๊ฐ๋ฅํ ์์ ํ ๋ฌธ์์ด๋ก ๋ฐ๊พธ๋ ๋ฉ์๋
3๊ฐ์ง ์ ํ ์กฐ๊ฑด
-
ID์ ์ํธ ๊ฐ์ ์ธ์์ ๊ธธ์ด ์ ํ
๊ณต๊ฒฉ ๊ตฌ๋ฌธ ์์ฑ์ ์ผ๋ฐ์ผ๋ก ID์ ์ํธ ๊ธธ์ด ๊ธธ์ด์ง
์ธ์์ ๊ธธ์ด ์ ํํ์ฌ ์ธ์๋ฅผ ํตํ ๊ณต๊ฒฉ๊ตฌ๋ฌธ ์ฝ์ ์ด๋ ค์ ๋ถ์ฌ -
์ธ์์ SQL๋ฌธ์์ ์ฐ์ด๋ ์์ฝ์ด ์ฝ์ ์ ํ
Injection ๊ณต๊ฒฉ ๊ตฌ๋ฌธ ์์ฑ์ SQL๋ฌธ์์ ์ฐ๋ ์์ฝ์ด ์ฌ์ฉ ๊ฐ๋ฅ์ฑ ๋์
SQL ์ฌ์ฉํ๋ ๋ช ๋ น์ด ๋ธ๋๋ฆฌ์คํธ ๋ฑ๋ก๊ณผ ์ธ์์์ ๊ฐ์ ์ ์ญ์ ํด ๊ณต๊ฒฉ ์ฐจ๋จ -
์ธ์์ ์ํ๋ฒณ๊ณผ ์ซ์๋ฅผ ์ ์ธํ ๋ฌธ์ ์ฌ์ฉ ์ ํ
๊ณต๊ฒฉ ๊ตฌ๋ฌธ ์์ฑ์, ํน์ ๋ฌธ์ ์ฌ์ฉ ๊ฐ๋ฅ์ฑ ๋์
์ํ๋ฒณ, ์ซ์ ์ ์ธ ๋ฌธ์๋ฅผ ์ธ์์์ ๊ฐ์ ์ ์ญ์ ํด ๊ณต๊ฒฉ์ ์ํ ํผํด ๋ฐฉ์ง
Regular Expression (์ ๊ท์) ์ฌ์ฉํด 3๊ฐ์ง ์ ํ ์กฐ๊ฑด ์ ์ฉ
[^\\p{Alnum}] | select | delete | update | insert | create | alter | drop
[^\\p{Alnum}]
: ์ซ์, ์ํ๋ฒณ ์ ์ธ ๋ฌธ์๋คselect, delete, update, insert, create, alter, drop
: SQL๋ฌธ ์์ฝ์ด- SQL ์์ฝ์ด๋ฅผ null string("")์ผ๋ก ๋์ฒด
๊ทธ ์ธ ๋ฌธ์์ด ๊ธธ์ด ์ ํ ๋ฎ์ถค, ์ ๊ท์ ํฌํจ ๋จ์ด ๋๋ฆผ์ ํตํด ๋ณด๋ค ์ ๋ฐํ ๋ฐฉ์ด ์คํ ๊ฐ๋ฅ
โฆโฆ
public class SqlInjectionSample extends HttpServlet
{
//์์
type ์ง์
private final String GET_USER_INFO_CMD = "get_user_info";
private Connection con;
//id์ password ์ด๋ ๊ธธ์ด ์ ํ
private final static int MAX_USER_ID_LENGTH = 8;
private final static int MAX_PASSWORD_LENGTH = 16;
//SQL ์์ฝ์ด์ ์ํ๋ฒณ, ์ซ์๋ฅผ ์ ์ธํ ๋ค๋ฅธ ๋ฌธ์ ๊ฒ์ถํ๋ ์ ๊ท์ ์ค์
private final static String UNSECURED_CHAR_REGULAR_EXPRESSION = "[^\\
p{Alnum}]|select|delete|update|insert|create|alter|drop";
private Pattern unsecuredCharPattern;
//์ ๊ท์ ์ด๊ธฐํ
public void initlalize()
{
unsecuredCharPattern = Pattern.compile(UNSECURED_CHAR_REGULAR_EXPRESSION,
attern.CASE_INSENSITIVE);
//โฆโฆ
}
//์
๋ ฅ๊ฐ์ ์ ๊ท์์ ํตํด ํํฐ๋ง ํ ์์ฌ๋๋ ๊ฒ์ ์ ๊ฑฐ
private String makeSecureString(final String str, int maxLength)
{
String securestStr = str.substring(0, maxLength);
Matcher matcher = unsecuredCharPattern.matcher(securestStr);
return matcher.replaceAll("");
}
//โฆโฆ
//์
๋ ฅ๊ฐ์ ํํฐ๋งํ ํ ์ฟผ๋ฆฌ๋ก ์์ฑํด ์ฒ๋ฆฌ
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException
{
String command = request.getParameter("command");
if (command.equals(GET_USER_INFO_CMD))
{
Statement stmt = con.createStatement();
String userId = request.getParameter("user_id");
String password = request.getParameter("password");
String query = "SELECT * FROM members WHERE username '" + makeSecureString(userId,
MAX_USER_ID_LENGTH) + "' AND password = '" + makeSecureString(password,
MAX_PASSWORD_LENGTH) + "'";
stmt.executeUpdate(query);
}
โฆโฆ
}
โฆโฆ
}
์์ ํ์ง ์์ ์ฝ๋(C):
queryStr์ ์ธ๋ถ ์
๋ ฅ์์ user_id
์ password
์ ๊ฐ์ ์๋ผ ๊ทธ๋๋ก SQL๋ฌธ ์ธ์ ๊ฐ์ผ๋ก ์ฌ์ฉ
static SQLHSTMT statmentHandle;
const char * GetParameter(const char * queryString, const char * key);
static const char * GET_USER_INFO_CMD = "get_user_info";
static const char * USER_ID_PARAM = "user_id";
static const char * PASSWORD_PARAM = "password";
static const int MAX_QUERY_LENGTH = 256;
const int EQUAL = 0;
int main(void)
{
SQLCHAR * queryStr;
queryStr = getenv("QUERY_STRING");
if (queryStr == NULL)
{
// Error ์ฒ๋ฆฌ ๋ฃจํด
...
}
// ์
๋ ฅ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
char * command = GetParameter(queryStr, "command");
if (strcmp(command, GET_USER_INFO_CMD) == EQUAL)
{
// userId์ password ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
const char * userId = GetParameter(queryStr, USER_ID_PARAM);
const char * password = GetParameter(queryStr, PASSWORD_PARAM);
char query[MAX_QUERY_LENGTH];
sprintf(query, "SELECT * FROM members WHERE username= '%s' AND password ='%s'", userId, password);
SQLExecDirect(statmentHandle, query, SQL_NTS);
}
return 0;
}
์์ ํ ์ฝ๋(C):
makeSecureString
๋ฉ์๋๋ฅผ ํตํด ์ํํ SQL ์์ฑ ์ ๋ฐํ๋ ๋จ์ด/ํน์๋ฌธ์ ์ ๊ฑฐ
static const int
๋ก ์ ์ธํ MAX_USER_ID_LENGTH
์ MAX_PASSWORD_LENGTH
์ ์ค์ ํ ๊ฐ์ ๋ฒ์ด๋๋ ๋ฌธ์๋ ์ ๊ฑฐ๋จ
regexec
๋ฉ์๋๋ฅผ ํตํด ์ ๊ท์์ ๊ทผ๊ฑฐํด ์ํ ๋ฌธ์๋ ์ ๊ฑฐํ๊ณ ๊ทธ ์๋ค๋ฅผ ์ฐ๊ฒฐํจ
const char * GetParameter(const char * queryString, const char * key);
static const char * GET_USER_INFO_CMD = "get_user_info";
static const char * USER_ID_PARAM = "user_id";
static const char * PASSWORD_PARAM = "password";
static const int MAX_QUERY_LENGTH = 256;
static const int MAX_USER_ID_LENGTH = 8;
static const int MAX_PASSWORD_LENGTH = 16;
const char * makeSecureString(const char *str, int maxLength);
const int EQUAL = 0;
int main(void)
{
// ํํฐ๋ง์ ์ฌ์ฉํ ์ ๊ท์ ์ค์
int reti = regcomp(&unsecurePattern, "[^[:alnum:]]|select|delete|update|insert|create|alter|drop", REG_ICASE | REG_EXTENDED);
// ์ ๊ท์ ์ค์ ์ด ์คํจํ์ ๊ฒฝ์ฐ ๋๋จธ์ง ํผ๋ฆฌ๋ฅผ ํ์ง ์๊ณ ๊ฐ์ ์ข
๋ฃํจ
if (reti)
{
fprintf(stderr, "Could not compile regex\n");
exit(1);
}
SQLCHAR * queryStr;
queryStr = getenv("QUERY_STRING");
if (queryStr == NULL)
{
// ์
๋ ฅ๊ฐ์ด null์ธ ๊ฒฝ์ฐ Error ์ฒ๋ฆฌ
...
}
char * command = GetParameter(queryStr, "command");
if (strcmp(command, GET_USER_INFO_CMD) == EQUAL)
{
// ๊ฐ ์
๋ ฅ๊ฐ์ ์ชผ๊ฐ ํ makeSecureStringํจ์๋ก ํํฐ๋ง ๊ฑฐ์ณ ๊ฒ์ฆ
const char * userId = GetParameter(queryStr, USER_ID_PARAM);
userId = makeSecureString(userId, MAX_USER_ID_LENGTH);
const char * password = GetParameter(queryStr, PASSWORD_PARAM);
password = makeSecureString(password, MAX_PASSWORD_LENGTH);
char query[MAX_QUERY_LENGTH];
sprintf(query, "SELECT * FROM members WHERE username= '%s' AND password ='%s'", userId, password);
SQLExecDirect(statmentHandle, query, SQL_NTS);
free(userId);
free(password);
}
regfree(&unsecurePattern);
return EXIT_SUCCESS;
}
// ์
๋ ฅ๊ฐ์ ํํฐ๋งํด ๊ฒ์ฆํ๋ ๋ฃจํด
const char * makeSecureString(const char *str, int maxLength)
{
char * buffer = (char *) malloc(maxLength + 1);
char * originalStr = (char *)malloc(maxLength + 1);
strncpy(originalStr, str, maxLength);
originalStr[maxLength] = NULL;
regmatch_t mt;
const char * currentPos = originalStr;
//โ์ ๊ท์์ ๋งค์นญ๋๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ๊ทธ ๋ถ๋ถ ๊ฑด๋๋ฐ๋ ํํ๋ก ๋ฌธ์์ด ๋ณ๊ฒฐ
while (regexec(&unsecurePattern, currentPos, 1, &mt, REG_NOTBOL) == 0)
{
strncat(buffer, currentPos, mt.rm_so);
currentPos += mt.rm_eo;
}
strcat(buffer, currentPos);
free(originalStr);
return buffer;
}
ํน์ ํ ์ฝ๋๋ฅผ ์ ๋ณํด ๋ง๋ ๋ธ๋๋ฆฌ์คํธ์ ์๊ฑฐํ ํํฐ๋ง ๋ฐฉ์ ์ฌ์ฉ์ ๋ค์ํ ํํ์ SQL ํจ์ ์ด์ฉํ ๊ณต๊ฒฉ๋ฐฉ๋ฒ์ ์ฐธ๊ณ ํด์ผ ํจ
ํํฐ๋ง ์ฝ๋์ ์์ฑ๋๋ ๋ธ๋๋ฆฌ์คํธ๋ฅผ ๋ง๋๋ ์์ฑ์๊ฐ ์ผ๋ง๋ ๊ณต๊ฒฉ ํจํด์ ๋ง์ด ์๊ณ ์๋๋์ ๋ฐ๋ผ ์ข์ฐ๋จ
๊ณต๊ฒฉ ๊ตฌ๋ฌธ | ์ค๋ช |
---|---|
IF (1=1) SELECT 'true' ELSE SELECT 'false' | ์ํํ ๊ตฌ๋ฌธ ํ์ฉ |
SELECT CHAR(0x66) | ์ํํ ํจ์์ฌ์ฉ ํ์ฉ (๋ค๋ฅธ blind sql ๊ณต๊ฒฉ๊ตฌ๋ฌธ์ ์์ฃผ ์ด์ฉ๋จ) |
SELECT CONCAT(CHAR(75),CHAR(76),CHAR(77)) | ์ํํ ํจ์์ฌ์ฉ ํ์ฉ |
SELECT ASCII('a') | ์ํํ ํจ์์ฌ์ฉ ํ์ฉ |
SELECT header, txt FROM news UNION ALL SELECT name, pass FROM members | ์ํํ ๊ตฌ๋ฌธ ํ์ฉ |
INSERT INTO members(id, user, pass) VALUES(1, "+SUBSTRING(@@version,1,10) , 10) | ์์คํ ์ ๋ณด ๋ ธ์ถ |
exec master..xp_cmdshell 'dir' | ์ํํ ๋ช ๋ น์ด ์ฌ์ฉ(์์คํ down) |
SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>78-- | True, False๋ฅผ ํตํด ๋ฐ์ดํฐ ๊ฐ์ ์ ์ถํ ์ ์์ |
IF (SELECT * FROM login) BENCHMARK(1000000,MD5(1)) | ์์คํ ์์ ์๋ชจ ์ ๋ |
WAITFOR DELAY '0:0:10'-- | ์์คํ ์์ ์๋ชจ ์ ๋ |
MD5(), SHA1(), PASSWORD(), ENCODE(), COMPRESS(), ROW_COUNT(), SCHEMA(), VERSION() | ์ํํ ํจ์ ์ฌ์ฉ |
bulk insert foo from '\YOURIPADDRESS\C$\x.txt' | Windows UNC Share๋ฅผ ์ ์ฉ |