SQL 질의문을 생성할 때 검증되지 않은 외부 입력 값을 허용하여 악의적인 질의문이 실행가능한 보안약점
인증 우회 시스템에 로그인 가능
조작된 쿼리를 이용해 권한 없는 사용자가 DB 데이터를 유출하거나 수정, 삭제 가능
저장 프로시저(Stored Procedure)를 호출하여 원격으로 시스템 명령어를 수행 가능
(PreparedStatement 객체를 이용) 컴파일 된 정적 쿼리문(상수)으로 쿼리문 구조가 바뀌지 않도록 제한
(동적 쿼리를 사용해야 하는 경우) 입력값에 쿼리문의 구조를 변경할 수 있는 특수문자 및 쿼리 예약어를 검사(필터링)한 후 쿼리문 생성에 사용
(스트러츠(Struts), 스프링(Spring) 등과 같은 프레임워크 사용) 외부입력값 검증모듈 및 보안모듈을 상황에 맞추어 적절하게 사용
HTTP 500 오류 정보가 노출되지 않도록 제한
(웹 애플리케이션) 데이터베이스 사용자 권한을 최소화
//외부로부터 입력받은 값을 검증 없이 사용할 경우 안전하지 않다.
String gubun = request.getParameter("gubun");
…
String sql = "SELECT * FROM board WHERE b_gubun = '" + gubun + "'";
Statement stmt = con.createStatement();
//외부로부터 입력받은 값이 검증 또는 처리 없이 쿼리로 수행되어 안전하지 않다.
ResultSet rs = stmt.executeQuery(sql);
외부로부터 입력받은 gubun의 값을 아무런 검증과정을 거치지 않고 SQL 쿼리를 생성하는데 사용
gubun의 값으로 a' or 'a' = 'a 를 입력하면 조건절이 b_gubun = 'a' or 'a' = 'a' 로 바뀌어 쿼리의 구조가 변경되어 board 테이블의 모든 내용이 조회
String gubun = request.getParameter("gubun");
…
//1. 사용자에 의해 외부로부터 입력받은 값은 안전하지 않을 수 있으므로, PreparedStatement 사용을 위해 “?” 문자로 바인딩 변수를 사용한다.
String sql = "SELECT * FROM board WHERE b_gubun = ?";
//2. PreparedStatement 사용한다.
PreparedStatement pstmt = con.prepareStatement(sql);
//3.PreparedStatement 객체를 상수 스트링으로 생성하고, 파라미터 부분을 setString등의 메소드로 설정하여 안전하다.
pstmt.setString(1, gubun); ResultSet rs = pstmt.executeQuery();
//$기호를 사용하는 경우 외부에서 입력된 keyword값을 문자열에 결합한 형태로 쿼리에 반영되므로 안전하지 않다.
select * from tbl_board where title like '%${keyword}%' order by pos asc 1
//$ 대신 #기호를 사용하여 변수가 쿼리맵에 바인딩 될 수 있도록 수정하는 것이 안전하다.
select * from tbl_board where title like '%'||#{keyword}||'%' order by pos asc
MyBatis Data Map은 외부에서 입력되는 값이 SQL 질의문을 연결하는 문자열로 사용되는 경우에 의도하지 않은 정보가 노출될 수 있는 공격 형태이다
//Hiberate는 기본으로 PreparedStatement를 사용하지만, 파라미터 바인딩 없이 사용 할 경우 안전하지 않다.
Query query = session.createQuery("from Student where studentName = '" + name + "' ");
String name = request.getParameter("name");
//1. 파라미터 바인딩을 위해 ?를 사용한다.
Query query = session.createQuery("from Student where studentName = ? ");
//2. 파라미터 바인딩을 사용하여 외부 입력값에 의해 쿼리 구조 변경을 못하게 사용하였다.
query.setString(0, name);
//1. 파라미터 바인딩을 위해 명명된 파라미터 변수를 사용한다.
Query query = session.createQuery("from Student where studentName = :name ");
//2. 파라미터 바인딩을 사용하여 외부 입력값에 의해 쿼리 구조 변경을 못하게 사용하였다.
query.setParameter("name", name);
public void ButtonClickBad(object sender, EventArgs e)
{
string connect = "MyConnString";
string usrinput = Request["ID"];
// 외부로부터 입력받은 값을 SQL 쿼리에 직접 사용하는 것은 안전하지 않다.
string query = "Select * From Products Where ProductID = " + usrinput;
using (var conn = new SqlConnection(connect))
{
using (var cmd = new SqlCommand(query, conn))
{
conn.Open();
cmd.ExecuteReader(); /* BUG */
}
}
}
void ButtonClickGood(object sender, EventArgs e)
{
string connect = "MyConnString";
string usrinput = Request["ID"];
//파라미터 바인딩을 위해 @ 을 사용합니다. 외부입력 값에 의해 쿼리 구조 변경을 할 수 없습니다.
string query = "Select * From Products Where ProductID = @ProductID";
using (var conn = new SqlConnection(connect))
{
using (var cmd = new SqlCommand(query, conn))
{
cmd.Parameters.AddWithValue("@ProductID",
Convert.ToInt32(Request["ProductID"]);
conn.Open();
cmd.ExecuteReader();
}
}
Statement statement 객체로 쿼리가 실행되는 부분을 확인
Statement 객체가 PreparedStatement 객체인지 확인
쿼리에 사용되는 변수가 외부 입력값인 경우엔 적절한 필터링 모듈이 존재하는지 추가로 확인