데이터베이스 용어 사전
보안SQL Injection · PreparedStatement · 바인딩 변수

SQL 인젝션

사용자 입력이 SQL 문법으로 해석되게 만드는 공격. 파라미터 바인딩으로 구조와 값을 분리하면 원천 차단된다.

사용자 입력이 데이터가 아니라 SQL 문법으로 해석되게 만드는 공격. 웹 취약점의 고전이자 여전히 흔하다.

어떻게 일어나나

// ❌ 문자열을 이어 붙였다
String sql = "SELECT * FROM member WHERE email = '" + input + "'";
-- input 이 정상일 때
SELECT * FROM member WHERE email = 'a@b.com';

-- input 이  ' OR '1'='1  일 때
SELECT * FROM member WHERE email = '' OR '1'='1';   -- 전체 행이 반환된다

-- input 이  '; DROP TABLE member; --  일 때
SELECT * FROM member WHERE email = ''; DROP TABLE member; --';

따옴표를 닫고 문법을 이어 쓰는 것이 공격의 본질이다.

왜 파라미터 바인딩이 막아 주나

// ✅ SQL 뼈대와 값을 분리해 보낸다
PreparedStatement ps = con.prepareStatement("SELECT * FROM member WHERE email = ?");
ps.setString(1, input);
  • ① SQL 뼈대를 먼저 DB 에 보내 파싱시킨다 → 실행 계획의 구조가 이 시점에 확정된다

  • ② 값은 나중에 별도로 보낸다 → 파싱이 이미 끝났으므로 문법으로 해석될 여지가 없다

  • 입력에 무엇이 들어와도 '값' 으로만 취급된다

이스케이프 처리가 아니라 구조적 분리라는 점이 중요하다. 이스케이프는 빠뜨릴 수 있지만 바인딩은 원천적으로 불가능하게 만든다.

바인딩으로 못 막는 곳

// 테이블명 · 칼럼명 · ORDER BY · ASC/DESC 는 바인딩할 수 없다
String sql = "SELECT * FROM member ORDER BY " + sortColumn;   // ❌ 위험

// ✅ 화이트리스트로 검증한다
Set<String> ALLOWED = Set.of("created_at", "name", "email");
if (!ALLOWED.contains(sortColumn)) throw new IllegalArgumentException();

정렬 칼럼을 파라미터로 받는 목록 API가 실무에서 가장 자주 뚫리는 지점이다.

ORM을 써도 안전하지 않은 경우

// JPQL 도 문자열을 이어 붙이면 똑같이 뚫린다
em.createQuery("SELECT m FROM Member m WHERE m.email = '" + input + "'");   // ❌
em.createQuery("SELECT m FROM Member m WHERE m.email = :email")             // ✅
  .setParameter("email", input);

방어를 겹친다

  • 파라미터 바인딩 — — 근본 대책
  • 화이트리스트 검증 — — 바인딩 못 하는 식별자
  • 최소 권한 계정 — — 뚫려도 DROP TABLE 이 실패하게
  • ④ 오류 메시지를 노출하지 않는다 — 스키마 정보가 공격자에게 흘러가지 않게

면접 함정

  • "특수문자를 필터링하면 된다" → 우회 기법이 많다. 바인딩이 정답이다.
  • "ORM을 쓰면 안전하다" → 네이티브 쿼리나 문자열 결합에서는 그대로 뚫린다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 보안·권한 — 인증·인가·암호화·SQL 인젝션