[DB] SQL Injection 공격과 방어 방법

강민승·2023년 8월 13일
0

DB

목록 보기
3/12
post-thumbnail

SQL Injection

해커에 의해 조작된 SQL 쿼리문이 데이터베이스에 그대로 전달되어 비정상적 명령을 실행시키는 공격 기법


📍 SQL Injection

SQL Injection 이란 악의적인 사용자가 보안상의 취약점을 이용하여, 임의의 SQL 문을 주입하고 실행되게 하여 데이터베이스가 비정상적인 동작을 하도록 조작하는 행위 입니다. 인젝션 공격은 OWASP Top10 중 첫 번째에 속해 있으며, 공격이 비교적 쉬운 편이고 공격에 성공할 경우 큰 피해를 입힐 수 있는 공격입니다.

📍 여기어때 해킹 사건

2017년 3월에 일어난 “여기어때” 의 대규모 개인정보 유출 사건도 SQL Injection 으로 인해 피해가 발생하였습니다.

OWASP top 10

공격 방법

1. 클래식 SQL 인젝션 (Classic SQL Injection)

정의: 사용자 입력값을 그대로 SQL 쿼리에 삽입하여 악의적인 SQL 코드를 실행하는 기본적인 공격 방법입니다.
예시:
sql

SELECT * FROM users WHERE username = '[입력값]' AND password = '[입력값]';

사용자가 admin'; -- 라는 값을 입력하면 쿼리는 다음과 같이 변합니다.

SELECT * FROM users WHERE username = 'admin'; --' AND password = '[입력값]';

이렇게 되면 password 조건이 주석처리되어 비밀번호 검증 없이 관리자 계정에 액세스할 수 있게 됩니다.

2. 블라인드 SQL 인젝션 (Blind SQL Injection)

정의: 공격자가 데이터베이스의 데이터를 직접 조회하지 않고, 참/거짓의 결과를 통해 정보를 추출하는 방법입니다.

하위 유형:
불리언 기반 블라인드 SQL 인젝션 (Boolean-based Blind SQLi): SQL 쿼리의 결과가 참이면 정상적인 응답을, 거짓이면 에러 응답을 반환하는 경우를 이용합니다.

예시: 사용자 이름을 검증하는 쿼리에서 OR 1=1 또는 OR 1=2와 같은 조건을 추가하여 참/거짓 확인.

admin' and (실행할 쿼리문) and 1 = 1 ()

시간 기반 블라인드 SQL 인젝션 (Time-based Blind SQLi): SQL 쿼리의 결과에 따라 응답 지연을 유발시키는 함수를 사용하여 참/거짓을 판별합니다.

예시: SLEEP 또는 BENCHMARK 함수를 이용하여 조건이 참일 때 지연을 발생시키는 쿼리를 생성합니다.

3. UNION 기반 SQL 인젝션 (UNION-based SQL Injection)

정의: UNION SQL 연산자를 사용하여 원래의 쿼리 결과에 추가적인 선택 결과를 결합하는 방식입니다.
예시: 원래 쿼리에 UNION SELECT NULL, username, password FROM users를 추가하여 사용자 정보를 반환하는 쿼리를 생성합니다.

4. 에러 기반 SQL 인젝션 (Error-based SQL Injection)

정의: 데이터베이스가 에러 메시지를 반환할 때, 그 메시지를 통해 데이터베이스의 정보를 추출하는 방법입니다.
예시: CAST 함수와 같은 함수를 사용하여 강제적으로 에러를 발생시키고, 해당 에러 메시지에서 데이터베이스의 정보를 추출합니다.

5. Out-of-band SQL Injection

정의: 데이터베이스 서버가 직접 외부와 통신할 수 있을 때 사용되는 방법입니다.
예시: 데이터베이스 함수를 이용하여 공격자의 서버로 특정 데이터를 전송하게 만듭니다.

6. 스택쿼리 SQL 인젝션 (Stacked-queries SQL Injection)

정의: 하나의 SQL 문장 뒤에 추가적인 SQL 문장을 넣어서 실행하는 방식입니다.
예시: 세미콜론(;)을 사용하여 기존 쿼리 뒤에 DROP TABLE users;와 같은 악의적인 쿼리를 추가합니다.



📌 방어 방법

1) input 값을 받을 때, 특수문자 여부 검사하기

로그인 전, 검증 로직을 추가하여 미리 설정한 특수문자들이 들어왔을 때 요청을 막아낸다.

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

public class InputValidation {

    private static final String EMAIL_PATTERN = 
        "^[A-Za-z0-9+_.-]+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,6}$";

    private Pattern pattern;
    private Matcher matcher;

    public InputValidation() {
        pattern = Pattern.compile(EMAIL_PATTERN);
    }

    public boolean validateEmail(String email) {
        matcher = pattern.matcher(email);
        return matcher.matches();
    }

    public static void main(String[] args) {
        InputValidation validator = new InputValidation();

        String email = "user@example.com";
        boolean isValid = validator.validateEmail(email);

        if (isValid) {
            System.out.println("Email is valid.");
        } else {
            System.out.println("Email is invalid.");
        }
    }
}

2) SQL 서버 오류 발생 시, 해당하는 에러 메시지 감추기

view를 활용하여 원본 데이터베이스 테이블에는 접근 권한을 높인다. 일반 사용자는 view로만 접근하여 에러를 볼 수 없도록 만든다.

-- 원본 테이블 예시: 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;

3) preparestatement 사용하기

preparestatement를 사용하면, 특수문자를 자동으로 escaping 해준다. (statement와는 다르게 쿼리문에서 전달인자 값을 ?로 받는 것) 이를 활용해 서버 측에서 필터링 과정을 통해서 공격을 방어한다.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DatabaseExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/yourdatabase";
        String user = "username";
        String password = "password";

        try (Connection con = DriverManager.getConnection(url, user, password)) {
            String query = "INSERT INTO Users (UserName, UserEmail) VALUES (?, ?)";

            try (PreparedStatement pst = con.prepareStatement(query)) {
                pst.setString(1, "John Doe");
                pst.setString(2, "john@example.com");

                pst.executeUpdate();
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
}
profile
Step by Step goes a long way. 꾸준하게 성장하는 개발자 강민승입니다.

0개의 댓글