🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

SQL Injection 방어 완벽 가이드 - 슬라이드 1/13
A

AI Generated

2025. 10. 31. · 22 Views

SQL Injection 방어 완벽 가이드

웹 애플리케이션의 가장 위험한 보안 취약점 중 하나인 SQL Injection을 방어하는 다양한 기법을 학습합니다. Prepared Statement, ORM, 입력 검증 등 실전에서 바로 적용 가능한 방어 기법을 다룹니다.


카테고리:JavaScript
언어:JavaScript
메인 태그:#JavaScript
서브 태그:
#SQLInjection#Security#PreparedStatement#InputValidation

들어가며

이 글에서는 SQL Injection 방어 완벽 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.

목차

  1. Prepared_Statement_기본
  2. Named_Parameters_사용
  3. ORM을_통한_안전한_쿼리
  4. 입력_검증_화이트리스트
  5. 입력_이스케이프_처리
  6. 저장_프로시저_활용
  7. 최소_권한_원칙_적용
  8. 에러_메시지_숨김
  9. WAF_활용
  10. 입력_타입_검증
  11. 쿼리_빌더_안전_사용
  12. 정기_보안_테스트

1. Prepared Statement 기본

개요

Prepared Statement는 SQL 쿼리와 데이터를 분리하여 SQL Injection을 근본적으로 차단하는 가장 효과적인 방법입니다.

코드 예제

const mysql = require('mysql2/promise');

async function getUserById(userId) {
  const connection = await mysql.createConnection(config);
  const [rows] = await connection.execute(
    'SELECT * FROM users WHERE id = ?',
    [userId]
  );
  return rows;
}

설명

'?' 플레이스홀더를 사용하여 SQL 쿼리와 사용자 입력을 분리합니다. 데이터베이스 드라이버가 자동으로 입력값을 이스케이프 처리하여 악의적인 SQL 코드 실행을 방지합니다.


2. Named Parameters 사용

개요

Named Parameter를 사용하면 복잡한 쿼리에서도 가독성을 유지하면서 안전하게 데이터를 바인딩할 수 있습니다.

코드 예제

async function searchUsers(username, email) {
  const [rows] = await connection.execute(
    'SELECT * FROM users WHERE username = :username AND email = :email',
    { username, email }
  );
  return rows;
}

설명

:username, :email과 같은 명명된 파라미터를 사용하여 여러 값을 안전하게 바인딩합니다. 쿼리의 가독성이 높아지고 파라미터 순서 오류를 방지할 수 있습니다.


3. ORM을 통한 안전한 쿼리

개요

Sequelize, TypeORM 같은 ORM을 사용하면 SQL을 직접 작성하지 않고도 안전한 데이터베이스 작업이 가능합니다.

코드 예제

const { Sequelize, DataTypes } = require('sequelize');

const User = sequelize.define('User', {
  username: DataTypes.STRING,
  email: DataTypes.STRING
});

async function findUser(username) {
  return await User.findOne({ where: { username } });
}

설명

ORM은 내부적으로 Prepared Statement를 사용하여 모든 쿼리를 자동으로 파라미터화합니다. 개발자가 SQL Injection을 신경 쓰지 않아도 안전한 코드를 작성할 수 있습니다.


4. 입력 검증 화이트리스트

개요

사용자 입력을 받을 때 허용된 값만 통과시키는 화이트리스트 방식으로 검증하여 공격 벡터를 원천 차단합니다.

코드 예제

function getSortColumn(userInput) {
  const allowedColumns = ['id', 'name', 'created_at'];

  if (!allowedColumns.includes(userInput)) {
    throw new Error('Invalid sort column');
  }
  return userInput;
}

설명

동적 테이블명, 컬럼명, ORDER BY 절 등 Prepared Statement로 처리할 수 없는 경우 화이트리스트로 검증합니다. 허용된 값만 통과시켜 공격 가능성을 완전히 제거합니다.


5. 입력 이스케이프 처리

개요

불가피하게 동적 SQL을 사용해야 할 때는 데이터베이스별 이스케이프 함수를 사용하여 특수문자를 무력화합니다.

코드 예제

const mysql = require('mysql2');

function searchByPattern(pattern) {
  const escaped = mysql.escapeId(pattern);
  const query = `SELECT * FROM ${escaped}`;
  return connection.query(query);
}

설명

mysql.escape()는 값을, mysql.escapeId()는 식별자(테이블명, 컬럼명)를 이스케이프합니다. 하지만 이 방법은 최후의 수단이며, 가능하면 Prepared Statement를 사용해야 합니다.


6. 저장 프로시저 활용

개요

저장 프로시저를 사용하면 SQL 로직을 데이터베이스 내부에 캡슐화하여 애플리케이션 레이어의 SQL Injection 위험을 줄일 수 있습니다.

코드 예제

async function registerUser(username, email) {
  await connection.execute(
    'CALL sp_register_user(?, ?)',
    [username, email]
  );
}

// DB: CREATE PROCEDURE sp_register_user(
//   IN p_username VARCHAR(50), IN p_email VARCHAR(100))

설명

저장 프로시저는 파라미터화된 방식으로 실행되며, 복잡한 비즈니스 로직을 안전하게 처리할 수 있습니다. 애플리케이션 코드에서는 프로시저를 호출만 하면 됩니다.


7. 최소 권한 원칙 적용

개요

데이터베이스 사용자 계정에 필요한 최소한의 권한만 부여하여 SQL Injection 공격의 피해 범위를 제한합니다.

코드 예제

// DB 사용자 권한 설정 예시
// CREATE USER 'app_user'@'localhost'
//   IDENTIFIED BY 'password';
// GRANT SELECT, INSERT, UPDATE ON mydb.users
//   TO 'app_user'@'localhost';

const config = {
  user: 'app_user',  // DROP, ALTER 권한 없음
  password: 'password',
  database: 'mydb'
};

설명

애플리케이션 DB 계정에 DROP, ALTER 등 위험한 권한을 부여하지 않습니다. SQL Injection이 발생해도 데이터 조회/수정만 가능하고 테이블 삭제 등은 불가능하게 만듭니다.


8. 에러 메시지 숨김

개요

데이터베이스 에러 메시지를 사용자에게 노출하지 않아 공격자가 데이터베이스 구조를 파악하지 못하도록 합니다.

코드 예제

async function handleQuery(userId) {
  try {
    const [rows] = await connection.execute(
      'SELECT * FROM users WHERE id = ?', [userId]
    );
    return rows;
  } catch (error) {
    console.error('DB Error:', error);
    throw new Error('Database operation failed');
  }
}

설명

상세한 SQL 에러 메시지는 로그에만 기록하고, 사용자에게는 일반적인 에러 메시지만 반환합니다. 공격자가 테이블명, 컬럼명 등의 정보를 얻지 못하게 합니다.


9. WAF 활용

개요

Web Application Firewall을 사용하여 SQL Injection 패턴을 자동으로 탐지하고 차단하는 추가 보안 계층을 구축합니다.

코드 예제

const helmet = require('helmet');
const expressSanitizer = require('express-sanitizer');

app.use(helmet());
app.use(expressSanitizer());

app.post('/search', (req, res) => {
  const sanitized = req.sanitize(req.body.query);
  // 추가 검증 및 처리
});

설명

미들웨어 레벨에서 위험한 패턴을 필터링합니다. UNION, OR 1=1, DROP TABLE 등의 SQL Injection 시도를 사전에 차단할 수 있습니다.


10. 입력 타입 검증

개요

TypeScript나 스키마 검증 라이브러리를 활용하여 입력 데이터의 타입과 형식을 엄격하게 검증합니다.

코드 예제

const Joi = require('joi');

const userSchema = Joi.object({
  id: Joi.number().integer().positive().required(),
  username: Joi.string().alphanum().min(3).max(30),
  email: Joi.string().email()
});

const { error, value } = userSchema.validate(req.body);

설명

Joi, Zod 같은 스키마 검증 라이브러리로 입력값의 타입, 길이, 형식을 검증합니다. SQL Injection에 사용되는 특수문자나 예상 밖의 입력을 사전에 차단합니다.


11. 쿼리 빌더 안전 사용

개요

Knex.js 같은 쿼리 빌더를 사용하면 동적 쿼리를 안전하게 작성할 수 있으며, 자동으로 파라미터 바인딩이 적용됩니다.

코드 예제

const knex = require('knex')({ client: 'mysql2' });

async function searchUsers(filters) {
  return await knex('users')
    .where('status', filters.status)
    .andWhere('age', '>', filters.minAge)
    .select('*');
}

설명

쿼리 빌더는 메서드 체이닝으로 쿼리를 구성하며, 모든 값은 자동으로 파라미터화됩니다. SQL 문자열을 직접 작성하지 않아 안전성이 높습니다.


12. 정기 보안 테스트

개요

OWASP ZAP, SQLMap 같은 도구로 정기적으로 SQL Injection 취약점을 스캔하여 보안 상태를 점검합니다.

코드 예제

// package.json 스크립트
{
  "scripts": {
    "security:scan": "npm audit && snyk test",
    "pentest:sql": "sqlmap -u 'http://localhost/api' --batch --risk=3"
  }
}

// CI/CD 파이프라인에 통합

설명

자동화된 보안 스캔을 CI/CD 파이프라인에 통합하여 배포 전에 SQL Injection 취약점을 발견합니다. 지속적인 모니터링으로 새로운 취약점을 조기에 탐지할 수 있습니다.


마치며

이번 글에서는 SQL Injection 방어 완벽 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.

관련 태그

#JavaScript #SQLInjection #Security #PreparedStatement #InputValidation

#JavaScript#SQLInjection#Security#PreparedStatement#InputValidation

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.