[Database] SQL Injection
SQL Injection
해커에 의해 조작된 SQL 쿼리문이 데이터베이스에 그대로 전달되어 비정상적 명령을 실행시키는 공격 기법
공격의 난이도가 쉬운 것에 비해 피해가 크게 발생하는 편이다
공격 방법
1) 인증 우회(AB: Auth Bypass, Error Based SQL Injection)
보통 로그인을 할 때, 아이디와 비밀번호를 input 창에 입력하게 된다. 쉽게 이해하기 위해 가벼운 예를 들어보자. 아이디가 abc, 비밀번호가 만약 1234일 때 쿼리는 아래와 같은 방식으로 전송될 것이다.
SELECT *
FROM Users
WHERE ID = "abc" AND PASSWORD = "1234";
SQL Injection으로 공격할 때, input 창에 비밀번호를 입력함과 동시에 다른 쿼리문을 함께 입력하는 것
SELECT *
FROM Users
WHERE ID = "abc" AND PASSWORD = "1234";
DELETE FROM Users WHERE ID = "1";
보안이 완벽하지 않은 경우, 이처럼 비밀번호가 아이디와 일치해서 True가 되고 뒤에 작성한 DELETE 문도 데이터베이스에 영향을 줄 수도 있게 되는 치명적인 상황
이 밖에도 기본 쿼리문의 WHERE 절에 OR문을 추가하여 1 = 1과 같은 true문을 작성해 무조건 적용되도록 수정한 뒤 DB를 마음대로 조작 가능
# 공격 대상
SELECT *
FROM Users
WHERE id = 'INPUT1' AND password = 'INPUT2'
# 공격 예시
SELECT *
FROM Users
WHERE id = '**' OR 1=1 --** ' AND password = 'INPUT2'
- 싱글 쿼터를 닫기 위한 싱글 쿼터와 OR 1=1을 통해 WHERE 절을 모두 참으로 만들고 -- 를 넣어 뒤의 구문을 모두 주석처리 함
- ⇒ User 테이블의 모든 정보 조회가 가능해져 가장 먼저 만들어진 계정(보통 관리자 계정)으로 로그인 가능해짐
2) 데이터 노출(DD: Data Disclosure, Union Based SQL Injection)
UNION을 통해 정상적인 쿼리문에 추가 쿼리를 삽입해 원하는 정보를 획득하는 방법
- UNION: 두 개의 쿼리문을 합쳐서 반환할 때 주로 이용
- SELECT * FROM A UNION SELECT * FROM B
조건 1. UNION하는 두 테이블의 칼럼 수가 같아야 함
조건 2. UNION하는 두 테이블의 데이터형이 같아야 함
# 공격 대상
SELECT * FROM Board
WHERE title LIKE '%INPUT%' OR contents '%INPUT%'
# 공격 예시
SELECT * FROM Board
WHERE title LIKE '% **'
UNION SELECT null,id,password
FROM Users --** INPUT%' OR contents '%INPUT%'
- 사용자의 id와 password를 요청하는 쿼리문을 삽입하여 개인정보가 게시글과 함께 화면에 보여지게 됨
3) Blind SQL Injection(Boolean Based SQL, Time Based SQL)
- Boolean Based SQL: 특정한 값이나 데이터를 전달받는 것이 아닌, 쿼리를 통해 나온 참과 거짓의 정보만을 통해 정보를 취득
- 에러가 발생되지 않는 사이트에서는 논리적 에러를 이용하거나 UNION을 이용할 수가 없기 때문에, Blind를 통해 정상적인 쿼리가 수행되는지, 혹은 쿼리가 수행되지 않아 쿼리 결과가 없는지를 판단
- 서버가 응답하는 성공과 실패 여부를 이용하여 DB의 테이블 정보 등을 추출 가능
# 로그인 폼을 통해 DB 테이블명 알아내기 # 공격 대상 SELECT * FROM Users WHERE id = 'INPUT1' AND password = 'INPUT2' # 공격 예시 SELECT * FROM Users WHERE id = '**abc123' and ASCII(SUBSTR((SELECT name FROM information_schema.tables WHERE table_type='base table' limit 0,1),1,1)) > 100(로그인이 될 때까지 시도) --** INPUT1' AND password = 'INPUT2'
- 임의로 가입한 abc123 아이디와 함께 뒤의 쿼리문 삽입
- limit 키워드로 하나의 테이블만 조회, SUBSTR 함수로 첫 글자만 확인, ASCII를 통해 ascii 값으로 변환
- 만약 조회되는 테이블명이 Users라면 ‘U’가 ascii값으로 조회되고 뒤의 100이라는 숫자값과 비교
- 거짓이면 로그인 실패, 참이 될 때까지 뒤의 100이라는 숫자를 변경해가며 비교 → 자동화 스크립트로 만들어 빠르게 테이블명 알아내기 가능
- Time Based SQL: 쿼리 결과를 특정 시간만큼 지연시키는 것으로 에러가 발생되지 않는 조건에서 사용(참 or 거짓 결과값이 나오지 않으므로 시간을 잼)
- 목적: DB 구조 파악
# 로그인 폼을 통해 DB 길이
# 공격 대상
SELECT * FROM Users
WHERE id = 'INPUT1' AND password = 'INPUT2'
# 공격 예시
SELECT * FROM Users
WHERE id = '**abc123' OR
(LENGTH(DATABASE())=1 (SLEEP 할 때까지 시도)
AND SLEEP(2)) --** INPUT1' AND password = 'INPUT2'
- LENGTH → 문자열 길이 반환, DATABASE → DB 이름 반환
- LENGTH(DATABASE()) = 1이 참이면 SLEEP(2) 동작, 거짓이면 동작하지 않음
- 숫자 1 부분을 조작하여 DB 길이 알아내기 가능
- SLEEP 대신 BENCHMARK, WAIT 함수 사용 가능
방어 방법
1) 입력값을 받을 때, 특수문자 여부 검사하기
로그인 전, 검증 로직을 추가하여 미리 설정한 특수문자들이 들어왔을 때 요청을 막아낸다.
2) SQL 서버 오류 발생 시, 에러 메시지 감추기
에러 발생 시 따로 처리하지 않으면 에러가 발생한 쿼리문과 에러 내용을 반환한다. 이 과정에서 테이블명, 칼럼명, 쿼리문이 노출될 수 있으므로 사용자에게 보여줄 페이지나 메시지를 따로 띄워야 한다.
3) Prepared Statement 사용하기
Prepared Statement: 미리 형식이 지정된 쿼리
만약 사용자 입력값에 공격이 발생해도 문법적인 의미를 가질 수 없으므로 방어할 수 있다.
- 전달인자 값을 ? 로 받는다
- ? 에 들어가는 데이터는 단순 문자열 취급하기 때문에 SQL Injection은 발생할 수 없다
# Prepared Statement 예시
INSERT INTO Guests VALUES(?, ?, ?)
출처.
SQL Injection 예시 및 과정 https://choco4study.tistory.com/10
Prepared Statement https://velog.io/@yanghl98/Database-SQL-Injection
'Etc. > CS' 카테고리의 다른 글
[CS 스터디] OSI 7계층 (0) | 2022.06.14 |
---|---|
[CS 스터디] CPU 스케줄링 (0) | 2022.05.20 |
[CS 스터디] IPC (Inter Process Communication) (0) | 2022.05.20 |
[네트워크] 네트워크 모델 (0) | 2022.05.12 |
[네트워크] Wireshark를 이용하여 사용된 프로토콜 확인하기 (0) | 2022.05.10 |