설명
SQL 인젝션은 외부 입력값이 SQL 쿼리문에 직접 포함되어 실행될 때 발생하는 취약점입니다. 공격자는 쿼리 구조를 바꾸어 허용되지 않은 데이터를 조회하거나 변경할 수 있습니다. 영향 범위는 데이터베이스 계정의 권한과 설정에 따라 달라집니다. 특히 문자열 연결(+)이나 템플릿 리터럴 등으로 사용자 입력을 쿼리에 직접 결합할 경우, 손쉽게 공격이 이루어질 수 있습니다.
잠재적 영향
- 데이터 유출(데이터 노출): 공격자가 암호 같은 민감한 정보를 확인할 수 있습니다.
- 데이터 변조(데이터 변경): 데이터베이스의 데이터를 마음대로 추가, 수정, 삭제할 수 있습니다.
- 서비스 거부(서비스 마비): 악의적인 쿼리로 데이터베이스와 애플리케이션을 다운시킬 수 있습니다.
해결 방법
- 사용자 입력값을 쿼리에 직접 포함하지 않고, 파라미터 바인딩을 사용하세요.
- 입력값의 타입, 길이, 허용 문자 등을 사전에 명확히 검증하세요.
- 동적 테이블·열 이름처럼 값으로 바인딩할 수 없는 부분은 고정된 허용 목록에서 선택하세요.
예시
변경 전
javascript
const express = require('express');
const mssql = require('mssql');
app.get('/user', async (req, res) => {
const id = req.query.id;
const pool = mssql.connect(/* config */);
const request = (await pool).request();
// 위험: 사용자 입력값을 직접 쿼리에 포함
const result = await request.query(`SELECT * FROM users WHERE id = ${id}`);
res.json(result);
});
변경 후
javascript
const express = require('express');
const mssql = require('mssql');
app.get('/user', async (req, res) => {
const id = parseInt(req.query.id, 10); // 정수 변환 예시: 별도 입력 검증 필요
const pool = mssql.connect(/* config */);
const request = (await pool).request();
// 안전: 파라미터 바인딩 사용
request.input('userId', mssql.Int, id);
const result = await request.query('SELECT * FROM users WHERE id = @userId');
res.json(result);
});
설명:
- 변경 전: 사용자 입력값(id)을 검증 없이 SQL 쿼리문에 바로 포함시킵니다. 공격자는 id를 통해 쿼리의 의미를 바꿀 수 있으며, 영향은 연결 계정의 권한에 따라 달라집니다.
- 변경 후:
@userId에 값을 바인딩하여 쿼리 구조와 분리합니다.parseInt는 엄격한 입력 검증이 아닙니다. 전체 입력 형식,NaN, SQLInt범위를 확인하고 요청자의 접근 권한도 검증하세요. 연결 설정과 오류 처리는 생략했습니다.