SQL Injection은 웹 애플리케이션이 사용자 입력을 적절히 검증하거나 필터링하지 않아 발생하는 보안 취약점입니다. 공격자는 미리 정의된 SQL 쿼리 뒤에 추가적인 SQL 문장을 삽입하여 데이터베이스를 조작할 수 있습니다. 이를 통해 권한 없는 쿼리를 실행하고 중요한 데이터를 탈취할 수 있습니다.
아래는 SQL Injection의 기본 원리를 설명하기 위한 테스트 코드입니다.
### 데이터베이스 설정
db.properties 파일:
### JdbcUtils.java (데이터베이스 연결 유틸리티)
### 문제 있는 로그인 코드 (SQL Injection 위험)
위 코드에서 사용자가 `' OR '1'='1`와 같은 값을 입력하면 모든 사용자 정보가 노출될 수 있습니다.
### 해결책: PreparedStatement 활용
PreparedStatement는 SQL Injection을 방지하며 성능도 개선합니다. 아래는 안전한 로그인 코드의 예시입니다.
이렇게 하면 사용자의 입력값이 쿼리문 자체에 직접 포함되지 않으므로 SQL Injection 공격을 방지할 수 있습니다.
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/exampledb?useUnicode=true&characterEncoding=utf8&useSSL=false
username=exampleuser
password=examplepass
public class DatabaseConnector {
private static String driver;
private static String url;
private static String username;
private static String password;
static {
try {
InputStream input = DatabaseConnector.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(input);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
public static java.sql.Connection connect() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
public static void closeResources(java.sql.Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try { rs.close(); } catch (SQLException ignored) {}
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException ignored) {}
}
if (conn != null) {
try { conn.close(); } catch (SQLException ignored) {}
}
}
}
public class VulnerableLogin {
public static void main(String[] args) {
authenticate("' OR '1'='1", "irrelevant"); // SQL Injection 시도
}
public static void authenticate(String id, String pwd) {
java.sql.Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DatabaseConnector.connect();
stmt = conn.createStatement();
String query = "SELECT * FROM users WHERE username='" + id + "' AND password='" + pwd + "'";
rs = stmt.executeQuery(query);
if (rs.next()) {
System.out.println("로그인 성공: " + rs.getString("username"));
} else {
System.out.println("로그인 실패");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DatabaseConnector.closeResources(conn, stmt, rs);
}
}
}
public class SecureLogin {
public static void main(String[] args) {
authenticate("' OR '1'='1", "irrelevant"); // 이제 Injection 공격 무효화됨
}
public static void authenticate(String id, String pwd) {
java.sql.Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DatabaseConnector.connect();
String query = "SELECT * FROM users WHERE username=? AND password=?";
pstmt = conn.prepareStatement(query);
pstmt.setString(1, id);
pstmt.setString(2, pwd);
rs = pstmt.executeQuery();
if (rs.next()) {
System.out.println("로그인 성공: " + rs.getString("username"));
} else {
System.out.println("로그인 실패");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DatabaseConnector.closeResources(conn, pstmt, rs);
}
}
}