사용자 입력을 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실수
bBLOB

정리

  • 값이 들어가는 모든 쿼리는 prepared statement로 작성한다.
  • 출력할 때는 htmlspecialchars() 로 XSS도 함께 막는다.
  • 테이블·컬럼명처럼 바인딩이 안 되는 부분은 화이트리스트로 검증한다.

작은 습관이지만, 서비스 전체의 보안 수준을 끌어올리는 가장 효과적인 한 줄이다.