flawopen.com/PHP의 SQL Injection/mysqli와 PDO
MySQLi와 PDO: 안전한 Prepared Statement
Prepared Statement는 SQL 명령을 고정하고 입력값을 별도로 보냅니다. 라벨이 붙은 칸을 채우는 것과 같습니다. PDO와 MySQLi 모두 가능합니다. 안전성은 라이브러리 이름이 아니라 올바른 값 바인딩에서 나옵니다.
두 API로 같은 쿼리를 안전하게 준비하기
$userId = $_GET['id']; $sql = "SELECT id, email FROM users WHERE id = " . $userId; // Both calls execute the already-built SQL string: $rows = $mysqli->query($sql); $rows = $pdo->query($sql);
// PDO $stmt = $pdo->prepare( "SELECT id, email FROM users WHERE id = :id" ); $stmt->execute(["id" => $userId]); // MySQLi $stmt = $mysqli->prepare( "SELECT id, email FROM users WHERE id = ?" ); $stmt->bind_param("i", $userId); $stmt->execute();
PDO의 주요 차이는 데이터베이스 이식성
PDO는 여러 드라이버에 공통 인터페이스를 제공하고 MySQLi는 MySQL과 MariaDB를 대상으로 합니다. PDO만으로 SQL이 이식 가능해지지는 않습니다. 방언, 드라이버 동작, 기능은 다릅니다.
PDO는 드라이버에 따라 :id 같은 이름 지정 또는 ? 위치 지정 플레이스홀더를 지원합니다. 한 문장에서 섞지 마세요. MySQLi는 위치 지정 ?를 사용합니다. 둘 다 값은 바인딩하지만 테이블/열 이름, 키워드, 정렬 방향은 바인딩하지 못합니다.
PDO 드라이버는 네이티브 또는 에뮬레이트 준비를 사용할 수 있습니다. MySQL에서 PDO::ATTR_EMULATE_PREPARES는 지원되는 경우 에뮬레이션을 제어합니다. 네이티브 준비가 불가능하거나 실패하면 에뮬레이션으로 대체될 수 있습니다. 드라이버 문서를 따르세요. 이 설정은 보간된 SQL을 안전하게 만들지 않습니다. SQL을 고정하고 값을 바인딩하세요.
두 라이브러리 모두의 SQL Injection 방지 규칙
prepare()를 사용하고 신뢰할 수 없는 값을 모두 바인딩하세요. SQL 구문을 고정하고 동적 식별자는 서버의 엄격한 매핑이나 유한한 쿼리 변형에서 선택하세요. Prepared Statement는 행 접근을 인가하지 않으므로 필요한 경우 인증 사용자 범위로 쿼리를 제한하세요. LIKE에서 바인딩은 SQL 구문 주입을 막지만 리터럴 검색을 위해 이스케이프하지 않으면 %와 _는 와일드카드입니다.
PDO와 MySQLi 중 선택하기
- ✓여러 드라이버에 공통 API가 필요하면 PDO를 선택하고 마이그레이션 때 SQL 방언과 드라이버 동작을 확인하세요.
- ✓MySQL/MariaDB 전용 앱에서 MySQL 특화 API가 필요하면 MySQLi를 선택하세요.
- ✓둘 다 보간, 연결, 동적 식별자를 점검하고 값을 바인딩하며 레코드 권한은 별도로 확인하세요.
자주 묻는 질문
아니요. 쿼리를 준비하고 값을 바인딩하면 둘 다 값을 안전하게 처리합니다. 이름 지정 방식은 읽기 쉬울 수 있지만 식별자를 바인딩하거나 권한 검사를 대신하지 않습니다.
일괄적으로 그렇지는 않습니다. 드라이버 지원과 대체 동작이 다릅니다. 문서와 앱 요구를 확인하세요. 어떤 모드에서도 SQL 구조를 고정하고 값을 매개변수로 전달하세요.
아니요. 매개변수는 값용이며 SQL 식별자나 키워드용이 아닙니다. 클라이언트 선택을 서버의 유한한 신뢰 SQL 조각에 매핑하세요.