사이버안전센터 운영

[Special Report] 웹 취약점과 해킹 매커니즘 #6 SQL Injection 보안대책
제목 [Special Report] 웹 취약점과 해킹 매커니즘 #6 SQL Injection 보안대책
작성자 사이버보안팀 등록일 2022-09-29 조회수 1527
내용

 개요

SQL Injection은 설계된 SQL구문에서 사용자 입력값 검증이 미흡하여, 악의적인 명령을 실행하는 임의의 쿼리를 삽입해 공격이 가능한 취약점이다. 지금까지 공격 유형에 따른 세 가지 종류의 SQL Injection을 살펴보았다. SQL Injection 취약점이 존재할 경우 인증 우회, 데이터베이스 접근과 같은 공격이 가능하기 때문에 데이터 유출, 변조, 삭제 등의 피해를 미칠 수 있다.


따라서 이번 Special Report에서는 SQL Injection의 보안대책을 다룬다. 적절한 보안대책을 통해 입력값을 검증하고 SQL Injection 공격을 예방해야 한다.


■ SQL Injection 보안 대책

SQL Injection 보안 대책의 최선은 Prepared Statement를 사용하여 소스코드를 구성하는 것이다. Prepared Statement를 사용할 수 없는 환경이라면, 차선책으로 사용자 입력값 필터링 또는 정제를 통해 사용자 입력값이 SQL구문에 영향을 미치지 못하도록 해야 한다. 보안 대책에 대한 상세 설명은 다음과 같다.

1) Prepared Statement 사용

Prepared Statement는 SQL Injection을 방어하는 최선의 보안 대책이며, SQL구문이 미리 컴파일 되어 있어 입력값을 변수로 선언해 두고 필요에 따라 값을 대입하여 처리하는 방식이다.

• Statement vs Prepared Statement

SELECT문은 DBMS 내부적으로 4단계의 과정(Parse, Bind, Execute, Fetch)을 거쳐 결과를 출력한다. 특히 구문 분석을 하는 parse 과정을 거치면 다음과 같은 파싱 트리가 생성된다.

 



[파싱 트리]

일반적인 Statement의 경우, 구문 분석(parse)부터 인출(fetch)까지 모든 과정을 매번 수행한다. 따라서 입력값에 SQL구문에 영향을 미치는 특수문자나 예약어가 들어갈 경우 구문 분석 과정에서 SQL구문의 일부로 작용하여 SQL Injection 공격이 가능하다.



[Statement]

Prepared Statement의 경우, 구문 분석(parse) 과정을 최초 1회만 수행하여 생성된 결과를 메모리에 저장해 필요할 때마다 사용한다. 미리 구성된 파싱 트리를 반복적으로 사용하기 때문에 Statement에 비해 시간을 단축할 수 있다. 또한 SQL구문이 미리 컴파일 되어 사용자 입력값을 변수로 선언해 값을 대입하여 사용한다. 따라서 외부 입력값으로 SQL문법에 영향을 미치는 특수문자나 예약어가 입력되어도 문법적인 의미로 작용하지 못한다.


  
[Prepared Statement]

SQL Injection 공격에 취약한 Statement와 안전한 Prepared Statement로 구성된 소스코드는 다음과 같다.

 

• 일반적인 Statement 코드 (취약)


 

Connection conn = null;

Statement stmt = null;

ResultSet rs = null;

...

String sql = "SELECT * FROM MEMBER WHERE ID = '"+param_id+"' AND PW = '"+param_ passwd+"'";

stmt = conn.createStatement();

rs = stmt.executeQuery(sql);






사용자 입력값인 아이디와 패스워드 파라미터인 param_id, param_passwd에 SQL Injection 공격이 가능하다.

• Prepared Statement 코드 (양호)

 

String param_id=request.getParameter("id");

String param_passwd=request.getParameter("passwd");

Connection conn = null;

PreparedStatement pstmt = null;

ResultSet rs = null;

String sql = "SELECT * FROM MEMBER WHERE ID = ? AND PW = ?";

pstmt.setString(1, param_id);

pstmt.setString(2, param_passwd);

rs = pstmt.executeQuery(sql);






SQL구문이 미리 컴파일 되어 있고 사용자 입력값을 받는 부분을 '?'로 바인딩 처리를 하여 setString 메소드를 통해 외부 입력값을 받기 때문에 SQL Injection 공격이 불가능하다.

Prepared Statement를 통해 SQL Injection에 대응하는 것이 근본적인 해결책이지만, 문법적/비즈니스 로직 상 사용 불가한 로직이 존재하기도 한다. 예를 들어, Prepared Statement는 데이터를 파라미터로 전달하는 역할을 하기 때문에 데이터 정렬의 기능을 하는 ORDER BY절에서는 쓰이지 않는다.


또한, 실제 운영 중인 서버의 경우엔 소스코드 수정이 어려울 수 있다. 따라서 이러한 점을 고려하여 Prepared Statement 사용이 불가피한 경우에는 문자열 필터링 또는 정제를 통해 사용자 입력값을 검증하는 방법을 사용해야 한다.

2) 입력값 필터링 및 정제

서비스 운영 등의 이유로 Prepared Statement 사용이 불가피할 경우 차선책을 고려해야 한다.

WhiteList Filter는 허용할 문자열을 제외한 모든 문자를 필터링하는 방법이다. 이때 개별 문자에 대해 지정하는 것보단 정규식 등을 이용해 패턴화해두는 것이 유용하다. 입력값 필터링 시에는 DBMS마다 대소문자 구분이 상이하다는 점을 고려하여 대소문자를 모두 필터링하는 것을 권장한다. 또한 입력값에 길이 제한을 두어 공격 구문 삽입이 어렵도록 해야 한다.



이 콘텐츠의 저작권은 ㈜SK쉴더스에 있으며 영리적, 개인적 사용, 무단 수정을 금합니다. COPYRIGHT ⓒ 2022 SK SHIELDUS. All Rights Reserved.

 



  • 담당부서 : 사이버보안팀
  • 전화번호 : 02-6908-8235
  • 담당자 : 심재인

Q. 이 페이지에서 제공하는 정보에 대하여 어느 정도 만족하셨습니까?

평균 4.3점 / 37명 참여