[DB] SQL 인젝션(SQL Injection)

joyful·2021년 8월 25일
0

CS

목록 보기
9/14

1. 개요

응용 프로그램 보안 상의 허점을 의도적으로 이용해, 악의적인 SQL문을 실행되게 함으로써 데이터베이스를 비정상적으로 조작하는 코드 인젝션 공격 방법



2. 종류

2.1 Error based SQL Injection

  • GET, POST 요청 필드, HTTP 헤더값, 쿠키값 등에 특수문자(싱글 쿼드(') 혹은 세미콜론(;)) 삽입 시, SQL 관련 에러를 통해 데이터베이스 정보 예상

✅ 예시 - 우편번호 검색

  • 검색창에 '(작은 따옴표) 입력 후 검색 시도
    • Error base SQL Injection에 취약함 확인
    • zipcode_id라는 컬럼 값 확인
  • ' and db_namd() > 1-- 입력하여 DB명(web) 확인
    • 문자열인 DB 명을 숫자형(int)으로 비교하려고 함에 따라 에러 메시지 출력
  • ' having 1=1-- 입력하여 테이블명.컬럼명(zipcode.zipcode_id) 확인
    • 구문 오류에 따라 메시지 출력
      (having 구문은 반드시 앞에 group by가 같이 쓰여야 함)

2.2 Union based SQL Injection

  • 공격자가 Union을 이용하여 원래의 요청에 추가 쿼리를 삽입하여 정보를 얻어내는 방식

    💡 Union

    2개 이상의 쿼리를 요청하여 결과를 얻은 SQL 연산자

  • Union 쿼리는 2개의 테이블이 동일한 필드 갯수와 데이터타입을 가져야 함
    → 사전 공격을 통해 해당 정보를 얻어야 함

✅ 예시

  • unionselect 절을 이용해 컬럼 갯수를 알아냄
  • select는 검색을 하려던 테이블의 컬럼 명 대신 컬럼 순서를 대신 입력 가능
    → 컬럼 수 확인 가능

2.3 Blind based SQL Injection

  • 에러가 발생되지 않는 사이트에서 SQL 쿼리의 참/거짓 동작만으로 DB 구조를 파악하는 공격 기법
  • 조건이 참이면 페이지가 정상적으로 출력, 거짓이면 페이지가 출력되지 않음으로 구분 가능
  • 요즘에는 에러 메시지를 출력하지 않게 웹서버를 구축하고 있기 때문에 주로 사용하는 방식임

✅ 예시

  • SQL문을 통한 결과가 참인 경우
  • SQL문을 통한 결과가 거짓인 경우

2.4 Stored Procedure based SQL Injection

  • 웹에서 Stored Procedure에 대한 접근 권한을 가짐으로써 실행 가능

    💡 저장 프로시저(Stored Procedure)

    • 사용하고자 하는 Query에 미리 형식을 지정하는 것
    • 일련의 쿼리를 하나의 함수처럼 실행하기 위한 쿼리의 집합
    • 운영상 편의를 위해 만들어둔 SQL 집합 형태
  • 시스템 권한을 획득해야 하므로 공격 난이도가 높으나, 성공한다면 서버에 직접적인 피해 입힐 수 있음

✅ 예시

  • User라는 테이블이 있다고 가정
CREATE TABLE User(
   username VARCHAR(50),
   password VARCHAR(50)
)ENGINE=InnoDB;
  • 데이터베이스에서 usernamepassword 선택
String Query = "SELECT * FROM username = ' " + username
               + " ' AND password = ' " + password + " ' ";
               
execute_query(Query);
  • 사용자 입력을 받은 후 유효성 검사 없이 쿼리 생성
//문제 없이 수행
SELECT * FROM `User` WHERE UserName = 'Hady' AND Password = 'root';

//SQL Injection 공격 - 테이블 삭제
SELECT * FROM `User` WHERE UserName = '' AND Password = '';drop table User;--'

2.5 Time based SQL Injection

  • 쿼리 결과를 특정 시간만큼 지연시키는 것을 이용하는 기법
  • Blind 기법과 마찬가지로 에러가 발생되지 않는 조건에서 사용 가능

✅ 예시

  • 현재 사용하고 있는 데이터베이스의 길이를 알아냄
  • 악의적인 사용자가 abc123’ OR (LENGTH(DATABASE())=1 AND SLEEP(2)) – 이라는 구문을 주입
    • LENGTH() : 문자열의 길이 반환
    • DATABASE() : 데이터베이스의 이름 반환
  • LENGTH(DATABASE()) = 1
    • 참 → SLEEP(2) 동작
    • 거짓 → SLEEP(2) 동작 x
    • 숫자 1 부분을 조작하여 데이터베이스의 길이 알아낼 수 있음


3. 대응 방안

3.1 입력값 검증

  • 사용자의 입력이 DB Query에 동적으로 영향을 주는 경우, 입력된 값이 개발자가 의도한 값 인지 검증

    /*, –, ‘, “, ?, #, (, ), ;, @, =, *, +, union, select, drop, update, from, where, join, substr, user_tables, user_table_columns, information_schema, sysobject, table_schema, declare, dual,…


✅ 예시

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 

/* 특수문자 공백 처리 */
final Pattern SpecialChars = Pattern.compile([‘\”\\-#()@;=*/+]);
UserInput = SpecialChars.matcher(UserInput).replaceAll(“”);
 

final String regex =(union|select|from|where);
 

final Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
final Matcher matcher = pattern.matcher(UserInput);
 

if(matcher.find()){
    out.println(<script>alert(No SQL-Injection);</script>);
}

3.2 저장 프로시저 사용

  • 저장 프로시저를 사용하여 지정된 형식의 데이터가 아니면 Query가 실행되지 않도록 함

✅ 예시

📝 취약 코드

try{
  String uId = props.getProperty(“jdbc.uId”);
 

  String query = “SELECT * FROM tb_user WHERE uId=+ uId;
  stmt = conn.prepareStatement(query);
  ResultSet rs = stmt.executeQuery();
  while(rs.next()){
    ..}
}catch(SQLException se){
  ..}finally{
  ..}

📝 안전한 코드

try{
  String uId = props.getProperty(“jdbc.uId”);
 

  String query = “SELECT * FROM tb_user WHERE uId= ?”
 

  stmt = conn.prepareStatement(query);
  stmt.setString(1, uId);
 

  ResultSet rs = stmt.executeQuery();
  while(rs.next()){
    ..}
}catch(SQLException se){
  ..}finally{
  ..}

3.3 서버 보안

  • 최소 권한 유저로 DB 운영
  • 사용하지 않는 저장 프로시저와 내장함수 제거/권한 제어
  • 목적에 따라 Query 권한 수정
  • 공용 시스템 객체의 접근 제어
  • 신뢰할 수 있는 네트워크, 서버에 대해서만 접근 허용
  • 에러 메시지 노출 차단



📖 참고

profile
기쁘게 코딩하고 싶은 백엔드 개발자

0개의 댓글