>  기사  >  백엔드 개발  >  PHP Part 3에서 SQL 주입 공격을 완전히 금지합니다.

PHP Part 3에서 SQL 주입 공격을 완전히 금지합니다.

黄舟
黄舟원래의
2016-12-24 09:57:391093검색

1. 보안 추상화 계층 설정

위에서 설명한 기술을 각 사용자 입력 인스턴스에 수동으로 적용하는 것은 권장되지 않지만 이 목적을 위해 추상화 계층을 생성하는 것이 좋습니다. 간단한 추상화는 유효성 검사 계획을 함수에 추가하고 사용자가 입력한 각 항목에 대해 이 함수를 호출하는 것입니다. 물론 모든 애플리케이션에서 사용할 수 있는 클래스에 안전한 쿼리를 캡슐화하는 보다 복잡하고 높은 수준의 추상화를 만들 수도 있습니다. 온라인에서 이용할 수 있는 기성 무료 강좌가 많이 있으며, 이 기사에서는 그 중 일부에 대해 논의하겠습니다.

이 추상화에는 최소한 세 가지 이점이 있습니다(각각 보안 수준이 향상됩니다).

1. 현지화된 코드.

 2. 쿼리 구조를 더 빠르고 안정적으로 만듭니다. 이렇게 하면 일부 작업을 추상 코드로 오프로드할 수 있기 때문입니다.

 3. 보안 기능을 염두에 두고 구축하고 적절하게 적용하면 앞서 논의한 다양한 주입 공격을 효과적으로 방지할 수 있습니다.

2. 기존 애플리케이션 개선

기존 애플리케이션을 개선하려면 간단한 추상화 계층을 사용하는 것이 가장 적합합니다. 수집한 모든 사용자 입력을 단순히 '삭제'하는 함수는 다음과 같습니다:

function safe( $string ) {
return ''' . mysql_real_escape_string( $string ) . }
 【참고】값 요청과 mysql_real_escape_string() 함수에 해당하는 작은따옴표를 만들었습니다. 다음으로, 이 함수를 사용하여 다음과 같이 $query 변수를 생성할 수 있습니다.

$variety = safe( $_POST['variety'] );
$query = ' SELECT * FROM wines WHERE 다양성 =' . $variety;
이제 사용자는 변수 $variety의 값으로 다음을 입력하여 주입 공격을 시도합니다.

lagrein' 또는 1=1;
위의 '클리닝'이 수행되지 않으면 최종 쿼리는 다음과 같습니다(이로 인해 예측할 수 없는 결과가 발생합니다):

SELECT * FROM wines WHERE Variant = 'lagrein' or 1= 1;'
그러나 이제 사용자 입력이 지워졌으므로 쿼리 문은 다음과 같은 무해한 형식이 됩니다.

SELECT * FROM wines WHERE Variant = 'lagrein' or 1 =1;'

데이터베이스에 지정된 값에 해당하는 다양한 필드가 없기 때문에(정확히 악의적인 사용자가 입력한 것 - lagrein' 또는 1=1;) 이 쿼리는 작동하지 않습니다. 결과가 반환되지 않으며 주입이 실패합니다.

3. 새 애플리케이션 보호


새 애플리케이션을 생성하는 경우 보안 추상화 계층을 처음부터 생성할 수 있습니다. 이제 PHP 5의 새로 향상된 MySQL 지원(주로 새로운 mysqli 확장에 반영됨)은 이 보안 기능(절차적 및 객체 지향 모두)에 대한 강력한 지원을 제공합니다. mysqli에 대한 정보는 http://php.net/mysqli 사이트에서 얻을 수 있습니다. 이 mysqli 지원은 --with-mysqli=path/to/mysql_config 옵션을 사용하여 PHP를 컴파일하는 경우에만 사용할 수 있습니다. 다음은 mysqli 기반 쿼리를 보호하는 데 사용되는 이 코드의 절차적 버전입니다.



//사용자 입력 검색
$animalName = $_POST['animalName'];
//데이터베이스에 연결
$connect = mysqli_connect( ' localhost ', 'username', 'password', 'database' );
if ( !$connect ) exit( 'connection failed: ' . mysqli_connect_error() );
//쿼리문 소스 생성
$stmt = mysqli_prepare( $connect,'SELECT Intelligence FROM Animals WHERE name = ?' );
if ( $stmt ) {
//교환을 명령문에 바인딩
mysqli_stmt_bind_param( $stmt, 's ', $animalName );
//문 실행
mysqli_stmt_execute( $stmt );
//결과 검색...
mysqli_stmt_bind_result( $stmt, $intelligence ) // ...
if ( mysqli_stmt_fetch( $stmt ) ) {
print 'A $animalName has $intelligence Intelligence.n';
} else {
print '죄송합니다. 기록을 찾을 수 없습니다.';
위 스니펫에서는 사용자가 제출한 입력 콘텐츠가 먼저 수집되어 데이터베이스 연결이 설정됩니다. 그런 다음 mysqli_prepare() 함수를 사용하여 쿼리 소스를 생성합니다. 여기서는 이를 사용하는 함수의 이름을 반영하기 위해 $stmt라는 이름이 지정됩니다. 이 함수는 연결 리소스와 문자열이라는 두 가지 매개변수를 사용합니다(확장을 사용하여 값을 삽입할 때마다 '?' 표시가 삽입됩니다). 이 경우 해당 값은 동물 이름 하나만 갖습니다.

SELECT 문에서 '?' 표시를 배치할 수 있는 유일한 위치는 값 비교 부분입니다. 이것이 바로 적용할 변수를 지정할 필요가 없는 이유입니다(mysqli_stmt_bind_param() 함수 제외). 여기서는 해당 유형도 지정해야 합니다. 이 경우 's'는 문자열을 나타냅니다. 다른 가능한 유형은 정수의 경우 'I', double(또는 float)의 경우 'd', 이진 문자열의 경우 'b'입니다.

mysqli_stmt_execute(), mysqli_stmt_bind_result() 및 mysqli_stmt_fetch() 함수는 쿼리 실행 및 결과 검색을 담당합니다. 검색 결과가 있으면 표시하고, 결과가 없으면 무해한 메시지를 표시합니다. 마지막으로 $stmt 리소스와 데이터베이스 연결을 닫아 메모리에서 해제해야 합니다.

합법적인 사용자가 'lemming' 문자열을 입력한다고 가정하면 이 루틴은 (데이터베이스에 적절한 데이터가 있다고 가정하여) 'A lemming has 매우 낮은 지능을 가지고 있습니다.'라는 메시지를 출력합니다. 임시 주입이 있다고 가정하면(예: 'lemming' 또는 1=1;') 이 루틴은 '죄송합니다. 레코드를 찾을 수 없습니다.'라는 (무해한) 메시지를 인쇄합니다.
또한 mysqli 확장은 동일한 루틴의 객체 지향 버전도 제공합니다. 아래에서는 이 버전이 어떻게 적용되는지 명확히 설명하고자 합니다.

$animalName = $_POST['animalName'];
$mysqli = new mysqli( 'localhost', 'username', 'password', 'database');
if ( !$mysqli ) exit( '연결 실패: ' . mysqli_connect_error() );
$stmt = $mysqli->prepare( 'SELECT Intelligence


FROM 동물 WHERE 이름 = ?' );
if ( $stmt ) {
$stmt->bind_param( 's', $animalName );
$stmt->execute();
$stmt->bind_result( $intelligence );
if ( $stmt->fetch() ) {
print '$animalName에 $intelligence Intelligence.n이 있습니다.';

} else {

print '죄송합니다. 기록을 찾을 수 없습니다. .';
 }
 $stmt->close();
 }
$mysqli->close();
?>
 사실 이 부분의 코드는 앞서 설명한 내용 코드 복제 - 엄격한 절차적 코드가 아닌 객체 지향 구문 및 구성 접근 방식을 적용합니다.



4. 상위 수준 추상화

외부 라이브러리 PearDB를 사용하면 애플리케이션의 보안 모듈을 완전히 추상화할 수 있습니다.

반면에 이 라이브러리를 사용하는데 있어서 눈에 띄는 단점이 있습니다: 특정 사람들의 아이디어에만 제한을 받을 수 있고, 코드 관리에 많은 작업이 추가되었습니다. 이러한 이유로 적용 여부를 결정하기 전에 신중하게 생각해야 합니다. 이렇게 하기로 결정했다면 적어도 사용자 입력을 '정리'하는 데 실제로 도움이 되는지 확인하세요.

5. 주입 방지 기능 테스트

앞서 논의한 것처럼 스크립트 보안을 보장하는 중요한 부분은 스크립트를 테스트하는 것입니다. 이를 수행하는 가장 좋은 방법은 SQL 코드 주입 테스트를 직접 만드는 것입니다.
여기서 그러한 테스트의 예를 제공합니다. 이 예에서는 SELECT 문에 대한 주입 공격을 테스트합니다.

//테스트된 보호 기능
function safe( $string ) {
return ''' . mysql_real_escape_string( $string )
//데이터베이스에 연결
///////////////////////
//삽입 시도
//// // /////////////////
$exploit = ​​​​'lemming' AND 1=1;';
//청산
$safe = safe( $ Exploit );
$query = 'SELECT * FROM Animals WHERE name = $safe';
$result = mysql_query( $query );
//보호가 충분한지 테스트
if( $ result && mysql_num_rows( $result ) == 1 ) {
exit '보호 성공:n
exploit $exploit가 무력화되었습니다.';
}
else {
exit( '보호 실패: n
 exploit $exploit는 모든 행을 검색할 수 있었습니다.' );
}
?>
 이러한 테스트 세트를 생성하고 다양한 주입을 기반으로 다양한 SQL 명령을 실험하고 싶다면, 보호 전략의 허점을 빠르게 감지할 수 있습니다. 이러한 헤더를 수정하면 주입 공격에 대한 진정한 보호 기능을 구축했다고 확신할 수 있습니다.

6. 요약

이 기사 시리즈의 시작 부분에서 부적절한 사용자 입력으로 인해 발생하는 SQL 삽입 토론을 통해 스크립트에 대한 특정 위협을 분석했습니다. 그런 다음 SQL 주입이 어떻게 작동하는지 설명하고 PHP가 어떻게 쉽게 주입되는지 정확하게 분석했습니다. 그런 다음 실제 주입 사례를 제공합니다. 그 후, 주입 공격 시도를 무해하게 만들기 위한 일련의 조치를 권장합니다. 이는 제출된 모든 값이 따옴표로 묶여 있는지 확인하고, 사용자가 제출한 값의 유형을 확인하고, 사용자 입력을 필터링하는 것입니다. 위험한 캐릭터를 매복하는 등 고급 방법을 통해 달성됩니다. 마지막으로 유효성 검사 루틴을 추상화하고 기존 애플리케이션 변경을 위한 예제 스크립트를 제공하는 것이 좋습니다. 그런 다음 타사 추상화 솔루션의 장단점에 대해 논의했습니다.

위 내용은 PHP에서 SQL 주입 공격을 완전 금지하는 세 번째 내용입니다. 자세한 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!



성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.