SQL Injection이란?
악의적인 사용자가 보안상의 취약점을 이용하여 임의의 SQL 문을 주입하고 실행되게 하여 데이터베이스가 비정상적인 동작을 하도록 조작하는 행위이다.
SQL Injection 예시 (로그인 Dao)
public boolean login(User user) {
String sql = "SELECT * FROM user WHERE " +
"email = '" + user.getEmail() + "' AND password = '" + user.getPassword() + "'";
try {
ResultSet rs = conn.createStatement().executeQuery(sql);
if (rs.next()) {
return true;
}
return false;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
위의 Dao 클래스는 email과 password를 입력받아 sql문을 실행한다.
email과 password 두 값이 쿼리에 직접 삽입되기 때문에 악의적인 코드를 포함할 경우 SQL Injection이 발생한다.
1. 인증 우회
user.getEmail() -> ' OR '1'='1
user.getPassword() -> ' OR '1'='1
위와 같은 값들이 들어갔다고 해보겠다.
SELECT * FROM user WHERE email = '' OR '1'='1' AND password = '' OR '1'='1'
이런 쿼리가 실행될 것이다.
이렇게 되면은 1=1은 항상 참이기 때문에 데이터베이스는 모든 값을 반환하게 되고 로그인이 되게 될 것이다.
2. 데이터 유출
user.getEmail() -> ' OR 1=1 --
user.getPassword() -> asdfasd (아무 값)
위와 같은 값들이 들어갔다고 해보겠다.
SELECT * FROM user WHERE email = '' OR 1=1 -- AND password = 'asdfasd'
-- 은 SQL에서 주석을 의미한다. 1=1은 항상 참이므로 모든 사용자의 데이터를 반환하게 될 것이다.
3. 데이터 삭제
user.getEmail() -> ' ; DROP TABLE user --
user.getPassword() -> asdfasd (아무 값)
위와 같은 값들이 들어갔다고 해보겠다.
SELECT * FROM user WHERE email = ''; DROP TABLE user -- AND password = 'asdfasd'
; (세미콜론)은 SQL에서 명령어를 구분한다. 그렇게 되면 첫 번째 쿼리가 끝난 후 user라는 테이블이 삭제가 될 것이다.
=> 위와 같은 공격들이 가능한 이유는 쿼리에 데이터를 직접 삽입했기 때문이다. 이를 방지하기 위해서는 반드시 PreparedStatement를 사용해야 한다.
PreparedStatement 사용
public boolean login(User user) {
// String sql = "SELECT * FROM user WHERE " +
// "email = '" + user.getEmail() + "' AND password = '" + user.getPassword() + "'";
String sql = "SELECT * FROM user WHERE email = ? AND password = ?";
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, user.getEmail());
pstmt.setString(2, user.getPassword());
ResultSet rs = pstmt.executeQuery();
// CallableStatement cstmt = conn.prepareCall("{CALL LOGIN(?, ?)}");
// cstmt.setString(1, user.getEmail());
// cstmt.setString(2, user.getPassword());
// ResultSet rs = cstmt.executeQuery();
if (rs.next()) {
return true;
}
return false;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
SQL Injection이 발생하는 이유는 위와 같이 입력값을 SQL 문장에 직접 넣기 때문에 발생한다.
Statement는 입력값을 검증없이 그냥 쿼리안에 넣으니 해커가 악의적인 정보를 넣음으로써 SQL Injection이 발생하였다.
그런데 PreparedStatment는 입력값을 쿼리에 넣지 않고 입력값은 그냥 데이터로 취급해서 SQL 문법으로 해석이 되지 않는다. 즉 입력값을 SQL에 바로 넣지 않고 따로 처리하기 때문에 아무리 악의적인 값을 넣어도 그냥 문자열로 다뤄지기 때문에 해커가 의도한대로 동작이 이뤄지지 않는다는 뜻이다.
결론은 Statement는 쿼리안에 그대로 넣고 PreparedStatement는 데이터를 SQL과 분리하기 때문에 보안에 강한 문법이라 PreparedStatement를 쓰는 것을 권장한다.
'한화시스템 Beyond SW Camp > 백엔드' 카테고리의 다른 글
| [Java] PortOne 카카오결제 연동 (1) | 2025.02.02 |
|---|---|
| [Java] 카카오 로그인 (2) | 2025.01.31 |
| [Java] MVC 패턴, Layered 패턴 (0) | 2025.01.15 |
| [IntelliJ] Tomcat Servlet, mariaDB 연동 (0) | 2025.01.08 |
| [Java] 스레드 (1) | 2025.01.08 |