PHP에서 SQL 주입 공격을 방지하는 방법은 무엇입니까?
P粉939473759
2023-08-22 10:23:01
<p>사용자 입력이 수정 없이 SQL 쿼리에 삽입되면 다음 예와 같이 애플리케이션이 SQL 삽입 공격에 취약해집니다. </p>
<pre class="lang-php Prettyprint-override"><code>$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
<p>이는 사용자가 <code>value');-</code>와 같은 항목을 입력할 수 있기 때문입니다. </p>
<pre class="brush:php;toolbar:false;">INSERT INTO `table`(`column`) VALUES('value'); DROP TABLE table;--')
<p>이런 일이 발생하지 않도록 하려면 어떻게 해야 하나요? </p>
매개변수화된 쿼리를 사용하려면 Mysqli 또는 PDO를 사용해야 합니다. mysqli를 사용하여 예제를 다시 작성하려면 다음과 유사한 코드가 필요합니다.
으아아아읽고 싶은 주요 기능은
mysqli::prepare
입니다.또한 다른 사람들이 제안한 것처럼 PDO와 같은 더 높은 수준의 추상화 계층을 사용하는 것이 더 유용하고 쉽다는 것을 알 수 있습니다.
당신이 언급한 상황은 매우 간단하며, 더 복잡한 상황에서는 더 정교한 접근 방식이 필요할 수 있습니다. 특히:
mysql_real_escape_string
에 포함되지 않습니다. 이 경우 "안전한" 값만 허용되도록 사용자의 입력을 화이트리스트를 통해 전달하는 것이 좋습니다.어떤 데이터베이스를 사용하든 SQL 삽입 공격을 올바른방법은 SQL에서 데이터를 분리하여 데이터가 절대로 SQL 파서의 명령으로 해석되지 않는 데이터 형식으로 유지되도록 하는 것입니다. . 올바른 형식의 데이터 부분을 사용하여 SQL 문을 생성할 수 있지만 세부 사항을 완전히 이해하지 못하는 경우 항상 준비된 문과 매개변수화된 쿼리를 사용해야 합니다. 이는 매개변수와 별도로 전송되어 데이터베이스 서버에서 분석되는 SQL 문입니다. 이렇게 하면 공격자가 악성 SQL을 삽입할 수 없습니다.
이를 달성하는 방법에는 기본적으로 두 가지가 있습니다.
PDO 사용(지원되는 모든 데이터베이스 드라이버에서 작동):
으아아아MySQLi 사용(MySQL용):
으아아아PHP 8.2+부터
execute_query()
메소드를 사용하여 매개변수를 준비하고, 매개변수를 바인딩하고, SQL 문을 실행할 수 있습니다.PHP8.1 이전:
으아아아MySQL이 아닌 다른 데이터베이스에 연결하는 경우 두 번째 드라이버별 옵션을 참조할 수 있습니다(예를 들어 PostgreSQL의 경우
pg_prepare()
和pg_execute()
를 사용할 수 있음). PDO는 보편적인 옵션입니다.연결을 올바르게 설정하세요
PDO
MySQL 데이터베이스에 액세스하기 위해 PDO를 사용할 때 실제 준비된 명령문은 기본적으로 사용되지 않습니다. 이 문제를 해결하려면 준비된 문의 시뮬레이션을 비활성화해야 합니다. 다음은 PDO을 사용하여 연결을 생성하는 예입니다.
으아아아위의 예에서 오류 모드는 꼭 필요한 것은 아니지만 추가하는 것이 좋습니다 . 이렇게 하면 PDO가
PDOException
를 던져 모든 MySQL 오류를 알려줍니다.그러나 반드시 해야 하는 것은 PDO에게 시뮬레이션된 준비된 문을 비활성화하고
setAttribute()
실제 준비된 문을 사용하도록 지시하는 첫 번째 줄 입니다. 이렇게 하면 명령문과 값이 MySQL 서버로 전송되기 전에 PHP에서 구문 분석되지 않으므로 잠재적인 공격자가 악성 SQL을 삽입할 수 없습니다.생성자의 옵션에서 설정할 수 있지만
charset
PHP의 "이전" 버전(5.3.6 이전)은 DSN의 charset 매개변수 를 자동으로 무시한다는 점에 유의하는 것이 중요합니다.Mysqli
mysqli의 경우 동일한 루틴을 따라야 합니다.
으아아아설명
prepare
的SQL语句由数据库服务器解析和编译。通过指定参数(在上面的示例中,可以是?
或命名参数,如:name
),您告诉数据库引擎您要在哪里进行过滤。然后,当您调用execute
을 통과하면 준비된 문이 지정된 매개변수 값과 결합됩니다.여기서 중요한 점은 매개변수 값이 SQL 문자열이 아닌 컴파일된 문장과 결합된다는 점입니다. SQL 주입은 데이터베이스로 전송될 SQL을 생성할 때 스크립트를 속여 악성 문자열을 포함시키는 방식으로 작동합니다. 따라서 실제 SQL을 매개변수와 별도로 전송함으로써 예상치 못한 결과가 발생할 위험을 제한할 수 있습니다.
Prepared 문을 사용하여 전송된 모든 매개변수는 문자열로 처리됩니다(단, 데이터베이스 엔진이 매개변수를 일부 최적화할 수 있으므로 매개변수가 결국 숫자가 될 수 있음). 위의 예에서
$name
变量包含'Sarah'; DELETE FROM employees
,结果将仅是搜索字符串"'Sarah'; DELETE FROM employees"
하면 빈 테이블을 얻지 못할 것입니다.Prepared 문을 사용하는 또 다른 이점은 동일한 문이 동일한 세션에서 여러 번 실행되는 경우 한 번만 구문 분석되고 컴파일되므로 속도가 향상된다는 것입니다.
아, 인서트 작업 방법을 문의하셨기 때문에 다음은 예입니다(PDO 사용).
으아아아동적 쿼리에 준비된 명령문이 적합합니까?
쿼리 매개변수에 대해 준비된 문을 계속 사용할 수 있지만 동적 쿼리 자체의 구조는 매개변수화할 수 없으며 특정 쿼리 함수는 매개변수화할 수 없습니다.
이러한 특정 시나리오의 경우 화이트리스트 필터를 사용하여 가능한 값을 제한하는 것이 가장 좋습니다.
으아아아