스택큐힙리스트

PHP에서 SQL 인젝션을 어떻게 방지할 수 있을까요? 본문

카테고리 없음

PHP에서 SQL 인젝션을 어떻게 방지할 수 있을까요?

스택큐힙리스트 2023. 3. 16. 12:17
반응형

이 질문의 답변은 #@!%&##$&입니다. 기존 답변을 편집하여이 게시물을 개선하십시오. 새로운 답변 또는 상호 작용을 현재 받지 않습니다.

만약 사용자 입력이 수정 없이 SQL 쿼리에 삽입된다면, 애플리케이션은 다음과 같은 SQL injection로 취약해질 수 있습니다.

$unsafe_variable = $_POST['user_input'];

mysql_query(INSERT INTO `table` (`column`) VALUES ('$unsafe_variable'));

그것은 사용자가 value'); DROP TABLE table;--와 같은 것을 입력할 수 있기 때문에 그 쿼리는 다음과 같아진다:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

이것을 방지하기 위해 무엇을 할 수 있을까요?

답변 1

어떤 데이터베이스를 사용하더라도 SQL 인젝션 공격을 피하는 올바른 방법은 SQL에서 데이터를 분리하여 데이터가 SQL 파서에 의해 명령으로 해석되지 않고 데이터 그대로 유지되도록 하는 것입니다. 데이터의 부분을 올바르게 서식 지정된 SQL 문을 만들 수 있으나 세부 정보를 완전히 이해하지 못한다면 항상 준비된 문장과 매개 변수화된 쿼리를 사용해야 합니다. 이러한 문장은 모든 매개 변수를 분리하고 데이터베이스 서버로 전송하여 파싱됩니다. 이렇게 하면 공격자가 악성 SQL을 삽입하는 것이 불가능합니다.

이를 달성하는 방법은 기본적으로 두 가지가 있습니다.

PDO (지원되는 데이터베이스 드라이버에 대해 사용):

$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');

$stmt->execute([ 'name' => $name ]);

foreach ($stmt as $row) {

// Do something with $row

}

MySQL을 사용하여 MySQLi을(를) 사용합니다.

PHP 8.2+ 이후로는 execute_query()를 사용하여 매개 변수를 준비하고 바인드하며 SQL 문을 실행할 수 있습니다.

$result = $dbConnection->execute_query('SELECT * FROM employees WHERE name = ?', [$name]);

while ($row = $result->fetch_assoc()) {

// Do something with $row

}

PHP8.1 이전까지:

$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');

$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'

$stmt->execute();

$result = $stmt->get_result();

while ($row = $result->fetch_assoc()) {

// Do something with $row

}

만약 MySQL이 아닌 데이터베이스에 연결하고 싶다면, (pg_prepare()와 pg_execute() 같은) 드라이버 특정의 두 번째 옵션을 참조할 수 있습니다. PDO는 보편적인 옵션입니다.

연결을 올바로 설정하기

PDO (PHP Data Objects)

PDO를 사용하여 MySQL 데이터베이스에 액세스 할 때 기본적으로 실제 준비된 문장은 사용되지 않습니다. 이를 해결하기 위해서는 준비된 문장의 에뮬레이션을 비활성화해야합니다. PDO를 사용하여 연결을 생성하는 예시는 다음과 같습니다.

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4', 'user', 'password');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

위의 예에서 오류 모드는 엄격하게 필요하지는 않지만, 추가하는 것이 좋습니다. 이렇게하면 PDO가 PDOException을 던져 MySQL 모든 오류를 알려줍니다.

그러나 필수적인 것은 PDO에게 에뮬레이트된 준비문을 비활성화하고 실제 준비문을 사용하도록 지시하는 첫 번째 setAttribute() 라인입니다. 이는 문과 값이 MySQL 서버로 보내기 전에 PHP에서 구문 분석되지 않도록하므로 (악의적인 SQL을 삽입할 수 있는 공격자에게 기회를 제공하지 않음) MySQL 서버에 안전하게 전송됩니다.

생성자의 옵션에서 charset 을 설정할 수 있지만, 중요한 것은 PHP의 '이전' 버전 (5.3.6 이전)에서는 DSN에서 silently ignored the charset parameter 가 발생할 수 있다는 점입니다.

Mysqli

mysqli를 사용하려면 같은 과정을 따라야 합니다.

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // error reporting

$dbConnection = new mysqli('127.0.0.1', 'username', 'password', 'test');

$dbConnection->set_charset('utf8mb4'); // charset

설명

prepare에 전달하는 SQL 문은 데이터베이스 서버에서 구문 분석 및 컴파일됩니다. 매개 변수 (?) 또는 위의 예제에서와 같이 이름이 지정된 매개 변수 (:name)를 지정함으로써 필터링할 위치를 데이터베이스 엔진에 알려줍니다. 그런 다음 execute를 호출할 때 준비된 문은 지정한 매개 변수 값과 결합됩니다.

여기서 중요한 것은 매개 변수 값이 SQL 문자열이 아닌 컴파일된 문에 결합된다는 것입니다. SQL 주입은 스크립트가 데이터베이스로 전송할 SQL을 작성할 때 악성 문자열을 포함하도록 속이는 것입니다. 따라서 실제 SQL을 매개 변수와 별도로 보내면 의도하지 않은 결과물이 나오는 위험을 제한할 수 있습니다.

준비된 문에서 사용할 때 보내는 매개변수는 모두 문자열로 처리됩니다 (물론 데이터베이스 엔진이 최적화 작업을 수행하여 매개변수가 숫자로 끝날 수도 있습니다). 위의 예제에서, 만약 $name 변수가 'Sarah'; DELETE FROM employees를 포함한다면, 결과는 간단히 문자열 'Sarah'; DELETE FROM employees의 검색이 될 것이며, 결과적으로 an empty table를 얻을 수 없습니다.

준비된 문장을 사용하는 또 다른 이점은 동일한 세션에서 동일한 문장을 여러 번 실행하면 한 번만 구문 분석 및 컴파일되며 일부 속도 향상을 제공한다는 것입니다.

아, 그리고 삽입을 위해 어떻게 할 수 있는지 물어보셨으니 예제를 드리겠습니다 (PDO를 사용하여):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute([ 'column' => $unsafeValue ]);

동적 쿼리에 준비된 문을 사용할 수 있습니까?

쿼리 매개 변수의 경우 여전히 준비된 문을 사용할 수 있지만, 동적 쿼리의 구조는 매개 변수화 될 수 없으며 특정 쿼리 기능도 매개 변수화될 수 없습니다.

이러한 특정 시나리오에서는 가능한 값을 제한하는 화이트리스트 필터를 사용하는 것이 가장 좋습니다.

// Value whitelist

// $dir can only be 'DESC', otherwise it will be 'ASC'

if (empty($dir) || $dir !== 'DESC') {

$dir = 'ASC';

}

답변 2

SQL 인젝션은 웹 개발자들에게 언제나 위협적인 문제 중 하나입니다. SQL 인젝션은 악의적인 사용자가 웹 사이트로 액세스하여 데이터베이스에 악용되는 공격 적인 방법입니다. 그러나 PHP를 사용하는 개발자라면 SQL 인젝션을 충분히 예방할 수 있습니다.

SQL 인젝션 공격은 입력 양식을 통해 자바스크립트 코드를 삽입하여 데이터베이스를 조작한 다음, 악용자의 의도에 따라 데이터를 변경하거나 삭제하거나 추가 할 수 있습니다. 이를 방지하기 위해서는 PHP에서 데이터 검증과 이스케이프 함수를 사용해야합니다.

데이터 검증은 사용자가 입력한 데이터가 우리가 예상 한 형식과 일치하는지 여부를 확인하는 것입니다. 예를 들어, 사용자가 이름과 성별을 입력해야하는 양식이 있다고 가정해보겠습니다. 이 때 비록 사용자가 '도'와 같은 불완전한 이름을 입력했을 경우, 그것은 우리가 예상한 형식에 맞지 않습니다. 이러한 경우, 프로그램은 그 값을 거절하고, 사용자가 값을 다시 입력하도록 요청해야합니다.

데이터 검증의 또 다른 방법은 제한된 문자열 문자에 대한 컨트롤을 포함하는 것입니다. 예를 들어, 사용자 이름에는 특수 문자가 포함되어서는 안됩니다. 따라서, 이름에 대한 입력 양식에서 사용자가 특수 문자를 입력하는 것은 우리가 원하지 않는 것입니다. 따라서 이름에 대한 입력 양식에서는 특수 문자를 거부하는 방법으로 검증을 수행할 수 있습니다.

데이터 검증은 SQL 인젝션을 방지하는 데 필수적인 부분이지만, 그것만으로는 충분하지 않습니다. 데이터 이스케이프 함수도 매우 중요한 방법 중 하나입니다. 이스케이프 함수는 사용자의 입력값에 포함 된 특수문자를 분해하고 그 값을 데이터베이스에 전송하기 전에 SQL 특수문자를 해당 특수문자로 대체합니다. 이를 통해 SQL 인젝션으로부터 데이터베이스를 보호할 수 있습니다.

또한, PHP5 이상의 버전에서는 PDO(PhpDataObject) 클래스를 사용하여 SQL 인젝션을 방지할 수 있습니다. PDO 클래스는 데이터베이스 연결 후 위험한 유형 변환을 자동으로 수행하며, 악의적인 사용자가 데이터베이스에 액세스하도록하는 웹 사이트를 보호합니다.

SQL 인젝션은 데이터 누출, 데이터베이스 위험, 상당한 손실을 야기하는 매우 위험한 공격 중 하나입니다. 하지만 적절한 방어책을 구축하고, 이를 유지하는 PHP 개발자라면 SQL 인젝션으로부터 시스템을 보호하고 안전한 웹 사이트를 더욱 쉽게 구축할 수 있습니다.

반응형
Comments