SQL 인젝션 (SQL Injection)

SQL Injection

설명

SQL 인젝션은 애플리케이션에서 사용자의 입력 데이터를 SQL 쿼리문에 직접 삽입하여 실행할 때 발생하는 취약점입니다. 공격자가 쿼리문의 구조를 망가뜨리거나, 악의적인 SQL 코드를 삽입해 인증 우회, 데이터 열람/수정/삭제와 같은 불법적인 행위를 할 수 있습니다.

잠재적 영향

  • 데이터 유출/변조(데이터 노출 및 조작): 공격자가 데이터베이스의 중요 데이터를 불법적으로 조회, 변경, 삭제할 수 있습니다.
  • 인증 우회(인증 우회): 로그인/권한 검증 쿼리에 인젝션을 넣어 허가받지 않은 사용자가 시스템에 접근할 수 있습니다.
  • 시스템 손상(서비스 거부 또는 시스템 파괴): 데이터베이스 구조의 삭제/변경 등으로 서비스가 마비될 수 있습니다.

해결 방법

  • 쿼리 작성 시 항상 파라미터 바인딩(Prepared Statement, parameterized query)을 사용하세요.
  • 사용자 입력값을 쿼리문에 직접 삽입하지 마세요.
  • ORM(Object-Relational Mapping)에서 제공하는 안전한 쿼리 메서드를 사용하세요.
  • 입력값의 유효성(형식, 길이, 허용값 등)을 검증하고, 불필요한 입력값은 거부하세요.

예시

conn은 미리 준비한 MySQL2 Promise 연결 또는 풀입니다. 연결 생성, 오류 처리와 요청별 접근 권한 확인은 생략했습니다.

변경 전

javascript
const mysql = require('mysql2');
async function getUserById(req, res) {
  let userId = req.query.userId;
  let sql = "SELECT * FROM users WHERE id = " + userId; // 위험: 입력값이 바로 사용됨
  const [rows, fields] = await conn.execute(sql);
  res.json(rows);
}

변경 후

javascript
const mysql = require('mysql2');
async function getUserById(req, res) {
  let userId = req.query.userId;
  // 파라미터 바인딩 사용
  const [rows, fields] = await conn.execute(
    "SELECT * FROM users WHERE id = ?", [userId]
  );
  res.json(rows);
}

설명:

  • 변경 전: 위 코드는 userId를 쿼리 문자열에 직접 연결합니다. 공격자는 userId에 악의적인 SQL 구문을 입력하여 쿼리 조작 및 데이터 탈취, 삭제 등의 공격이 가능합니다.
  • 변경 후: execute에 고정 SQL과 값 배열을 따로 전달하면 prepared statement의 ?에 값이 바인딩됩니다. 값은 SQL 문법과 구분되어 처리됩니다. 입력 형식 검증과 해당 사용자 데이터에 대한 접근 권한 확인은 별도로 수행하세요.

참조