programing

PHP에서 mysql_* 함수를 사용하면 안 되는 이유는 무엇입니까?

prostudy 2022. 9. 12. 10:38
반응형

PHP에서 mysql_* 함수를 사용하면 안 되는 이유는 무엇입니까?

사용해서는 안 되는 기술적 이유는 무엇입니까?mysql_*기능하고 있습니까?(예:mysql_query(),mysql_connect()또는mysql_real_escape_string())?

내 사이트에서 작동하는데 다른 것을 사용해야 하는 이유는 무엇입니까?

내 사이트에서 작동하지 않는 경우 다음과 같은 오류가 발생하는 이유는 무엇입니까?

경고: mysql_connect(): 해당 파일 또는 디렉토리가 없습니다.

MySQL 확장자:

  • 현재 개발 중이 아님
  • PHP 5.5(2013년 6월 출시)를 기점으로 공식적으로 폐지되었습니다.
  • PHP 7.0(2015년 12월 출시)에서 완전히 삭제되었습니다.
    • 즉, 2018년 12월 31일 현재 지원되는 버전의 PHP에는 존재하지 않습니다.이를 지원하는 PHP 버전을 사용하는 경우 보안 문제가 해결되지 않은 버전을 사용하는 것입니다.
  • OO 인터페이스가 없음
  • 지원하지 않음:
    • 비블로킹, 비동기 쿼리
    • 준비된 문 또는 매개 변수화된 쿼리
    • 저장 프로시저
    • 복수 스테이트먼트
    • 트랜잭션
    • "새로운" 비밀번호 인증 방식(MySQL 5.6에서는 기본 설정, 5.7에서는 필수)
    • MySQL 5.1 이후 신기능 중 하나

더 이상 사용되지 않기 때문에 이 기능을 사용하면 향후 코드 증명에 도움이 되지 않습니다.

준비된 스테이트먼트를 지원하지 않는 것이 특히 중요합니다.이것은, 다른 함수 호출에 의해서 수동으로 데이터를 이스케이프 하는 것보다, 외부 데이터를 이스케이프 해 견적하는, 보다 명확하고 에러가 발생하기 쉬운 방법을 제공하기 때문입니다.

SQL 확장비교를 참조하십시오.

PHP는 MySQL에 접속하기 위한 3가지 API를 제공합니다.(PHP 7에서 삭제됨), 및 확장입니다.

mysql_*예전에는 매우 인기가 많았지만, 이제는 그 사용이 권장되지 않습니다.문서 팀은 데이터베이스 보안 상황에 대해 논의하고 있으며, 일반적으로 사용되는 ext/mysql 확장자에서 벗어나도록 사용자를 교육하는 것도 그 일부입니다(ph.internals: decreating ext/mysql).

그리고 나중에 나온 PHP 개발자 팀은 사용자가 MySQL에 접속할 때 오류를 발생시키기로 결정했습니다.mysql_connect(),mysql_pconnect()또는 암묵적인 접속기능이 내장되어 있습니다.ext/mysql.

ext/mysql PHP 5.5에서 공식적으로 폐지되었으며 PHP 7에서 제거되었습니다.

빨간 상자 보여?

어떤 일을 할 때mysql_*기능 매뉴얼 페이지에 빨간색 상자가 표시되어 더 이상 사용하지 않아야 함을 설명합니다.

왜죠


에서 벗어나다ext/mysql보안뿐만 아니라 MySQL 데이터베이스의 모든 기능에 액세스할 수 있어야 합니다.

ext/mysqlMySQL 3.23용으로 구축되어 그 이후 거의 추가되지 않았지만 대부분 이전 버전과의 호환성을 유지하고 있어 코드를 유지하기가 다소 어렵습니다.에서 지원되지 않는 기능 누락ext/mysqlinclude: (PHP 매뉴얼 참조)

기능을 사용하지 않는 이유:

  • 현재 개발 중이 아님
  • PHP 7에서 삭제됨
  • OO 인터페이스가 없음
  • 비차단 비동기 쿼리를 지원하지 않습니다.
  • 준비된 문 또는 매개 변수화된 쿼리를 지원하지 않습니다.
  • 저장 프로시저를 지원하지 않습니다.
  • 여러 문을 지원하지 않습니다.
  • 트랜잭션을 지원하지 않습니다.
  • MySQL 5.1의 일부 기능을 지원하지 않음

쿠엔틴의 답변에서 인용한 상기 사항

준비된 스테이트먼트를 지원하지 않는 것이 특히 중요합니다.이는 다른 함수 호출을 사용하여 수동으로 데이터를 이스케이프하는 것보다 더 명확하고 오류가 발생하기 쉬운 외부 데이터 이스케이프 방법을 제공하기 때문입니다.

SQL 확장의 비교를 참조하십시오.


폐지 경고 억제

코드 변환 중MySQLi/PDO,E_DEPRECATED에러는, 설정함으로써 억제할 수 있습니다.error_reporting제외할 php.iniE_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

이것은 다른 권장 해제 경고도 숨겨지지만 MySQL 이외의 경고일 수 있습니다.(PHP 매뉴얼 참조)

PDO와 Dejan Marjanovic의 MySQLi: Which Should You Use?가 선택에 도움이 됩니다.

그리고 더 좋은 방법은PDO, 그리고 나는 지금 간단한 글을 쓰고 있다.PDO튜토리얼을 참조해 주세요.


간단하고 짧은 PDO 튜토리얼


Q. 첫 번째 의문점은 PDO가 무엇인가였다.

A. "PDO PHP Data Objects – 여러 데이터베이스에 대한 통일된 액세스 방법을 제공하는 데이터베이스 액세스 계층입니다."

alt text


MySQL에 연결

와 함께mysql_*또는 이전 방식으로 말할 수 있습니다(PHP 5.5 이상에서는 권장되지 않음).

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

와 함께PDO: 새로 작성하기만 하면 됩니다.PDO물건.생성자가 데이터베이스 소스를 지정하기 위한 매개 변수를 수락합니다.PDO의 컨스트럭터는 주로 다음 4개의 파라미터를 취합니다.DSN(데이터 소스 이름) 및 옵션username,password.

여기서 당신은 모든 것에 익숙하다고 생각합니다만,DSN의 새로운 기능PDO.aDSN기본적으로는 일련의 옵션입니다.PDO사용할 드라이버 및 연결 세부 정보.자세한 내용은 PDO MySQL DSN을 참조하십시오.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

주의: 다음을 사용할 수도 있습니다.charset=UTF-8단, 에러가 발생할 수 있으므로 를 사용하는 것이 좋습니다.utf8.

접속 에러가 발생했을 경우, 이 에러는PDOException잡을 수 있는 물건Exception더.

올바른 읽기: 연결연결 관리

여러 드라이버 옵션을 배열로 네 번째 매개 변수에 전달할 수도 있습니다.이 매개 변수를 전달할 것을 권장합니다.PDO예외 모드로 합니다.왜냐하면...PDO드라이버는 네이티브 준비 스테이트먼트를 지원하지 않기 때문에PDO는 준비 에뮬레이션을 수행합니다.이 에뮬레이션을 수동으로 실행할 수도 있습니다.네이티브 서버 측에서 준비된 문을 사용하려면 명시적으로 설정해야 합니다.false.

다른 하나는 에서 활성화되어 있는 준비 에뮬레이션을 끄는 것입니다.MySQL디폴트 드라이버입니다만, 에뮬레이션을 사용하려면 , 준비 에뮬레이션을 오프할 필요가 있습니다.PDO안전하게.

준비 에뮬레이션을 꺼야 하는 이유는 나중에 설명하겠습니다.이유를 찾으려면 이 게시물을 확인하십시오.

이전 버전의 를 사용하고 있는 경우에만 사용할 수 있습니다.MySQL추천하지 않습니다.

다음은 그 방법의 예입니다.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

PDO 시공 후 속성을 설정할 수 있습니까?

, PDO 구축 후 몇 가지 속성을 설정할 수도 있습니다.setAttribute방법:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

에러 처리


에러 처리의 용이성은PDO보다mysql_*.

사용하는 경우의 일반적인 방법mysql_*다음과 같습니다.

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die()에러를 처리하는 방법은 적절하지 않습니다.그것은, 에러에 대처할 수 없기 때문입니다.die스크립트가 갑자기 종료되고 보통 최종 사용자에게 보여주고 싶지 않은 오류가 화면에 반영되어 빌어먹을 해커들이 스키마를 발견하게 됩니다.또는 의 반환값mysql_*함수를 mysql_error()와 함께 사용하여 오류를 처리할 수 있습니다.

PDO는 예외라는 더 나은 해결책을 제공합니다.우리가 하는 모든 일PDO에 싸야 한다try-catch차단해, 강제로PDO에러 모드 Atribute를 설정함으로써, 3개의 에러 모드 중 하나로 이행합니다.3가지 에러 처리 모드를 다음에 나타냅니다.

  • PDO::ERRMODE_SILENT에러 코드를 설정했을 뿐이며, 동작은 다음과 같습니다.mysql_*각 결과를 확인하고 나서$db->errorInfo();에러의 상세를 취득합니다.
  • PDO::ERRMODE_WARNING올리다E_WARNING. (실행시 경고(치명적이지 않은 오류)스크립트의 실행은 정지되지 않습니다).
  • PDO::ERRMODE_EXCEPTION: 예외를 설정합니다.PDO에 의해 발생한 오류를 나타냅니다.던지면 안 돼요PDOException당신의 코드로요.PHP의 예외에 대한 자세한 내용은 예외를 참조하십시오.이 동작은 마치or die(mysql_error());잡히지 않을 때.하지만 그와는 달리or die(),그PDOException를 검출하여 적절하게 처리할 수 있습니다.

좋은 읽기:

예를 들어 다음과 같습니다.

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

그리고 당신은 그것을 포장할 수 있다.try-catch다음과 같습니다.

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

를 사용하여 처리할 필요가 없습니다.try-catch지금 당장.언제든지 적절한 시간에 잡을 수 있지만, 나는 당신이 그것을 사용하는 것을 강력히 추천한다.try-catch또, 콜 하는 함수의 외부로부터 캐치 하는 것이, 보다 타당할 가능성이 있습니다.PDO내용:

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

또한 다음 방법으로 처리할 수 있습니다.or die()혹은 이렇게 말할 수 있다.mysql_*하지만 정말 다양할 거예요.다음을 수행하여 프로덕션에서 위험한 오류 메시지를 숨길 수 있습니다.display_errors off에러 로그를 읽고 있습니다.

이제, 위의 모든 것을 읽고 나면, 당신은 아마 생각할 것이다: 내가 단지 단순하게 기대고 싶을 때, 대체 저게 뭐지?SELECT,INSERT,UPDATE, 또는DELETE스테이트먼트걱정 마세요. 여기 있습니다.


데이터 선택

PDO select image

그래서 지금 하고 있는 일은mysql_*다음과 같습니다.

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

자, 들어가겠습니다.PDO다음과 같이 할 수 있습니다.

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

또는

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

주의: 다음과 같은 방법을 사용하는 경우(query()), 이 메서드는PDOStatement물건.따라서 결과를 가져오려면 위와 같이 사용하십시오.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

PDO 데이터에서 이 정보는->fetch()스테이트먼트 핸들링의 메서드.fetch를 호출하기 전에 가장 좋은 방법은 PDO에 데이터 가져오기 방법을 알려주는 것입니다.아래 섹션에서는 이에 대해 설명합니다.

가져오기 모드

의 사용에 주의해 주세요.PDO::FETCH_ASSOC에서fetch()그리고.fetchAll()위의 코드이것이 말해준다PDO필드 이름을 키로 하여 행을 연관 배열로 반환합니다.그 밖에도 여러 가지 페치모드가 있어 하나하나 설명하겠습니다.

먼저 fetch 모드를 선택하는 방법을 설명합니다.

 $stmt->fetch(PDO::FETCH_ASSOC)

위에서는, 지금까지,fetch(). 다음 항목도 사용할 수 있습니다.

이제 가져오기 모드로 들어갑니다.

  • PDO::FETCH_ASSOC: 결과 세트에 반환된 대로 열 이름으로 인덱스된 어레이를 반환합니다.
  • PDO::FETCH_BOTH(기본값): 결과 집합에 반환된 대로 열 이름과 0-숫자 열 번호로 인덱싱된 배열이 반환됩니다.

더 많은 선택지가 있다!Fetch 설명서에서 이러한 모든 내용을 읽어보십시오.

개수 가져오기:

사용하는 대신mysql_num_rows반환된 행의 수를 얻으려면,PDOStatement하고 있다rowCount()예를 들어 다음과 같습니다.

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

마지막으로 삽입된 ID 가져오기

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

문 삽입 및 업데이트 또는 삭제

Insert and update PDO image

델이 하고 있는 일mysql_*기능은 다음과 같습니다.

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

pdo에서는 다음과 같은 작업을 수행할 수 있습니다.

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

위의 쿼리에서 SQL 문을 실행하고 영향을 받는 행 수를 반환합니다.

삽입 및 삭제에 대해서는 나중에 설명하겠습니다.

위의 방법은 쿼리에서 변수를 사용하지 않는 경우에만 유용합니다.단, 쿼리에서 변수를 사용해야 할 경우 위와 같이 prepared 스테이트먼트 또는 파라미터화된 스테이트먼트를 시도하지 마십시오.


준비된 스테이트먼트

Q. 준비PHP에서 mysql_* 함수를 사용하면 안 되는 이유는 무엇입니까?된 진술서는 무엇이며 왜 필요한가?
A. 준비된 문은 미리 컴파일된 SQL 문을 말하며, 서버에 데이터만 전송하여 여러 번 실행할 수 있습니다.

준비된 스테이트먼트를 사용하는 일반적인 워크플로는 다음과 같습니다(Wikipedia 3에서 인용).

  1. 준비:스테이트먼트 템플릿은 어플리케이션에 의해 작성되어 Database Management System(DBMS; 데이터베이스 관리 시스템)으로 전송됩니다.파라미터, 플레이스 홀더 또는 바인드 변수(라벨 부착)라고 불리는 특정 값은 지정되지 않은 상태로 남습니다.?이하에 나타냅니다.

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. DBMS는 문 템플릿을 구문 분석, 컴파일 및 쿼리 최적화를 수행하고 결과를 실행하지 않고 저장합니다.

  3. 실행:나중에 응용 프로그램은 파라미터의 값을 제공하고(또는 바인드), DBMS는 문을 실행합니다(결과를 반환할 수 있습니다).응용 프로그램은 다른 값을 사용하여 원하는 횟수만큼 문을 실행할 수 있습니다.이 예에서는 첫 번째 파라미터에 'Bread'를 제공할 수 있습니다.1.00두 번째 파라미터로 설정합니다.

SQL에 자리 표시자를 포함하여 준비된 문을 사용할 수 있습니다.기본적으로 플레이스 홀더가 없는 것은 세 가지(위의 변수와 함께 시도하지 않음), 이름 없는 플레이스 홀더가 있는 플레이스 홀더와 이름 있는 플레이스 홀더가 있는 플레이스 홀더가 있는 플레이스 홀더가 있습니다.

Q. 그럼 플레이스 홀더란 무엇이며 어떻게 사용해야 합니까?
A. 이름 있는 플레이스 홀더.물음표 대신 콜론 앞에 설명적인 이름을 사용합니다.이름 자리 보유자의 위치/값 순서는 상관없습니다.

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

실행 어레이를 사용하여 바인드할 수도 있습니다.

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

또 하나의 뛰어난 기능OOPfriends는 지정된 플레이스홀더가 지정된 필드와 속성이 일치한다고 가정할 때 데이터베이스에 오브젝트를 직접 삽입할 수 있는 기능입니다.예를 들어 다음과 같습니다.

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

Q. 그럼 이름 없는 플레이스 홀더는 무엇이며 어떻게 사용해야 합니까?
A. 예를 들어 보겠습니다.

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

그리고.

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

위에서 볼 수 있습니다.?네임플레이스 홀더에 있는 이름 대신.첫 번째 예에서는 다양한 플레이스 홀더에 변수를 할당합니다($stmt->bindValue(1, $name, PDO::PARAM_STR);그런 다음 플레이스 홀더에 값을 할당하고 문을 실행합니다.두 번째 예에서는 첫 번째 배열 요소가 첫 번째 배열 요소로 이동합니다.?그리고 두 번째에서 두 번째까지?.

메모: 이름 없는 플레이스 홀더에서는 어레이 내에서 전달되는 요소의 올바른 순서를 관리할 필요가 있습니다.PDOStatement::execute()방법.


SELECT,INSERT,UPDATE,DELETE준비된 쿼리

  1. 다음과 같습니다SELECT.

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. 다음과 같습니다INSERT.

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. 다음과 같습니다DELETE.

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. 다음과 같습니다UPDATE.

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

주의:

하지만PDO및/또는MySQLi완전히 안전하지 않습니다."PDO 준비 문장은 SQL 주입을 방지하기에 충분한가?"라는 답변을 확인합니다.ircmaxell에 의해.또, 나는 그의 대답의 일부를 인용하고 있다.

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));

먼저, 모든 고객에게 제공하는 표준적인 코멘트부터 시작하겠습니다.

새로운 코드의 함수는 사용하지 말아 주세요.그것들은 더 이상 유지되지 않고 공식적으로 폐지된다.빨간 상자 보이지?대신 준비된 문장에 대해 알아보고 PDO 또는 MySQLi사용하십시오. 이 문서는 어떤 문장이 어떤 것인지 결정하는 데 도움이 됩니다.PDO를 선택한 경우 다음 튜토리얼을 참고하십시오.

그럼 한 문장 한 문장씩 살펴보고 설명하겠습니다.

  • 그것들은 더 이상 유지되지 않고 공식적으로 폐지된다.

    이는 PHP 커뮤니티가 이러한 매우 오래된 기능에 대한 지원을 점차 중단하고 있음을 의미합니다.PHP의 미래(최근) 버전에는 존재하지 않을 수 있습니다!이러한 기능을 계속 사용하면 앞으로 코드가 깨질 수 있습니다(그렇지 않을 수도 있습니다.

    신규! - PHP 5.5에서 ext/mysql은 공식적으로 폐지되었습니다!

    최신! ext/mysql이 PHP 7에서 제거되었습니다.

  • 대신, 당신은 준비된 문장을 배워야 합니다.

    mysql_*extension은 SQL Injection에 대한 매우 효과적인 대책인 준비된 문을 지원하지 않습니다.이를 통해 MySQL 종속 응용 프로그램의 매우 심각한 취약성을 수정하여 공격자가 스크립트에 액세스하고 데이터베이스에서 가능한 쿼리를 수행할 수 있습니다.

    자세한 내용은 PHP에서 SQL 주입을 방지하는 방법을 참조하십시오.

  • 빨간 상자 보여?

    아무데나 가면mysql기능 매뉴얼 페이지에 빨간색 상자가 표시되어 더 이상 사용하지 않아야 함을 설명합니다.

  • PDO 또는 MySQLi 사용

    데이터베이스 상호 작용에 대한 완전한 OOP 접근 방식을 제공하는 PDO - PHP Database Object와 MySQL 고유의 개선점인 MySQLi 등 보다 우수하고 견고하며 잘 구축된 대안이 있습니다.

사용의 용이성

분석적 이유와 종합적 이유는 이미 언급되었다.새로 온 사람에게는 날짜 mysql_ 함수의 사용을 중지하는 것이 더 큰 인센티브입니다.

최신 데이터베이스 API는 사용하기가 더 쉬울 뿐입니다.

코드를 단순화할 수 있는 것은 대부분 바운드 파라미터입니다.또, 뛰어난 튜토리얼(상기 참조)을 사용하고 있기 때문에, PDO로의 이행은 그다지 어렵지 않습니다.

단, 한 번에 더 큰 코드 베이스를 다시 쓰려면 시간이 걸립니다.이 중간 대안을 위한 Raison d'étre:

mysql_* 대신 동등한 pdo_* 함수

<pdo_mysql.php>를 사용하면 최소한의 노력으로 오래된 mysql_ 함수에서 전환할 수 있습니다.가세하다pdo_기능 래퍼를 대체하다mysql_상대편

  1. 간단하게include_once("pdo_mysql.php");데이터베이스와 상호 작용해야 하는 각 호출 스크립트에서 사용됩니다.

  2. 함수 프리픽스를 삭제하고 로 교체합니다.

    • mysql_connect()가 되다connect()
    • mysql_query()가 되다query()
    • mysql_num_rows()가 되다num_rows()
    • mysql_insert_id()가 되다insert_id()
    • mysql_fetch_array()가 되다fetch_array()
    • mysql_fetch_assoc()가 되다fetch_assoc()
    • mysql_real_escape_string()가 되다real_escape_string()
    • 기타 등등...

  3. 코드는 동일하게 동작하지만 대부분 동일하게 표시됩니다.

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

엣보일라
코드가 PDO를 사용하고 있습니다.
이제 실제로 활용할 때입니다.

바운드 파라미터의 사용이 용이함

덜 다루기 쉬운 API만 있으면 됩니다.

pdo_query()는 바인딩된 파라미터에 대한 매우 쉬운 지원을 추가합니다.이전 코드 변환은 간단합니다.

SQL 문자열에서 변수를 이동합니다.

  • 이들을 콤마로 구분된 함수 파라미터로 추가합니다.pdo_query().
  • 물음표 배치?자리 표시자로 사용할 수 있습니다.
  • 제거하다'이전에 문자열 값 또는 문자열을 묶은 작은 따옴표.

코드의 길이가 길수록 이점이 더욱 뚜렷해집니다.

대부분의 문자열 변수는 SQL에 보간될 뿐만 아니라 중간에 이스케이프 콜과 연결됩니다.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

와 함께?플레이스 홀더 신청은 번거롭지 않습니다.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

pdo_*는 여전히 또는 둘 중 하나를 허용합니다.
변수를 이스케이프하지 말고 동일한 쿼리에 바인딩하십시오.

  • 플레이스 홀더 기능은 플레이스 홀더 뒤에 있는 실제 PDO에 의해 제공됩니다.
  • 그러므로 또한 허용된다.:named나중에 플레이스 홀더 리스트를 참조해 주세요.

더 중요한 것은 $_REQUEST[] 변수를 쿼리 뒤에 안전하게 전달할 수 있다는 것입니다.제출 시<form>필드는 데이터베이스 구조와 정확히 일치합니다.

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

너무 단순해요.하지만 왜 제거하거나 탈출하고 싶은지에 대한 몇 가지 조언과 기술적인 이유로 돌아가 봅시다.

구식 기능 수정 또는 제거

모든 콜을 로 변환하면pdo_query바인드된 매개 변수를 사용하여 모든 중복 제거pdo_real_escape_string콜을 클릭합니다.

특히 수정해야 합니다.sanitize또는clean또는filterThis또는clean_data는 다음 중 하나의 형식으로 날짜가 지난 튜토리얼에 의해 애드버타이즈된 기능을 합니다.

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

여기서 가장 눈에 띄는 문제는 문서화의 부족이다.더 중요한 것은 필터링 순서가 정확히 잘못되었다는 것입니다.

  • 올바른 순서는 다음과 같습니다: 권장되지 않음stripslashes그럼 제일 안쪽의 전화로서trim, 그 후strip_tags,htmlentities출력 콘텍스트의 경우,_escape_string응용 프로그램이 SQL 인터스패싱에 직접 선행해야 하기 때문입니다.

  • 하지만 첫 번째 단계로 을 제거합니다.

  • 나머지 부분은 보관해야 할 수도 있습니다.sanitize()데이터베이스와 어플리케이션플로우가 HTML 컨텍스트 세이프 스트링을 요구하고 있는 경우는, 현시점에서는 기능합니다.이후 HTML 이스케이프만 적용된다는 주석을 추가합니다.

  • 문자열/값 처리는 PDO 및 해당 매개 변수화된 문에 위임됩니다.

  • 에 대한 언급이 있었다면stripslashes()sanitize 기능에서는 더 높은 수준의 감시를 나타낼 수 있습니다.

    • 그것은 일반적으로 폐지된 로부터의 파손(이중 탈출)을 복구하기 위해 있었다.단, 스트링별로가 아니라 중앙에서 수정하는 것이 가장 좋습니다.

    • 사용자 랜드 반전 접근법 중 하나를 사용합니다.그런 다음 를 삭제합니다.stripslashes()에서sanitize기능.

    magic_quotes의 이력 노트.그 기능은 당연히 권장되지 않는다.그러나 보안 기능 장애로 잘못 묘사되는 경우가 많습니다.그러나 magic_quotes는 테니스공이 영양 공급원만큼 실패한 보안 기능입니다.그것은 단순히 그들의 목적이 아니었다.

    PHP2/FI의 원래 구현에서는 "인용어가 자동으로 이스케이프되므로 데이터를 msql 쿼리에 직접 전달하기가 쉬워집니다."라고 명시되어 있습니다.특히 ASCII만 지원하므로 mSQL에서 사용하는 것은 우발적으로 안전합니다.
    그 후 PHP3/Zend는 MySQL용 magic_quotes를 재도입하여 잘못 문서화하였습니다.그러나 원래 이것은 보안을 목적으로 한 것이 아니라 편의 기능일 뿐입니다.

준비된 진술이 어떻게 다른가

문자열 변수를 SQL 쿼리에 스크램블하면 더 복잡해질 뿐만 아니라MySQL은 코드와 데이터를 다시 분리하는 작업도 불필요합니다.

SQL 주입은 단순히 데이터가 코드 컨텍스트에 블리딩되는 경우입니다.데이터베이스 서버는 나중에 쿼리 구 사이에 원래 PHP가 붙어 있던 변수를 찾을 수 없습니다.

경계 매개 변수를 사용하여 PHP 코드에서 SQL 코드와 SQL 컨텍스트 값을 구분합니다.그러나 이 문제는 뒷전에서는 다시 일어나지 않습니다(PDO 제외:에뮬레이트_PREPARES).데이터베이스는 변경되지 않은 SQL 명령과 1:1 변수 값을 수신합니다.

이 답변에서는 drop의 가독성 이점에 유의해야 한다고 강조합니다.이러한 가시적이고 기술적인 데이터/코드 분리에 의해 퍼포먼스가 향상되는 경우가 있습니다(INSERT의 값이 다른 반복).

파라미터 바인딩은 여전히 모든 SQL 주입에 대한 마법의 원스톱 솔루션이 아닙니다.데이터/값의 가장 일반적인 용도를 처리합니다.그러나 열 이름/테이블 식별자, 동적 절 구성 또는 일반 배열 값 목록만 화이트리스트로 만들 수는 없습니다.

하이브리드 PDO 사용

이것들pdo_*wrapper 함수는 코딩 친화적인 임시 API를 만듭니다.MYSQLI특이한 기능 시그니처 시프트가 없었다면 가능했을 것입니다).또한 대부분의 경우 실제 PDO를 노출합니다.
새 pdo_ 함수 이름을 사용하여 다시 쓸 필요가 없습니다.각 pdo_query()를 플레인 $pdo->prepare()->execute()콜로 하나씩 이행할 수 있습니다.

그러나 다시 단순화하는 것이 가장 좋습니다.예를 들어 일반적인 결과 가져오기:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

포어치 반복만으로 대체할 수 있습니다.

foreach ($result as $row) {

또는 직접적이고 완전한 어레이 취득:

$result->fetchAll();

대부분의 경우 쿼리 실패 후 PDO 또는 mysql_보다 더 유용한 경고가 표시됩니다.

기타 옵션

그래서 이것은 희망적으로 몇 가지 실제적인 이유와 떨어뜨리는 가치 있는 경로를 시각화했다.

로 바꾸는 것만으로는 충분치 않아요. pdo_query()앞부분일 뿐이죠.

파라미터 바인딩을 도입하거나 보다 좋은 API에서 다른 것을 이용할 수 없는 한 의미 없는 스위치입니다.새내기에게 더 이상 낙담하지 않을 정도로 단순하게 묘사되었으면 좋겠다. (보통 교육은 금지보다 효과가 있다.)

가장 간단한 작업 범주에 해당하지만 여전히 매우 실험적인 코드입니다.주말에 썼어.하지만 너무 많은 대안이 있다.PHP 데이터베이스 추상화를 구글에서 검색하여 조금만 참조하면 됩니다.이러한 작업을 위한 훌륭한 도서관은 항상 존재해 왔고 앞으로도 존재하게 될 것입니다.

데이터베이스 상호 작용을 더욱 단순화하고 싶다면 Paris/Idiorm과 같은 매핑을 시도해 볼 만하다.JavaScript에서 아무도 DOM을 사용하지 않는 것처럼, 오늘날에는 원시 데이터베이스 인터페이스를 돌볼 필요가 없습니다.

mysql_기능:

  1. 구식 - 더 이상 유지보수가 되지 않습니다.
  2. 다른 데이터베이스 백엔드로 쉽게 이동할 수 없음
  3. 준비된 스테이트먼트를 지원하지 않기 때문에
  4. 프로그래머가 쿼리를 작성하기 위해 연결을 사용하도록 권장하여 SQL 주입 취약성을 야기합니다.

기술적인 이유로 말하자면, 극히 구체적이고 거의 사용되지 않는 몇 가지 항목만 있습니다.아마도 당신은 평생 그것들을 사용하지 않을 것이다.
내가 너무 무식한 건지도 모르지만, 나는 그것들을 사용할 기회가 없었다.

  • 비동기 쿼리
  • 저장 프로시저에서 여러 결과 집합을 반환
  • 암호화(SSL)
  • 압축

필요한 경우 mysql 확장에서 좀 더 스타일리시하고 모던한 디자인으로 전환해야 하는 기술적 이유임에 틀림없습니다.

다만, 기술적인 문제가 아닌 것도 있기 때문에, 조작이 조금 어려워질 가능성이 있습니다.

  • 이러한 기능을 최신 PHP 버전에서 더 이상 사용하면 사용되지 않는 수준의 알림이 발생합니다.간단히 끌 수 있습니다.
  • 머지않아 디폴트 PHP 빌드에서 삭제될 가능성이 있습니다.mydsql ext가 PECL로 이동되고 모든 호스트들이 PHP를 컴파일하는 것을 기뻐할 것이기 때문에 큰 문제는 아닙니다.왜냐하면 그들은 수십 년 동안 사이트가 작동하던 클라이언트를 잃고 싶지 않기 때문입니다.
  • Stackoverflow 커뮤니티로부터의 강력한 저항입니다.③이러한 기능을 언급할 때마다 엄격한 금기시되고 있다는 말을 듣게 된다.
  • 평균적인 PHP 사용자이기 때문에 이러한 함수를 사용하는 당신의 생각은 오류가 발생하기 쉽고 잘못된 것일 수 있습니다.단지 잘못된 방법을 가르쳐 주는 수많은 튜토리얼과 매뉴얼 때문에.기능 자체가 아니라 기능 사용법입니다.

이 후자의 문제가 있습니다.
하지만, 제 생각에는, 제안된 해결책도 더 나을 것이 없습니다.
모든 PHP 사용자가 SQL 쿼리를 한 번에 적절하게 처리하는 방법을 배운다는 것은 너무 이상주의적인 꿈인 것 같습니다.대부분의 경우 mysql_*을 mysqli_*로 기계적으로 변경하여 접근 방식을 그대로 유지합니다.특히 mysqli는 준비된 문장을 사용하는 것을 엄청나게 고통스럽고 성가시게 만들기 때문입니다.
기본 준비 문장은 SQL 주입으로부터 보호하기에 충분하지 않으며 mysqli도 PDO도 솔루션을 제공하지 않습니다.

그래서 나는 이 정직한 연장선에 맞서 싸우기보다는 잘못된 관행에 맞서 싸우고 올바른 방법으로 사람들을 교육하는 것을 선호합니다.

또한, 잘못된 이유나 중요하지 않은 이유들이 있습니다.

  • 스토어드 프로시저를 지원하지 않습니다(사용하고 있습니다.mysql_query("CALL my_proc");장기간에 걸쳐)
  • 트랜잭션을 지원하지 않습니다(위와 동일).
  • 다중 스테이트먼트를 지원하지 않습니다(필요한 사용자)
  • 현재 개발 중이 아님(그래서? 실제적인 방식으로 영향을 미칩니까?)
  • OO 인터페이스가 없음(생성하는 데 몇 시간이 소요됨)
  • 준비된 문 또는 매개 변수화된 쿼리를 지원하지 않습니다.

마지막은 흥미로운 점이다.mysql ext는 네이티브 준비 문을 지원하지 않지만 안전을 위해 필요하지 않습니다.PDO와 마찬가지로 수동으로 처리되는 플레이스 홀더를 사용하여 준비된 문장을 쉽게 위조할 수 있습니다.

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

, 모든 게 파라미터화 되어 있고 안전해

하지만 설명서의 빨간색 상자가 마음에 들지 않으면 mysqli와 PDO 중 선택할 수 있는 문제가 발생합니다.

답은 다음과 같습니다.

  • 데이터베이스 추상화 레이어를 사용하여 API를 작성해야 하는 필요성을 이해한다면 mysqli는 실제로 많은 mysql 고유 기능을 지원하므로 매우 좋은 선택입니다.
  • 대부분의 PHP 사용자와 마찬가지로 애플리케이션 코드에서 raw API 호출을 사용하는 경우(이것은 본질적으로 잘못된 관행입니다), PDO가 유일한 선택입니다.이 확장자는 API뿐만 아니라 세미 DAL인 것처럼 가장하고, 아직 불완전하지만 많은 중요한 기능을 제공하므로 PDO는 mysqli와 매우 구별됩니다.

    • mysqli와 달리 PDO는 플레이스 홀더를 값별로 바인드할 수 있기 때문에 매우 복잡한 코드의 여러 화면 없이도 동적으로 구축된 쿼리를 실행할 수 있습니다.
    • mysqli와 달리 PDO는 항상 단순한 일반 배열로 쿼리 결과를 반환할 수 있지만 mysqli는 mysqlnd 설치에서만 쿼리 결과를 반환할 수 있습니다.

따라서, 당신이 평균적인 PHP 사용자이고 네이티브로 준비된 문을 사용할 때 많은 수고를 덜어주고 싶다면, PDO가 유일한 선택입니다.
하지만 PDO도 결코 쉬운 일이 아니며 어려움이 있다.
PDO 태그 Wiki의 일반적인 함정과 복잡한 사례에 대한 해결책을 작성했습니다.

그럼에도 불구하고 확장에 대해 이야기하는 모든 사람들은 Mysqli와 PDO에 대한 두 가지 중요한 사실을 항상 놓치고 있습니다.

  1. 준비된 진술은 약발이 아니야준비된 문을 사용하여 바인딩할 수 없는 동적 식별자가 있습니다.파라미터의 수를 알 수 없는 동적 쿼리가 있어 쿼리 구축이 어렵습니다.

  2. mysqli_* 와 PDO 의 어느 함수도 애플리케이션 코드에 표시되어 있지 않습니다.
    이러한 코드와 애플리케이션 코드 사이에는 추상화 레이어가 존재해야 합니다.이 레이어는 내부에서 바인딩, 루프, 에러 처리 등의 더러운 작업을 모두 수행하여 애플리케이션 코드를 건조하고 깔끔하게 만듭니다.특히 동적 쿼리 구축과 같은 복잡한 경우에는 더욱 그렇습니다.

따라서 PDO 또는 mysqli로 전환하는 것만으로는 충분하지 않습니다.코드 내에서 원시 API 함수를 호출하는 대신 ORM이나 쿼리 빌더 또는 데이터베이스 추상화 클래스를 사용해야 합니다.
반대로 어플리케이션코드와 mysql API 사이에 추상화 레이어가 있다면 실제로 어떤 엔진을 사용하는지는 중요하지 않습니다.mysql ext는 폐지될 때까지 사용할 수 있습니다.그러면 모든 어플리케이션코드를 그대로 유지한 채 추상화 클래스를 다른 엔진에 쉽게 다시 쓸 있습니다.

다음은 safemysql 클래스에 기반한 몇 가지 예시로 이러한 추상화 클래스가 어떻게 되어야 하는지를 보여 줍니다.

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

이 한 줄과 PDO에 필요한 코드의 양을 비교합니다.
그런 다음 Mysqli가 작성한 미가공 스테이트먼트와 함께 필요한 엄청난 양의 코드와 비교해 보십시오.오류 처리, 프로파일링, 쿼리 로깅이 이미 내장되어 실행 중임을 유의하십시오.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

모든 필드 이름이 6~10회 반복될 때 일반 PDO 삽입과 비교해 보십시오. 이름 있는 플레이스 홀더, 바인딩 및 쿼리 정의에서 이 모든 필드 이름이 6~10회 반복됩니다.

또 다른 예는 다음과 같습니다.

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

PDO가 이러한 실제 사례를 처리하는 예는 거의 없습니다.
그리고 그것은 너무 말이 많고 아마 안전하지 않을 것이다.

다시 한 번 말씀드리지만, 이 드라이버는 단순한 문제가 아니라 추상화 수업이어야 합니다.초보자 매뉴얼에 기재되어 있는 바보 같은 예뿐만 아니라 실제 문제를 해결하는 데 도움이 됩니다.

여러 가지 이유가 있지만 아마도 가장 중요한 이유는 이러한 함수가 준비된 문장을 지원하지 않기 때문에 안전하지 않은 프로그래밍 관행을 장려하기 때문일 것입니다.준비된 문장은 SQL 주입 공격을 방지하는 데 도움이 됩니다.

사용시mysql_*사용자가 지정한 파라미터를 실행하는 것을 기억해야 합니다.mysql_real_escape_string()한 곳에서만 잊어버리거나 입력의 일부만 이스케이프할 경우 데이터베이스가 공격을 받을 수 있습니다.

에서 준비된 스테이트먼트의 사용PDO또는mysqli이런 종류의 프로그래밍 오류를 만드는 것이 더 어려워질 것입니다.

왜냐하면 (다른 이유 중에서도) 입력 데이터를 확실하게 삭제하기가 훨씬 어렵기 때문입니다.PDO 또는 mysqli와 같이 매개 변수화된 쿼리를 사용하면 위험을 완전히 피할 수 있습니다.

예를 들어, 누군가는"enhzflep); drop table users"사용자명으로 지정합니다.이전 함수에서는 쿼리당 여러 개의 문을 실행할 수 있으므로, 이 성가신 버거와 같은 것은 테이블 전체를 삭제할 수 있습니다.

mysqli의 PDO를 사용하는 경우 사용자 이름은 다음과 같습니다."enhzflep); drop table users".

bobby-tables.com 를 참조해 주세요.

이 답변은 PHP 사용자 검증 코드를 잘못 작성하는 것이 얼마나 간단한지, 이러한 공격이 어떻게 동작하는지(그리고 무엇을 사용하는지), 오래된 MySQL 함수를 안전한 준비된 스테이트먼트로 대체하는 방법, 그리고 기본적으로 StackOverflow 사용자가 새로운 사용자에게 개선 질문을 하는 이유를 보여주기 위해 작성되었습니다.코드를 설정합니다.

우선, 이 테스트 mysql 데이터베이스를 작성해 주세요(저는 mine prep이라고 부릅니다).

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

그러면 PHP 코드로 이동할 수 있습니다.

다음 스크립트가 웹 사이트 관리자 검증 프로세스라고 가정합니다(간단하지만 테스트에 복사하여 사용하는 경우 작업합니다).

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    
    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

얼핏 보면 충분히 합법적으로 보이는군

사용자는 로그인 및 비밀번호를 입력해야 합니다.

이제 다음을 입력하십시오.

user: bob
pass: somePass

제출해 주세요.

출력은 다음과 같습니다.

You could not be verified. Please try again...

Super! 예상대로 동작하고 있습니다.실제 사용자 이름과 패스워드를 사용해 보겠습니다.

user: Fluffeh
pass: mypass

최고야!하이파이브를 하면, 코드가 admin을 올바르게 검증합니다.완벽해!

글쎄, 그렇진 않아.사용자가 영리한 작은 사람이라고 가정해 봅시다.그 사람이 나라고 합시다.

다음과 같이 입력합니다.

user: bob
pass: n' or 1=1 or 'm=m

출력은 다음과 같습니다.

The check passed. We have a verified admin!

축하합니다. 방금 제가 잘못된 사용자 이름과 잘못된 암호를 입력하는 슈퍼 프로텍트 관리자 전용 섹션에 들어갈 수 있게 해주셨습니다.정말로, 만약 당신이 나를 믿지 않는다면, 내가 제공한 코드로 데이터베이스를 만들고, 이 PHP 코드를 실행하세요.얼핏 보면 사용자 이름과 패스워드가 꽤 잘 확인되고 있는 것처럼 보입니다.

그 대답으로, 그게 당신이 소리를 지르는 이유에요

뭐가 잘못됐는지, 왜 내가 방금 슈퍼 관리자 전용 배트 케이브에 들어갔는지 살펴보자.저는 추측을 했습니다만, 당신이 입력에 주의를 기울이지 않고, 단지 직접 데이터베이스에 전달했을 뿐입니다.실제로 실행 중인 쿼리를 변경할 수 있도록 입력을 구성했습니다.그래, 그게 뭐였어야 했는데, 결국엔 뭐가 된거야?

select id, userid, pass from users where userid='$user' and pass='$pass'

이것이 쿼리입니다만, 변수를 사용한 실제 입력으로 치환하면 다음과 같은 결과가 나옵니다.

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

먼저 암호 주변의 단일 따옴표를 닫은 다음 완전히 새로운 비교를 소개하도록 "비밀번호"를 구성한 방법을 보십시오.그리고 안전을 위해서, 나는 우리가 원래 가지고 있던 코드에서 예상대로 작은 따옴표가 닫힐 수 있도록 다른 "문자열"을 추가했다.

하지만 지금은 사람들이 소리치는 것이 아니라 코드를 더 안전하게 만드는 방법을 보여 주는 것입니다.

그럼 뭐가 잘못됐나요? 어떻게 고칠 수 있을까요?

이것은 전형적인 SQL 주입 공격입니다.가장 간단한 문제 중 하나지공격 벡터의 척도로 볼 때, 이것은 유아가 탱크를 공격하여 이기는 것입니다.

그럼 어떻게 하면 신성한 관리 구역을 보호하고 안전하게 만들 수 있을까요?가장 먼저 해야 할 일은 정말 낡고 비천한 것들을 그만 사용하는 것이다.mysql_*기능들.온라인에서 찾은 튜토리얼을 따라다니면서 작동하긴 했지만 오래되고 구식이고 몇 분 만에 땀도 흘리지 않고 그냥 지나쳐 버렸어요.

이제 mysqli_ 또는 PDO를 사용할 수 있습니다.저는 개인적으로 PDO의 열렬한 팬이기 때문에 이 답변에서 PDO를 사용할 것입니다.찬반 양론이 있지만 개인적으로 찬반 의견이 반대 의견보다 훨씬 우세하다는 것을 알게 되었다.MySQL을 사용하든 Oracle을 사용하든 무엇이든 간에 여러 데이터베이스 엔진 간에 휴대할 수 있습니다. 연결 스트링을 변경하는 것만으로 우리가 사용하고 싶은 멋진 기능을 모두 갖추고 있고 깔끔합니다.난 깨끗한 게 좋아.

이번에는 PDO 개체를 사용하여 작성된 코드를 다시 살펴보겠습니다.

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;
    
    $database='prep';
    $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }
    
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

주요 차이점은 더 이상 존재하지 않는다는 것이다.mysql_*기능들.이 모든 작업은 PDO 개체를 통해 수행됩니다. 둘째, 준비된 스테이트먼트를 사용합니다.자, 이제, 당신이 질문하는 준비된 진술은 무엇입니까?쿼리를 실행하기 전에 데이터베이스에 실행할 쿼리가 무엇인지 알려주는 방법입니다.이 경우 데이터베이스에 "안녕하세요, userid 및 테이블 사용자로부터의 패스(userid가 변수이고 패스도 변수)를 원하는 select 문을 실행합니다."라고 말합니다.

그런 다음 execute 스테이트먼트에서는 현재 예상되는 모든 변수를 포함하는 배열을 데이터베이스에 전달합니다.

결과는 환상적이다.이전 사용자 이름과 비밀번호 조합을 다시 시도합니다.

user: bob
pass: somePass

사용자가 확인되지 않았습니다.신난다.

그럼 어떻게 해?

user: Fluffeh
pass: mypass

오, 그냥 좀 흥분했었어, 효과가 있었어.수표는 통과했다.검증된 관리자가 있습니다!

이제 우리의 작은 검증 시스템을 통과하기 위해 영리한 chap이 입력하는 데이터를 시험해 보겠습니다.

user: bob
pass: n' or 1=1 or 'm=m

이번에는, 다음과 같이 됩니다.

You could not be verified. Please try again...

이것이 당신이 질문을 게시할 때 소리를 지르는 이유입니다. 왜냐하면 사람들은 시도해도 당신의 코드가 무시될 수 있다는 것을 알 수 있기 때문입니다.코드를 개선하고 보안을 강화하며 최신 기능을 사용하려면 이 질문과 답변을 사용하십시오.

마지막으로, 이것이 완벽한 코드라는 것은 아닙니다.해시 패스워드를 사용하는 등 개선할 수 있는 것이 많이 있습니다.예를 들어 기밀 정보를 데이터베이스에 저장할 때 일반 텍스트로 저장하지 않고 여러 수준의 검증을 실시합니다.그러나 실제로 이전 주입 코드를 변경하기만 하면 올바르게 쓸 수 있습니다.코드 - 그리고 당신이 여기까지 왔고 여전히 읽고 있다는 사실은 당신이 웹사이트와 어플리케이션을 작성할 때 이러한 종류의 코드를 구현할 뿐만 아니라 내가 방금 말한 다른 것들을 연구할 수 있다는 희망을 줍니다.제대로 작동하지 않는 가장 기본적인 코드가 아니라 가능한 한 최선의 코드를 작성하십시오.

MySQL 확장자는 세 가지 중 가장 오래된 것으로 개발자들이 MySQL과 통신하기 위해 사용했던 원래의 방식입니다.이 확장자는 PHP와 MySQL의 새로운 릴리스에서 개선되었기 때문에 다른가지 대안으로 권장되지 않습니다.

  • MySQLi는 MySQL 데이터베이스 작업을 위한 '개선된' 확장입니다.새로운 버전의 MySQL Server에서 사용할 수 있는 기능을 활용하여 함수 지향 및 객체 지향 인터페이스를 개발자에게 공개하고 기타 몇 가지 유용한 작업을 수행합니다.

  • PDO는 이전에 주요 데이터베이스 액세스 확장에 분산되어 있던 대부분의 기능을 통합하는 API를 제공합니다.MySQL, PostgreSQL, SQLite, MS SQL 등이 인터페이스는 프로그래머가 데이터베이스 접속, 쿼리 및 결과 세트를 조작할 수 있도록 높은 수준의 객체를 공개하고 낮은 수준의 드라이버는 데이터베이스 서버와의 통신 및 자원 처리를 수행합니다.PDO에는 많은 논의와 작업이 수반되고 있으며, 이는 최신 프로페셔널 코드로 데이터베이스를 사용하는 데 적합한 방법으로 간주되고 있습니다.

위의 답변은 매우 길기 때문에 요약하면 다음과 같습니다.

mysqli 확장에는 여러 가지 이점이 있습니다.mysql 확장보다 향상된 주요 기능은 다음과 같습니다.

  • 객체 지향 인터페이스
  • 준비 스테이트먼트 지원
  • 여러 스테이트먼트에 대한 지원
  • 트랜잭션 지원
  • 강화된 디버깅 기능
  • 임베디드 서버 지원

출처: MySQLi의 개요


위의 답변에서 설명한 것처럼 mysql 대신 mysqli와 PDO(PHP Data Objects)가 있습니다.

  • API는 서버측 준비 스테이트먼트를 지원합니다.MYSQLi 및 PDO에서 지원
  • API는 클라이언트 측 준비 스테이트먼트를 지원합니다.PDO에서만 지원
  • API는 스토어드 프로시저를 지원합니다.MySQLi와 PDO 모두
  • API는 Multiple Statements와 모든 MySQL 4.1+ 기능을 지원합니다. MySQLi 및 대부분 PDO에서도 지원됩니다.

MySQLi와 PDO는 모두 PHP 5.0에서 도입된 반면 MySQL은 PHP 3.0보다 먼저 도입된 것입니다.주의할 점은 MySQL이 PHP5.x에 포함되어 있다는 점입니다.

거의 모든 것을 정의할 수 있습니다.mysql_*mysqli 또는 PDO를 사용하는 함수입니다.이러한 함수를 이전 PHP 어플리케이션 위에 포함시키면 PHP7에서 작동합니다.나의 해결책은 여기 있다.

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;

function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}

function mysql_connect($host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
    return $GLOBALS[MYSQL_LINK];
}

function mysql_pconnect($host, $user, $pass) {
    return mysql_connect($host, $user, $pass);
}

function mysql_select_db($db, $link=null) {
    $link = mysql_link($link);
    return mysqli_select_db($link, $db);
}

function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link);
}

function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link);
}

function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link);
}

function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link);
}

function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link);
}

function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link);
}

function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link);
}

function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link);
}

function mysql_escape_string($string) {
    return mysql_real_escape_string($string);
}

function mysql_real_escape_string($string, $link=null) {
    $link = mysql_link($link);
    return mysqli_real_escape_string($link, $string);
}

function mysql_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql);
}

function mysql_unbuffered_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}

function mysql_set_charset($charset, $link=null){
    $link = mysql_link($link);
    return mysqli_set_charset($link, $charset);
}

function mysql_get_host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_host_info($link);
}

function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link);
}

function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link);
}

function mysql_get_client_info() {
    $link = mysql_link();
    return mysqli_get_client_info($link);
}

function mysql_create_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "DROP DATABASE `$db`");
}

function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES");
}

function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}

function mysql_list_tables($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) {
    $link = mysql_link($link);
    mysqli_select_db($link, $db);
    return mysqli_query($link, $sql);
}

function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink);
}

function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink);
}

function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
    return mysqli_fetch_array($qlink, $result);
}

function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink);
}

function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink);
}

function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink);
}

function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink);
}

function mysql_data_seek($qlink, $row) {
    return mysqli_data_seek($qlink, $row);
}

function mysql_field_seek($qlink, $offset) {
    return mysqli_field_seek($qlink, $offset);
}

function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null)
        ? mysqli_fetch_object($qlink, $class)
        : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') {
    mysqli_data_seek($qlink, $row);
    $db = mysqli_fetch_assoc($qlink);
    return $db[$field];
}

function mysql_fetch_field($qlink, $offset=null) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink);
}

function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    $row = mysqli_fetch_array($qlink);
    return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->length : false;
}

function mysql_field_name($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable;
}

function mysql_field_type($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) {
    try {
        mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}

mysql은 더 이상 사용되지 않으므로 대신 Mysqli를 사용하십시오.

폐지된 의미:

즉, 특정 기능/방법/소프트웨어 기능/특정 소프트웨어 관행을 사용하지 마십시오.그 대신 사용할 수 있는 더 나은 대안이 있거나 있을 것이기 때문입니다.

사용되지 않는 함수를 사용할 경우 다음과 같은 일반적인 문제가 발생할 수 있습니다.

1. 기능이 완전히 정지된 경우: 어플리케이션이나 스크립트는 단순히 지원되지 않는 기능에 의존할 수 있습니다.따라서 개량된 버전이나 대체 기능을 사용합니다.

2. 권고에 대한 경고 메시지가 표시됩니다.이러한 메시지는 일반적으로 사이트 기능을 방해하지 않습니다.그러나 경우에 따라서는 서버가 헤더를 보내는 프로세스가 중단될 수 있습니다.

예:이로 인해 로그인 문제(쿠키/세션이 올바르게 설정되지 않음) 또는 전송 문제(301/302/303 리다이렉트)가 발생할 수 있습니다.

다음 사항에 유의하십시오.

- 폐지된 소프트웨어는 아직 소프트웨어의 일부입니다.

폐지된 코드는 코드의 상태(라벨)일 뿐입니다.

MYSQL과 MYSQLI의 주요 차이점*

  • 오래된 데이터베이스 드라이버
  • MySQL은 절차적으로만 사용할 수 있습니다.
  • SQL 주입 공격으로부터 보호 없음
  • PHP 5.5.0에서 더 이상 사용되지 않으며 PHP 7에서 제거되었습니다.

mysqli

  • 새 데이터베이스 드라이버
  • 현재 사용 중
  • 준비된 스테이트먼트를 공격으로부터 보호

php 버전을 업그레이드하지 않는 것이 확실하다면 업데이트할 필요가 없습니다.그러나 동시에 보안 업데이트도 받을 수 없기 때문에 웹사이트가 해커에게 더 취약해집니다.

언급URL : https://stackoverflow.com/questions/12859942/why-shouldnt-i-use-mysql-functions-in-php

반응형