본문 바로가기
공부낙서장

[SQL] Prepared Statements

by 곰인간 2024. 9. 4.

뭐라고 읽어야할까 미리 준비된 구문? Prepared Statements를 사용하면 입력 값을 파라미터로 전달해서 SQL문을 동적으로 생성하는 대신, 미리 정의된 SQL 쿼리에 값을 채워넣을 수 있는데, 사용자 입력이 SQL코드로 해석되는 것을 방지할 수 있다고 한다.

왜 사용자의 입력이 SQL코드로 해석되는 것을 방지해야할까?

SQL 인젝션(Injection)이라는 보안 취약점 때문이라고 하는 데, SQL 인젝션은 공격자가 사용자의 입력을 악의적으로 조작해, 데이터베이스에 임의의 SQL 쿼리를 실행시킬 수 있게 만드는 공격 기법이라고 한다.
이로 인해 공격자는 데이터베이스를 조작하거나 민감한 데이터에 접근할 수 있고, 시스템에 손상을 입힐 수 있다.

1. 데이터 유출 - 공격자는 SQL 인젝션을 통해 데이터베이스의 중요한 정보 (ex. 비밀번호, 개인정보 등)에 접근

2. 데이터베이스 조작 - 공격자는 SQL 인젝션을 통해 데이터베이스를 수정하거나, 데이터를 삭제할 수 있다.
(ex. 테이블 삭제, 중요 데이터 변경)

간단한 로그인 폼이 있다고 가정을 한다.

const username = req.body.username;
const password = req.body.password;

const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;
const result = await db.query(query);

위 코드에서, 사용자가 로그인 폼에 입력한 username과 password는 그대로 SQL 쿼리에 삽입이 되는데, 이때 사용자가 입력한 데이터가 SQL 구문으로 해석될 수 있다.

username: admin
password: password123

정상적으로 입력 시,
SELECT * FROM users WHERE username = 'admin' AND password = 'password123';

정상적으로 입력 시에는 SQL 쿼리에 문제가 없지만,

username: ' ; DROP TABLE users; --   // --뒤에 나오는 구문은 주석으로 처리됨
password: anything

DB 테이블을 삭제하는 악의적인 SQL 인젝션의 예시,

SELECT * FROM users WHERE username = ''; DROP TABLE users; --' AND password = 'anything';

SQL 쿼리에 명시적으로 username과 password가 들어가면 위 코드와 같이 악의적으로 DB 테이블을 삭제가 가능하다.

사용자 입력 필드를 통해 쿼리 변조를하여 데이터베이스 조작이 가능한 것 이다.

그래서 이러한 방지 방법으로 Prepared Statements를 사용하는데, 

쿼리와 데이터 입력값을 분리함으로써, 입력값이 SQL 구문으로 해석되지 않게 보장한다고 한다.

const username = req.body.username;
const password = req.body.password;

const query = `SELECT * FROM users WHERE username = ? AND password = ?`;
const result = await db.query(query, [username, password]);

위 코드는 Prepared Statements를 사용하여 쿼리와 데이터를 분리했기 때문에, 악의적인 입력을 시도하더라도 입력값은 SQL 구문으로 해석되지 않고 데이터로 처리된다고 한다.