사용자 입력을 SQL에 직접 이어붙이면 SQL 인젝션에 노출된다. 결론부터 말하면 mysqli의 prepared statement(준비된 쿼리) 를 쓰면 값과 쿼리 구문이 분리되어 대부분의 인젝션을 원천 차단할 수 있다.
위험한 코드
// 절대 이렇게 쓰지 말 것
$id = $_GET['id'];
$sql = "SELECT * FROM members WHERE id = '$id'";
$result = mysqli_query($conn, $sql);id 값에 ' OR '1'='1 같은 문자열이 들어오면 조건이 항상 참이 되어 전체 행이 노출될 수 있다.
안전한 코드 (prepared statement)
$stmt = mysqli_prepare($conn, "SELECT id, name FROM members WHERE id = ?");
mysqli_stmt_bind_param($stmt, "s", $_GET['id']);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while ($row = mysqli_fetch_assoc($result)) {
echo htmlspecialchars($row['name']);
}? 자리표시자에 값을 바인딩하므로, 입력값이 아무리 이상해도 쿼리 구조가 바뀌지 않는다.
bind_param 타입 문자
| 문자 | 의미 |
|---|---|
| s | 문자열 |
| i | 정수 |
| d | 실수 |
| b | BLOB |
정리
- 값이 들어가는 모든 쿼리는 prepared statement로 작성한다.
- 출력할 때는
htmlspecialchars()로 XSS도 함께 막는다. - 테이블·컬럼명처럼 바인딩이 안 되는 부분은 화이트리스트로 검증한다.
작은 습관이지만, 서비스 전체의 보안 수준을 끌어올리는 가장 효과적인 한 줄이다.