일반적인 Statement 사용 시 SQL Injection 공격에 취약
원인 : 매번 parse부터 fetch까지 모든 과정을 수행하기 때문이다
만약 SQL Injection 공격을 가하는 경우, 수정된 쿼리(공격)를 parse
웹 어플리케이션에서 입력값에 대한 유효성 검증을 하지 않을 경우, 공격자가 입력 폼 및 파라미터에 쿼리문을 조작하여 정보를 열람하거나 데이터를 조작하는 해킹 기술
Prepared Statement를 사용하는 경우 효율을 높이기 위하여 parse 과정을 최초 1번만 수행하고 이후에는 생략이 가능하다
최초 parse를 통한 결과 parse tree를 메모리 상에 저장해두고 필요할 때마다 사용한다
반복적으로 트리를 사용하기 위해 자주 변경되는 부분을 변수로 선언 해두고, 매번 다른 값을 대입(바인딩) 하여 사용한다
바인딩 데이터는 SQL 문법이 아닌 내부의 인터프리터나 컴파일 언어로 처리하기 때문에 문법적인 의미를 가질 수 없다. 따라서 바인딩 변수 SQL 공격 쿼리 입력할 지라도 의미있는 쿼리로 동작하지 않는다.
SQL Injection 공격을 피할 수 있다.
바인딩 변수를 제외한 공통 부분을 반복적으로 실행할 시, parse 실행시간을 절약할 수 있다.
(파싱이 많아질 경우 CPU 점유율이 높아진다.)
가독성이 높아진다.
// 일반적인 Statment
Class B {
(~~중략~~)
try {
String tableName = props.getProperty("jdbc.tableName");
String name = props.getProperty("jdbc.name");
String query = "SELECT * FROM" + tableName + "WHERE Name = " + name;
//문제가 되는 부분으로 SQL Injection 공격에 취약하다
stmt = con.prepareStatement(query);
// prepareStatement() 메소드를 사용했지만, 그냥 statement와 다를 바 없다
rs = stmt.executeQuery();
}
catch (SQLException sqle) {
}
}
// Prepared Statement
Class B {
(~~중략~~)
try {
String tableName = props.getProperty("jdbc.tableName");
String name = props.getProperty("jdbc.name");
String query = "SELECT * FROM ? WHERE Name = ?";
stmt = con.prepareStatement(query);
stmt.setString(1, tableName);
stmt.setString(2, name);
// 바인딩 변수 부분을 ?로 둔다.
// 그리고 바인딩 변수를 stmt.setString()을 통해 지정해준다.
// stmt.setString(바인딩 변수 인덱스, 값);
// 바인딩변수 인덱스는 1번부터 시작한다.
rs = stmt.executeQuery();
}
catch (SQLException sqle) {
}
}