로그인 페이지(login.php)를 작성하는데, 사용자 정보를 확인할 때 다음과 같은 코드를 작성한 것을 볼 수 있다.
// DB와 연결하기
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "my_db";
$conn = new mysqli($servername, $username, $password, $dbname);
$sql = "SELECT id, password FROM users WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $user_id);
$stmt->execute();
$result = $stmt->get_result();
이는 SQL injection을 방지하기 위한 prepared statement이다.
코드 한 줄 한 줄 해석하기에 앞서, 왜 prepared statement를 사용해야 하는지부터 살펴보겠다.
0-1. 만약 Prepared Statement가 없다면?
입력받은 로그인 정보와 DB에 저장된 users data를 비교하기 위해서, 가장 간단하게는 이런 방식으로 작성할 수 있다.
$sql = "SELECT id, password FROM users WHERE user_id = '$user_id'";
$result = $conn->query($sql);
첫 줄에서는 users 테이블에서 user_id값을 기준으로 데이터를 조회하고, 여기서 id와 password를 SELECT 쿼리로 가져온다. 그 다음, $conn 객체의 query() 함수를 실행하는데, 여기서 첫 줄에 만들어둔 SQL 쿼리문($sql)을 실행하고 그 값을 $result에 저장한다.
이 때, 사용자가 user_id에 'OR '1'='1 를 입력한다고 가정하면, SQL 인젝션이 가능해지는 것을 확인할 수 있다.
//보안 위험 예시
SELECT id, password FROM users WHERE user_id = '' OR '1'='1';
이렇게 되면 WHERE 조건이 항상 true가 되므로, 모든 사용자 정보를 가져올 수 있어 해커가 모든 계정 정보를 조회할 수도 있다.
0-2. Prepared Statement의 일반적인 Flow
prepared statement는 일반적으로 다음과 같은 단계를 거친다.
1) 준비(prepare) : 쿼리의 틀을 만들고, 이때 특정값 대신 ?(바인딩 변수)를 사용하여 placeholder를 남긴다.
2) 컴파일 및 최적화(compile & optimize) : 쿼리의 틀을 컴파일(최적화 및 변환)하며 아직 실행하지 않고 결과만 저장한다.
3) 바인딩(binding) : 바인딩 변수를 설정하여, ?에 들어갈 실제 값을 연결한다.
4) 실행(execute) : 실제 SQL 실행이 이루어진다.
이제 다시 맨 앞으로 돌아가서, 코드 한 줄 한 줄 살펴보자.
1. 준비(prepare) : 바인딩 변수 '?' 사용
$sql = "SELECT id, password FROM users WHERE user_id = ?";
첫 줄은 기존 statement와 거의 똑같은데, '$user_id' 를 바로 입력하지 않고 '?'를 입력했다.
이는 '바인딩 변수' 라고 하는데, SQL에서 값을 직접 넣지 않고 안전하게 처리하기 위해 사용하는 변수다.
2. 준비(prepare), 컴파일 및 최적화(compile & optimize) : Prepare() 사용
$stmt = $conn->prepare($sql);
MySQL에게 "이 SQL을 준비해" 라고 요청한다.
이는 바로 실행하지 않고, 나중에 안전하게 실행하기 위해 필요한 과정이다.
3. 바인딩(binding) : bind_param() 사용
$stmt->bind_param("s", $user_id);
드디어 ? 자리에 $user_id 값을 넣는다.
여기서 "s" 는 문자열(string) 타입의 값을 의미한다.
4. 실행(execute) : execute() 사용
$stmt->execute();
이제 execute()를 호출하면 SQL이 실행된다.
bind_param() 에서 받은 값을 포함해서 실행된다.
5. 결과 반환
$result = $stmt->get_result();
실행된 쿼리의 결과를 객체 형태로 가져온다.
'Learning Web > 2024~2025 Web Hacking' 카테고리의 다른 글
| [Web Hacking] File Download 취약점 (0) | 2025.02.15 |
|---|---|
| [Web Hacking] File Inclusion 취약점 (0) | 2025.02.15 |
| [Web Hacking] 인증/인가 취약점 (0) | 2025.02.14 |
| [Web Hacking] File Upload 취약점 (0) | 2025.02.09 |