#{} vs ${} — 한 글자 차이의 보안 차이
<select id="login" resultType="com.smhrd.domain.Member">
SELECT * FROM mymember
WHERE id = '${id}'
AND pwd = '${pwd}'
</select>
한 줄 짜리 SQL 처럼 보이고 — 실제로 동작합니다. 그런데 비밀번호 없이도 누구든 admin 으로 로그인할 수 있는 위험한 코드.
사용자 입력이 SQL 문에 그대로 끼워 넣어질 때 — 입력값에 SQL 코드를 함께 넣어 원래 의도와 다른 SQL 을 만드는 것.
INSERT INTO mymember(id, pwd) VALUES
('admin', 'admin1234'),
('hong', 'hongpw');
<!-- 의도적으로 위험한 SELECT -->
<select id="loginUnsafe" resultType="com.smhrd.domain.Member">
SELECT * FROM mymember
WHERE id = '${id}'
AND pwd = '${pwd}'
</select>
public Member loginUnsafe(String id, String pwd) {
return mapper.loginUnsafe(id, pwd);
}
${} 는 사용자 입력을 SQL 문자열에 그대로 끼워넣음:
"... WHERE id = '" + id + "'"
사용자가 따옴표를 입력하면 — SQL 문법 구조가 부서집니다. 입력값과 SQL 문이 섞여버림.
<!-- 안전한 코드 -->
<select id="login" resultType="com.smhrd.domain.Member">
SELECT * FROM mymember
WHERE id = #{id}
AND pwd = #{pwd}
</select>
👉 따옴표 없음! #{} 자체가 PreparedStatement 의 ? 로 변환됨.
WHERE id = '${id}'
SQL 문자열 직접 치환
사용자 입력이 SQL 문법으로 해석
인젝션 가능
WHERE id = #{id}
PreparedStatement 자동 변환
사용자 입력이 「값」으로만 처리
인젝션 불가
${} 가 필요한 경우는 매우 제한적:
ORDER BY ${sortColumn}이 경우에도 입력값을 화이트리스트로 검증한 후에만 ${} 사용:
List<String> allowed = List.of("num", "title", "writer");
if (!allowed.contains(sortColumn)) {
throw new IllegalArgumentException();
}
IDE 에서 ${} 를 검색하는 습관을 들이세요. 본 과정의 게시판 / 회원 SQL 에서 ${} 가 보이면 안 됩니다.
OWASP (Open Worldwide Application Security Project) 가 매년 발표하는 「가장 위험한 보안 취약점 Top 10」 에 SQL 인젝션은 20 년 넘게 상위권입니다.
현실적인 피해 사례:
| 취약점 | 본 과정 차시 |
|---|---|
| SQL 인젝션 | 이 차시 |
| 평문 비밀번호 | ★ v3 안전 인증 (BCrypt) |
| 인가 부재 (남의 글 수정) | ★ v6 안전 게시판 |
| XSS (스크립트 삽입) | JSP <c:out> / EL escape (개념 소개) |
| CSRF | 후속 과정 (Spring Security) |
${} 버전의 로그인 SQL 작성admin' --, 비밀번호 아무거나#{} 로 변경 후 재시도👉 한 번 직접 깨뜨려보면 ${} 를 평생 안 쓰게 됩니다.
| 도구 | 안전한 방식 |
|---|---|
| JDBC 직접 | PreparedStatement.setXxx(idx, value) |
| MyBatis | #{} |
| JPA / Hibernate | JPQL 의 :파라미터 |
| Python (psycopg2) | cur.execute(sql, (params,)) |
| Node.js (mysql2) | conn.query(sql, [params]) |
👉 어느 언어를 써도 「SQL 과 데이터를 분리」 의 발상은 같음.
Mapper XML 의 #{} 와 ${} 가 둘 다 보이는데 차이 모름.
한 글자 차이가 보안의 전부임을 직접 봤다. 모든 SQL 에 #{} 만 쓰는 습관.
다음: @Transactional 기본 — 송금 비유로 트랜잭션 이해.