SQL Injection이란?
데이터베이스와 연동된 웹 애플리케이션에서 공격자가 입력 폼 또는 URL 입력란에 SQL 구문을 삽입하여 DB 를 조작할 수 있는 취약점을 의미한다.
주로 사용자가 입력한 데이터를 제대로 filtering, escaping하지 못했을 경우에 발생한다.
공격 종류 및 방법
1. Classic SQL Injection
사용자 입력값을 그대로 SQL 쿼리에 삽입하여 악의적인 SQL 쿼리를 실행하는 기본적인 공격 방법이다.
로그인 공격 예시
로그인 페이지가 있고, 로그인을 할 때 USER_ID 와 USER_PASSWORD 를 입력받아 로그인이 진행된다고 했을 때,
기본 쿼리문
SELECT user FROM Users WHERE user_id = 'USER_ID' AND user_password = 'USER_PASSWORD';
공격 예시: 로그인 창의 ID 부분에 'OR 1=1 -- 를 입력
SELECT user FROM Users WHERE user_id = '' OR 1 = 1 --'USER_ID' AND user_password = 'USER_PASSWORD';
- WHERE 절에 있는 싱글 쿼터(')를 닫아주고,
- OR 1=1 로 모두 참을 만들어 준 후
- --를 이용해 그 뒤의 모든 쿼리문을 주석처리 한다.
결과적으로 Users 테이블에 있는 모든 정보를 조회하게 되며 가장 먼저 만들어진 계정(보통 관리자 계정)으로 로그인할 수 있게 되어 관리자 계정을 탈취하게 된다.
2. Blind SQL Injection
공격자가 데이터베이스의 데이터를 직접 조회하지 않고, 참/거짓의 결과를 통해 정보를 추출하는 방법이다.
Boolean 기반 공격 예시
기본 쿼리문
SELECT user FROM Users WHERE user_id = 'USER_ID' AND user_password = 'USER_PASSWORD';
공격 예시: 로그인 폼에 DB 테이블명을 알아내기 위한 쿼리문을 주입, 이때 임의로 가입한 id3이라는 아이디와 함께 구문을 주입
SELECT user FROM Users WHERE user_id = 'id3' AND ASCII(SUBSTR((SELECT name FROM information_schema.tables WHERE table_type='base table' limit 0,1),1,1)) > 100 -- USRE_ID' AND user_password = 'USER_PASSWORD';
- limit 키워드를 통해 하나의 테이블만 조회하고, SUBSTR 함수로 첫 글자만 찾게 된다.
- ASCII를 통해 값이 변환되고 조회디는 테이블명의 첫번째 글자가 U면 테이블이 조회된다.
- True(로그인)이 될 때까지 100 숫자를 변경하며 비교를 하게 된다.
- 자동화 스크립트로 만들어 단기간 내에 테이블 명을 알아낼 수도 있다.
Time 기반 공격 예시
기본 쿼리문
SELECT user FROM Users WHERE user_id = 'USER_ID' AND user_password = 'USER_PASSWORD';
공격 예시
SELECT user FROM Users WHERE user_id = 'id3' OR (LENGTH(DATABASE())=1 AND SLEEP(2)) -- USER_ID' AND user_password = 'USER_PASSWORD';
- 숫자 1을 조작해 현재 사용하고 있는 데이터베이스의 길이를 알아낼 수 있다.
- LENGTH를 사용해 문자열 길이를 반환하도록 한다.
- DATABASE()를 사용해 데이터베이스의 이름을 반환한다.
3. Union based SQL Injection
UNION SQL 연산자를 사용하여 원래의 쿼리 결과에 추가적인 SELECT 결과를 결합하는 방법이다.
게시글 조회 공격 예시
게시판이 있고, 게시글을 검색할 때 INPUT 을 받아 검색이 진행된다고 했을 때,
기본 쿼리문
SELECT * FROM Board WHERE title LIKE '%INPUT%' OR contents LIKE '%INPUT%';
공격 예시: 검색 창에 'UNION SELECT null, id, password FROM Users -- 를 입력
SELECT * FROM Board WHERE title LIKE '%' UNION SELECT null, id, password FROM Users --%' OR contents LIKE '%INPUT%';
사전 공격을 통해 컬럼명과 테이블명을 얻은 후 사용자의 ID와 PW를 요청하는 쿼리문을 함께 입력하게 되면 사용자의 개인 정보가 게시글과 함께 보이게 된다.
4. Error based SQL Injection
데이터베이스가 에러 메시지를 반환할 때, 그 메시지를 통해 데이터베이스의 정보를 추출하는 방법이다.
공격 예시: CAST 와 같은 함수를 사용하여 강제적으로 에러를 발생시키고, 해당 에러 메시지에서 데이터베이스의 정보를 추출한다.
대응 방법
1. 입력값 검증
- ', ", #, --, = 등 특수문자와 명령어 필터링
- 데이터 길이 제한
2. Prepared Statement 사용
3. SQL 서버 오류 발생시 해당하는 에러 메시지 감추기
4. 데이터베이스의 권한 제한
-- 원본 테이블 예시: Users
CREATE TABLE Users (
UserID INT PRIMARY KEY,
UserName VARCHAR(100),
UserEmail VARCHAR(100)
);
-- 뷰 생성
CREATE VIEW PublicUsers AS
SELECT UserName, UserEmail
FROM Users;
-- 사용자는 이 뷰를 통해서만 데이터를 조회
SELECT * FROM PublicUsers;
5. 웹 방화벽(WAF)을 사용하여 비정상적인 데이터가 전송될 경우 차단
[References]
https://velog.io/@33bini/DB-SQL-Injection
'Database' 카테고리의 다른 글
[MySQL] 트랜잭션 격리 수준 (0) | 2025.01.19 |
---|---|
[MySQL] 트랜잭션 (Transaction) (1) | 2024.01.05 |
[Mongo] 백업 및 복원하기 (0) | 2021.08.27 |