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




php pdo 예제 (12)

mysql_* 함수를 사용해서는 안되는 기술적 이유는 무엇입니까? (예 : mysql_query() , mysql_connect() 또는 mysql_real_escape_string() )?

내 사이트에서 일하는 경우에도 왜 다른 것을 사용해야합니까?

그들이 내 사이트에서 작동하지 않으면 왜 오류가 발생합니까?

경고 : mysql_connect () : 그런 파일이나 디렉토리가 없습니다.


MySQL 확장은 3 가지 중 가장 오래된 것이고 개발자들이 MySQL과 통신하는 데 사용 된 원래의 방식이었습니다. 이 확장은 이제 PHP와 MySQL의 최신 릴리스에서 개선 된 점 때문에 다른 mysqli_PDO 을 선호하여 deprecated .mysqli_ PDO

  • mysqli_ 는 MySQL 데이터베이스 작업을위한 '향상된'확장 기능입니다. 새로운 버전의 MySQL 서버에서 사용할 수있는 기능을 이용하고, 기능 지향적이며 객체 지향적 인 인터페이스를 개발자에게 제공하며, 다른 멋진 기능은 제공하지 않습니다.

  • PDO 는 이전에 주요 데이터베이스 액세스 확장 (예 : MySQL, PostgreSQL, SQLite, MSSQL 등)에 분산되어 있던 대부분의 기능을 통합하는 API를 제공합니다. 인터페이스는 프로그래머가 데이터베이스 연결, 쿼리 및 결과 세트 및 하위 레벨 드라이버는 데이터베이스 서버와 통신 및 자원 처리를 수행합니다. 많은 토론과 작업이 PDO에 들어가고 있으며 현대의 전문적인 코드로 데이터베이스 작업을하는 적절한 방법으로 간주됩니다.


MySQL 확장 :

  • 활발한 발전이 없다.
  • 공식적 으로 PHP 5.5 (2013 년 6 월 출시)부터 deprecated .
  • PHP 7.0부터 완전히 removed 되었습니다 (2015 년 12 월에 출시).
  • OO 인터페이스가 없다.
  • 지원하지 않음 :
    • 비 차단, 비동기 쿼리
    • 준비된 문 또는 매개 변수가있는 쿼리
    • 저장 프로 시저
    • 다중 명령문
    • 업무
    • "새로운"비밀번호 인증 방법 (MySQL 5.6에서는 기본적으로 5.7에서 필요)
    • MySQL 5.1의 모든 기능

향후 제공되지 않을 예정이므로 코드를 사용하면 미래의 증거가 줄어 듭니다.

준비된 문에 대한 지원 부족은 개별 함수 호출로 수동으로 이스케이프 처리하는 것보다 오류가 발생하기 쉬운 외부 ​​데이터 이스케이프 및 인용 방법을 제공하므로 특히 중요합니다.

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


PHP는 MySQL에 연결할 수있는 세 가지 API를 제공합니다. 이것들은 mysql (PHP 7에서 제거됨), mysqliPDO 확장입니다.

mysql_* 함수는 매우 인기가 있었지만 사용은 더 이상 권장되지 않습니다. 문서화 팀은 데이터베이스 보안 상황에 대해 논의하고 있으며 사용자가 일반적으로 사용되는 ext / mysql 확장자에서 벗어나도록 교육하는 것은이 과정의 일부입니다 ( php.internals : ext / mysql 사용 중단 ).

나중에 PHP 개발자 팀은 사용자가 mysql_connect() , mysql_pconnect() 또는 ext/mysql 내장 된 암시 적 연결 기능을 통해 MySQL에 연결할 때 E_DEPRECATED 오류를 생성하기로 결정했습니다.

ext/mysqldeprecated 이후로 폐지되었으며 PHP 7 부터 삭제되었습니다 .

레드 박스를 보시겠습니까?

어떤 mysql_* 함수 페이지로 가면, 더 이상 사용해서는 안되는 빨간색 상자가 보인다.

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

ext/mysqlMySQL 3.23을 위해 만들어졌으며 유지 보수하기가 좀 더 어렵게 만든이 오래된 버전과의 호환성을 유지하면서 거의 추가되지 않았다. ext/mysql 지원하지 않는 누락 된 기능은 다음과 같습니다 ( deprecated ).

mysql_* 함수를 사용하지 않는 이유 :

  • 활발한 개발 중이 아님
  • PHP 7에서 제거됨
  • OO 인터페이스가 없다.
  • 비 차단, 비동기 쿼리를 지원하지 않습니다.
  • 준비된 문이나 매개 변수가있는 쿼리를 지원하지 않습니다.
  • 저장 프로 시저를 지원하지 않습니다.
  • 여러 문장을 지원하지 않습니다.
  • transactions 지원하지 않습니다.
  • MySQL 5.1의 모든 기능을 지원하지는 않는다.

위의 질문은 Quentin의 답변에서 인용 한 것입니다.

준비된 문에 대한 지원 부족은 별도의 함수 호출을 사용하여 수동으로 이스케이프 처리하는 것보다 외부 데이터를 이스케이프하고 인용하는 오류가 발생하기 쉬운 명확하고 명확한 방법을 제공하므로 특히 중요합니다.

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

지원 중단 경고 표시 안 함

코드가 MySQLi / PDO 로 변환되는 동안, E_DEPRECATED 에러는 php.ini의 error_reportingE_DEPRECATED: 를 제외하도록 설정함으로써 억제 될 수 있습니다 E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

그러나 이것은 또한 MySQL과 다른 것들에 대한 것일 수있는 다른 deprecation 경고를 숨 깁니다. ( PHP 매뉴얼에서 )

PDO 대 MySQLi 문서 : 어느 것을 사용해야합니까? Dejan Marjanovic의 도움을 받아 선택할 수 있습니다.

더 좋은 방법은 PDO 이며, 이제는 간단한 PDO 자습서를 작성하고 있습니다.

간단하고 짧은 PDO 튜토리얼

질문. "PDO"란 무엇인가?

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

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 됩니다. 생성자는 데이터베이스 소스를 지정하기위한 매개 변수를 사용합니다. PDO 의 생성자는 대부분 DSN (데이터 소스 이름)과 선택적으로 username , password 인 네 개의 매개 변수를 사용 password .

여기에서는 DSN 제외한 모든 것에 익숙하다고 생각합니다. 이것은 PDO 에서 새로운 것입니다. DSN 은 기본적으로 PDO 에게 어떤 드라이버를 사용할 지와 연결 세부 사항을 알려주는 일련의 옵션입니다. 자세한 내용은 PDO MySQL DSN을 확인하십시오.

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

참고 : charset=UTF-8 사용할 수도 있지만 오류가 발생하는 경우가 있으므로 utf8 을 사용하는 것이 좋습니다.

접속 에러가있는 경우는 Exception 를 처리하기 위해서 캐치 할 수있는 PDOException 오브젝트를 슬로우합니다.

좋은 읽기 : 연결 및 연결 관리 ¶

네 번째 매개 변수에 대한 배열로 여러 드라이버 옵션을 전달할 수도 있습니다. 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 구축 후 속성을 설정할 수 있습니까?

, setAttribute 메소드를 사용하여 PDO를 생성 한 후 몇 가지 속성을 설정할 수도 있습니다.

$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);

오류 처리

오류 처리는 mysql_* 보다 PDO 에서 훨씬 쉽습니다.

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 를 세 가지 오류 모드 중 하나로 강제 설정할 수 있습니다. 다음 세 가지 오류 처리 모드가 있습니다.

  • PDO::ERRMODE_SILENT . 그것은 단지 에러 코드를 설정하고 mysql_* 과 거의 비슷하게 동작합니다. 여기서 각 결과를 확인한 다음 $db->errorInfo(); 오류 세부 사항을 얻으십시오.
  • PDO::ERRMODE_WARNING E_WARNING 시킵니다. (실행 중 경고 (치명적이지 않은 오류). 스크립트의 실행이 중지되지 않았습니다.)
  • PDO::ERRMODE_EXCEPTION : 예외를 던집니다. PDO가 제기 한 오류를 나타냅니다. 자신의 코드에서 PDOException 을 던져서는 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.
}

또한 by or die() 처리 할 수도 있고 mysql_* 과 같이 말할 수도 mysql_* 정말 다양 할 것입니다. display_errors off 오류 로그를 읽음으로써 프로덕션 환경에서 위험한 오류 메시지를 숨길 수 있습니다.

위의 모든 것을 읽은 후, 아마도 생각하고있을 것입니다. 단순한 SELECT , INSERT , UPDATE 또는 DELETE 문을 기대고 싶을 때 무엇입니까? 걱정마. 여기 우리가 간다.

데이터 선택

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에 알려주는 것입니다. 아래 섹션에서 나는 이것을 설명하고있다.

모드 불러 오기

위의 fetch()fetchAll() 코드에서 PDO::FETCH_ASSOC 을 사용합니다. 이것은 필드 이름을 키로하는 연관 배열로 행을 반환하도록 PDO 에 지시합니다. 하나씩 설명 할 다른 많은 가져 오기 모드도 있습니다.

우선, 가져 오기 모드를 선택하는 방법을 설명합니다.

 $stmt->fetch(PDO::FETCH_ASSOC)

위의 fetch() 있습니다. 다음을 사용할 수도 있습니다.

이제 모드 가져 오기 :

  • PDO::FETCH_ASSOC : 결과 집합에 반환 된 열 이름으로 인덱싱 된 배열을 반환합니다.
  • PDO::FETCH_BOTH (기본값) : 결과 집합에 반환 된 열 이름과 0 인덱싱 된 열 번호로 인덱싱 된 배열을 반환합니다.

더 많은 선택권이 있습니다! PDOStatement Fetch 문서 PDOStatement 모든 것을 읽어보십시오 . .

행 수 얻기 :

반환되는 행의 수를 얻기 위해 mysql_num_rows 를 사용하는 대신 다음과 같이 PDOStatement 가져 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();

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

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;

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

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

위의 방법은 쿼리에서 변수를 사용하지 않는 경우에만 유용합니다. 그러나 쿼리에서 변수를 사용할 필요가있을 때 위와 같이 준비된 문이나 매개 변수가있는 문에 대해 시도하지 마십시오.

준비된 진술

Q. 준비된 성명서 란 무엇이며 왜 필요합니까?
A. 준비된 명령문은 미리 컴파일 된 SQL 문으로서 서버에만 데이터를 보내 여러 번 실행할 수 있습니다.

준비된 문장을 사용하는 일반적인 워크 플로우는 다음과 같습니다 ( Wikipedia 3 3 포인트에서 인용 ) :

  1. 준비 : 명령문 템플리트는 응용 프로그램에 의해 작성되고 데이터베이스 관리 시스템 (DBMS)으로 송신됩니다. 특정 값은 매개 변수, 자리 표시 자 또는 바인드 변수라고하는 지정되지 않은 상태로 남습니다 (아래에 ? 라고 표시됨).

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

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

  3. 실행 : 나중에 응용 프로그램이 매개 변수 값을 제공 (또는 바인드)하고 DBMS가 명령문을 실행합니다 (결과를 반환 할 수 있음). 응용 프로그램은 다른 값으로 원하는만큼 여러 번 명령문을 실행할 수 있습니다. 이 예에서는 첫 번째 매개 변수에 '빵'을, 두 번째 매개 변수에 1.00 을 제공 할 수 있습니다.

SQL에 플레이스 & 더를 포함 시켜서 준비된 명령문을 사용할 수 있습니다. 기본적으로 placeholder가없는 세 가지가 있습니다 (위의 변수로 시도하지 마십시오). 하나는 명명되지 않은 자리 표시자가 있고, 다른 하나는 명명 된 자리 표시자가 있습니다.

Q. 이제는 이름이 지정된 자리 표시자가 무엇이며 어떻게 사용합니까?
A. 명명 된 자리 표시 자. 물음표 대신 콜론으로 시작하는 설명적인 이름을 사용하십시오. 우리는 이름 자리 홀더에서 가치의 위치 / 질서에 관심이 없다 :

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

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

또한 execute 배열을 사용하여 바인드 할 수 있습니다.

<?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);

OOP 친구를위한 또 다른 멋진 기능은 명명 된 자리 표시자는 속성이 명명 된 입력란과 일치한다고 가정 할 때 데이터베이스에 개체를 직접 삽입 할 수 있다는 것입니다. 예 :

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); )에 $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에서와 같이 매개 변수화 된 쿼리를 사용하면 위험을 완전히 피할 수 있습니다.

예를 들어 누군가가 "enhzflep); drop table users"사용자 이름으로 사용할 수 있습니다 . 이전 함수는 쿼리 당 여러 개의 명령문을 실행할 수 있으므로, 그 불쾌한 놈이 전체 테이블을 삭제할 수 있습니다.

mysqli의 PDO를 사용한다면 사용자 이름이 끝날 것이다 "enhzflep); drop table users".

bobby-tables.com 참조하십시오 .


나는 위의 답이 정말로 길다는 것을 알기 때문에 요약 할 수있다.

mysqli 확장에는 다음과 같은 몇 가지 이점이 있습니다.

  • 객체 지향 인터페이스
  • Prepared Statements 지원
  • 다중 명령문 지원
  • 트랜잭션 지원
  • 향상된 디버깅 기능
  • 임베디드 서버 지원

출처 : MySQLi 개요

위의 답변에서 설명한 것처럼 mysql의 대안은 mysqli 및 PDO (PHP Data Objects)입니다.

  • API는 서버 측 Prepared Statements를 지원합니다 : MYSQLi 및 PDO가 지원합니다.
  • API는 클라이언트 측 Prepared Statements를 지원합니다. PDO에서만 지원됩니다.
  • API는 저장 프로 시저를 지원합니다 : MySQLi와 PDO 둘 다
  • API는 다중 명령문 및 모든 MySQL 4.1+ 기능을 지원합니다. - MySQLi 및 대부분 PDO에서 지원합니다.

MySQLi와 PDO는 모두 PHP 5.0에서 소개되었지만 MySQL은 PHP 3.0보다 먼저 도입되었습니다. 주의해야 할 점은 MySQL은 PHP5.x에 포함되어 있지만 이후 버전에서는 더 이상 사용되지 않습니다.


mysql_ * 함수는 더 나은 함수와 코드 구조가 개발되었다는 사실을 감안할 때 ( PHP 5.5부터 ) 가치가 하락 했다. 함수가 감가 상각되었다는 사실은 성능과 보안면에서 더 이상 개선되지 않는다는 것을 의미합니다 . , 미래의 증거가 적다는 것을 의미합니다 .

더 많은 이유가 필요한 경우 :

  • mysql_ * 함수는 prepared statement를 지원하지 않는다.
  • mysql_ * 함수는 매개 변수의 바인딩을 지원하지 않습니다.
  • mysql_ * 함수는 객체 지향 프로그래밍을위한 기능이 부족합니다.
  • 그 목록은 계속된다.

기술적 인 이유에 대해서 말하자면 극히 특이하고 거의 사용되지 않는 몇 가지가 있습니다. 가장 가능성이 당신의 인생에서 절대 사용하지 않습니다.
어쩌면 나는 너무 무식하지만, 나는 그것들을 사용할 기회를 결코 얻지 못했다.

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

필요한 경우 mysql 확장에서 더 세련되고 현대적인 것으로 이동하려는 기술적 인 이유는 의심의 여지가 없습니다.

그럼에도 불구하고 기술적이지 않은 몇 가지 문제가있어 경험을 조금 더 어렵게 만들 수 있습니다.

  • 현대 PHP 버전에서 이러한 함수를 더 사용하면 더 이상 사용되지 않는 수준의 알림이 표시됩니다. 그들은 단순히 끌 수 있습니다.
  • 먼 미래에는 기본 PHP 빌드에서 제거 될 수 있습니다. mydsql ext가 PECL로 옮겨지고 모든 hoster는 수십 년 동안 사이트를 운영하는 고객을 잃고 싶지 않기 때문에 PHP를 컴파일하면 행복합니다.
  • 커뮤니티의 강력한 저항. 이 정직한 기능을 언급 할 때마다 엄격한 금기 사항에 처해 있다고 들었습니다.
  • 평균적인 PHP 사용자이기 때문에 이러한 함수를 사용하려는 생각은 오류가 발생하기 쉽고 잘못되었습니다. 단지 당신에게 잘못된 길을 가르치는 모든 수많은 자습서와 매뉴얼 때문입니다. 함수 자체가 아니라 강조해야합니다. 그러나 사용되는 방식입니다.

이 후자의 문제는 문제입니다.
그러나 제 의견으로는 제안 된 해결책이 더 좋지 않습니다.
그것은 나에게 보인다 너무 이상주의 모든 PHP 사용자가 제대로 한 번에 SQL 쿼리를 처리하는 방법을 배울 것입니다 꿈을. 대부분 mysql_ *을 mysqli_ *로 기계적으로 변경 하여 접근 방식을 동일하게 유지 합니다. 특히 mysqli가 prepared statement 사용법을 놀라 울 정도로 고통스럽고 귀찮게 만들기 때문에.
언급하지 않기 위하여 기본 준비가 문을 보호하기에 충분하지 않은 SQL 주입에서, 어느 쪽도 아니 mysqli 나 PDO는 솔루션을 제공합니다.

그래서,이 정직한 확장자와 싸우는 대신, 나는 그릇된 습관과 싸우고 올바른 방법으로 사람들을 교육하는 것을 선호합니다.

또한 일부 거짓 또는 중요하지 않은 이유로

  • 저장 프로 시저를 지원하지 않습니다 (우리는 mysql_query("CALL my_proc");여러 연령대 에서 사용 하고 있었습니다 )
  • 트랜잭션을 지원하지 않습니다 (위와 동일).
  • 다중 선언문을 지원하지 않습니다 (누가 필요합니까?).
  • 활발한 개발 단계에 있지 않습니다 (그래서 실제적인면에서 당신 에게 어떤 영향 미칩니 까?).
  • 객체 지향 인터페이스 (OO Interface)가 부족하다. (몇 시간 만에 생성된다.)
  • Prepared Statements 또는 Parametrized Queries를 지원하지 않습니다.

마지막 하나는 흥미로운 점입니다. mysql ext는 네이티브 prepared statement를 지원하지 않지만 , 안전을 위해 요구되지는 않는다. 수동으로 처리 된 자리 표시자를 사용하여 준비된 구문을 쉽게 위장 할 수 있습니다 (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 사용자와 마찬가지로 애플리케이션 코드에서 원시 API 호출을 사용하는 경우 (본질적으로 잘못된 관행) - PDO 만이 유일한 선택입니다 .이 확장은 API가 아니라 세미 DAL 인 것처럼 가장합니다. 여전히 불완전하지만 많은 중요한 기능을 제공합니다. 그 중 두 가지는 PDO를 mysqli와 비판적으로 구분합니다.

    • mysqli와는 달리 PDO는 자리 표시자를 값으로 바인딩 할 수 있기 때문에 동적으로 생성 된 쿼리를 여러 화면의 코드없이 구현할 수 있습니다.
    • mysqli와 달리 PDO는 쿼리 결과를 항상 일반적인 배열로 반환 할 수 있지만 mysqli는 mysqlnd 설치에서만 수행 할 수있다.

그래서 평범한 PHP 사용자이고 기본 준비된 명령문을 사용할 때 많은 두통을 피하려는 경우 PDO가 유일한 선택입니다.
그러나 PDO는은 총알이 아니며 어려움이 있습니다.
그래서 PDO 태그 위키 에있는 모든 일반적인 함정과 복잡한 사례에 대한 해결책을 작성했습니다.

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

  1. 준비된 진술 은은 총알이 아닙니다 . 준비된 문을 사용하여 바인딩 할 수없는 동적 식별자가 있습니다. 알 수없는 매개 변수가있는 동적 쿼리가있어 쿼리 작성이 어려운 작업이됩니다.

  2. mysqli_ * 나 PDO 함수가 애플리케이션 코드에 나타나서는 안된다. 바인딩과 루핑, 오류 처리 등의 모든 더러운 작업을 내부에서 수행하여 애플리케이션 코드를 DRY하고 깨끗하게 만드는 애플리케이션 코드와 추상화 레이어 사이에 추상화 레이어
    가 있어야합니다 . 특히 동적 쿼리 작성과 같은 복잡한 경우.

따라서 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 삽입과 비교하십시오.

다른 예시:

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

PDO가 이러한 실용적인 사례를 처리 할 수있는 예는 거의 찾을 수 없습니다.
그리고 그것은 너무 어리 석고 안전하지 않을 것입니다.

다시 한번 말하자면, 초보자 용 매뉴얼이 아닌 바보 같은 예제뿐만 아니라 실제 문제를 해결하는 데에도 유용합니다.


사용의 용이성

분석 및 합성 이유는 이미 언급되었습니다. 초보자들에게는 데이트 된 mysql_ 함수 사용을 중단하는 더 큰 동기가 있습니다.

최신 데이터베이스 API는 사용 하기더 쉽습니다 .

대부분 코드를 단순화 할 수있는 바운드 매개 변수 입니다. 그리고 share 하면 PDO 로 전환하는 것이 너무 힘들지 않습니다.

한 번에 더 큰 코드 기반을 다시 작성하는 데는 시간이 걸립니다. 이 중간체 대안을위한 Raison d' être :

mysql_ * 대신에 동일한 기능을하는 pdo_ *

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

  1. 단순히 include_once( <pdo_mysql.php> ); 각 호출 스크립트에서 데이터베이스와 상호 작용해야합니다.

  2. 모든 곳 에서 mysql_ 함수 접두사를 제거하고 pdo_ 바꾸십시오.

    • mysql_ connect()pdo_ connect() 가된다.
    • mysql_ query()pdo_ query() 가됩니다.
    • mysql_ num_rows()pdo_ num_rows()
    • mysql_ insert_id()pdo_ insert_id()
    • fetch_array()fetch_array() 가된다.
    • fetch_assoc()fetch_assoc() 가된다.
    • 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]";
    }
    

Et voilà.
귀하의 코드는 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);

너무 단순합니다. 그러나 mysql_ 제거하고 이스케이프 처리해야하는 이유에 대한 더 많은 재 작성 조언과 기술적 인 이유를 생각해 보겠습니다.

oldschool sanitize() 함수를 수정하거나 제거하십시오.

바인드 된 params를 사용하여 모든 mysql_ 호출을 pdo_query 로 변환했으면 모든 중복 pdo_real_escape_string 호출을 제거하십시오.

특히 날짜가 지정된 자습서에 의해 광고 된대로 sanitize 또는 clean 또는 filterThis 또는 clean_data 함수를 수정해야합니다.

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

여기서 가장 눈에 띄는 버그는 문서가 부족하다는 것입니다. 더 중요한 것은 필터링 순서가 정확히 잘못된 순서였습니다.

  • 올바른 순서는 다음과 같습니다. 가장 안쪽의 호출로 deprecatedly stripslashes , 그 다음에 _escape_string , strip_tags , 출력 컨텍스트에 대한 htmlentities , 마지막으로 _escape_string 은 응용 프로그램에서 직접 SQL 구문 분석을 수행해야합니다.

  • 그러나 첫 단계로 _real_escape_string 호출을 제거 하십시오.

  • 데이터베이스 및 응용 프로그램 흐름에서 HTML 컨텍스트에 안전한 문자열이 필요한 경우 나머지 sanitize() 함수를 지금 유지해야 할 수도 있습니다. 이후 HTML 만 적용되는 주석을 추가하십시오.

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

  • sanitize 함수에 stripslashes() 에 대한 언급이 있으면 더 높은 수준의 감독을 나타낼 수 있습니다.

    • 그것은 일반적으로 사용되지 않는 magic_quotes 에서 손상 (이중 이스케이프)을 실행 취소하기위한 것입니다. 그러나 어느 것이 문자열에 의한 문자열이 아니라 중앙 에서 가장 잘 고정되어 있습니다.

    • 사용자 영역 반전 접근법 중 하나를 사용하십시오. 그런 다음 sanitize 함수에서 stripslashes() 를 제거하십시오.

    magic_quotes에 대한 역사적인 주석. 이 기능은 올바르게 사용되지 않습니다. 그러나 종종 보안 기능이 실패한 것으로 잘못 묘사됩니다. 그러나 magic_quotes는 테니스 공이 영양 공급원으로 실패한만큼 실패한 보안 기능입니다. 그것은 단순히 그들의 목적이 아니 었습니다.

    PHP2 / FI의 원래 구현은 " 따옴표가 자동으로 이스케이프되어 양식 데이터를 직접 msql 쿼리로 전달하는 것이 더 쉬워졌습니다 "라고 명시 적으로 도입했습니다. 특히, 지원 된 ASCII만으로 mSQL 을 사용하는 것이 우연히 안전했습니다.
    그렇다면 PHP3 / Zend는 MySQL을 위해 magic_quotes를 다시 소개하고 그것을 잘못 문서화했습니다. 그러나 원래는 편의를위한 기능 이 아니며 보안을 염두에 두지 않았습니다.

준비된 진술의 차이점

문자열 변수를 SQL 쿼리로 스크램블하면 더 복잡해지지 않고 따라갈 수 있습니다. 또한 MySQL이 코드와 데이터를 다시 분리하기위한 외부적인 노력이기도합니다.

SQL 주입은 단순히 데이터가 코드 컨텍스트 로 추출 되는 경우입니다. 데이터베이스 서버는 나중에 PHP가 원래 붙인 변수를 쿼리 절 사이에서 구분할 수 없습니다.

바운드 매개 변수를 사용하면 PHP 코드에서 SQL 코드와 SQL 컨텍스트 값을 구분할 수 있습니다. 그러나 PDO :: EMULATE_PREPARES를 제외하고는 장면 뒤에서 다시 섞이지는 않습니다. 데이터베이스는 변환되지 않은 SQL 명령과 1 : 1 변수 값을받습니다.

이 답변은 mysql_ 때의 가독성에 대해주의를 기울여야 함을 강조합니다. 이 가시적이고 기술적 인 데이터 / 코드 분리로 인해 종종 성능상의 이점 (값이 다른 INSERT가 반복됨)이 있습니다.

매개 변수 바인딩이 모든 SQL 주입에 대한 마법의 원 스톱 솔루션이 아니라는 점에 유의하십시오. 데이터 / 값에 대한 가장 일반적인 사용을 처리합니다. 그러나 열 이름 / 테이블 식별자를 허용하거나, 동적 절 작성을 도와 주거나, 일반 배열 값 목록을 허용 할 수는 없습니다.

하이브리드 PDO 사용

이러한 pdo_* wrapper 함수는 코딩 친화적 인 stop-gap API를 만듭니다. (그것은 특이한 함수 서명 변경이 아니라면 MYSQLI 할 수 있었던 것입니다). 그들은 또한 대부분의 시간에 실제 PDO를 노출합니다.
재 작성은 새로운 pdo_ 함수 이름을 사용할 때 멈추지 않아도됩니다. 하나씩 순차적으로 각 pdo_query ()를 일반 $ pdo-> prepare () -> execute () 호출로 전환 할 수 있습니다.

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

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

foreach 반복으로 바꿀 수 있습니다.

foreach ($result as $row) {

아니면 직접적이고 완전한 어레이 검색 :

$result->fetchAll();

PDO 나 mysql_이 대개 실패한 쿼리 후에 제공하는 것보다 더 유용한 경고를 얻을 수 있습니다.

다른 옵션

따라서 이것은 mysql_ 을 삭제하기위한 실질적인 이유와 가치있는 경로를 시각적으로 보여줍니다.

단지 pdo 전환해도 pdo 되지 않습니다. pdo_query() 도 프론트 엔드에 있습니다.

매개 변수 바인딩을 도입하거나 더 멋진 API의 다른 것을 활용할 수 없다면, 그것은 무의미한 스위치입니다. 초보자들에게 실망감을 안겨 줄만큼 간단하게 묘사되기를 바랍니다. (교육은 금지 된 것보다 효과적입니다.)

가장 단순한 일이 될 수있는 범주에 속하지만, 여전히 실험적인 코드이기도합니다. 방금 주말에 썼습니다. 그러나 대안의 과다가 있습니다. PHP 데이터베이스 추상화를 위한 google과 조금 탐색. 그러한 작업을 위해 항상 훌륭한 라이브러리가 많이 있었고 앞으로도 많이있을 것입니다.

데이터베이스 상호 작용을 더 단순하게하고 싶다면 Paris/Idiorm 과 같은 맵퍼를 사용해 볼 가치가 있습니다. 아무도 자바 스크립트에서 더 이상 사용하지 않는 DOM을 사용하지 않는 것처럼 요즘 원시 데이터베이스 인터페이스를 보모 할 필요가 없습니다.


이 대답은 잘못 작성된 PHP 사용자 유효성 검사 코드를 우회하는 방법,이 공격의 작동 방식 및 사용 방법, 이전 MySQL 기능을 안전한 준비된 문으로 대체하는 방법을 보여주기 위해 작성되었습니다. 기본적으로 사용자 (아마 많은 담당자와 함께) 코드를 개선하기위한 질문을하는 신규 사용자들에게는 짖고있다.

먼저,이 테스트 mysql 데이터베이스 (나는 광산 준비라고 불렀다)를 만들어야한다.

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...

감독자! 예상대로 작업하면 실제 사용자 이름과 비밀번호를 사용해 볼 수 있습니다.

user: Fluffeh
pass: mypass

놀랄 만한!안녕하세요 - 파이브 모든 라운드, 코드가 올바르게 관리자를 확인했습니다. 그것은 완벽!

글쎄,별로.사용자가 현명한 작은 사람이라고합니다. 그 사람이 저라고합니다.

다음을 입력하십시오.

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이나 오라클을 사용하든 피 묻은 것에 관계없이 여러 데이터베이스 엔진에 이식 할 수 있습니다. 단지 연결 문자열을 변경하여 사용하려는 모든 멋진 기능을 갖추고 있으며 훌륭하고 깨끗합니다. 나는 깨끗해.

이제 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 객체를 통해 수행됩니다. 둘째, 준비된 명령문을 사용하고 있습니다. 자, 당신이 묻는 준비된 진술은 무엇입니까? 쿼리를 실행하기 전에 데이터베이스에 알려주는 방법, 우리가 실행할 쿼리를 알려주는 방법입니다. 이 경우 데이터베이스에 "안녕하세요, id, userid를 원하는 select 문을 실행하고 userid가 변수이고 패스가 변수 인 테이블 사용자로부터 전달할 것입니다.".

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

결과는 훌륭합니다. 다시 이전의 사용자 이름과 암호 조합을 사용해보십시오.

user: bob
pass: somePass

사용자가 확인되지 않았습니다. 굉장해.

어때?

user: Fluffeh
pass: mypass

오, 방금 흥분했는데, 효과가있었습니다 : 수표가 통과되었습니다. 우리는 검증 된 관리자가 있습니다!

자, 우리의 작은 검증 시스템을 통과하기 위해 영리한 균열이 입력 할 데이터를 시도해보십시오.

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

이번에는 다음을 얻습니다.

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

이것이 질문을 게시 할 때 소리를 지르는 이유입니다. 사람들이 코드를 시도해도 무시할 수 있음을 알 수 있기 때문입니다. 이 질문과 답변을 사용하여 코드를 개선하고보다 안전하고 최신 기능을 사용하십시오.

마지막으로 이것은 이것이 PERFECT 코드라는 말은 아닙니다. 당신이 그것을 개선하기 위해 할 수있는 많은 것들이 있습니다. 예를 들어 암호 해시를 사용하면 데이터베이스에 감각 정보를 저장할 때 일반 텍스트로 저장하지 않고 여러 수준의 검증을 할 수 있습니다. 당신은 단지 오래된 주입 경향이있는 코드를이 코드로 바꾼다. 좋은 코드를 작성하는 길을 따라 잘 될 것이다. - 그리고 지금까지 읽었던 것과 아직도 읽고있는 사실은 나에게 당신이이 타입을 구현하지 않을 것이라는 희망을 준다. 귀하의 웹 사이트 및 응용 프로그램을 작성할 때 코드를 작성해야하지만, 앞서 언급 한 다른 것들을 연구하고 연구하는 것이 좋습니다. 간신히 기능하는 가장 기본적인 코드가 아닌 최상의 코드를 작성하십시오.


많은 이유가 있지만, 아마도 가장 중요한 것 중 하나는 준비 함수를 지원하지 않기 때문에 이러한 함수가 안전하지 않은 프로그래밍 방법을 권장한다는 것입니다. 준비된 문은 SQL 주입 공격을 방지합니다.

mysql_*함수를 사용할 때는 사용자가 제공 한 매개 변수를 실행해야합니다 mysql_real_escape_string(). 단 한 곳에서 잊어 버렸거나 입력의 일부만을 벗어난 경우 데이터베이스가 공격받을 수 있습니다.

PDO또는 에서 준비된 문을 사용하면 mysqli이러한 종류의 프로그래밍 오류를 만들기가 더 어려워집니다.


이와 유사한있는 기능 mysql_connect(), mysql_query()유형은 현재 사용하지 않는 이전 버전의 PHP 즉 (PHP 4) 기능이다.

이들은으로 대체 mysqli_connect(), mysqli_query()유사 최신 PHP5에서.

이것은 오류 뒤에있는 이유입니다.


먼저, 우리가 모든 사람에게주는 표준 주석부터 시작합시다.

share . 더 이상 유지 deprecated . 빨간 상자 보이니 ? 대신 준비된 문에 대해 배우고 PDO 또는 MySQLi 사용 하십시오.이 기사 는 어떤 결정을 내리는 데 도움 됩니다. PDO를 선택한 경우 다음 은 좋은 자습서 입니다.

이 문장을 통해 문장을 읽고 설명해 보겠습니다.

  • 더 이상 유지 관리되지 않으며 공식적으로 사용 중지됩니다.

    이것은 PHP 커뮤니티가 이러한 아주 오래된 함수에 대한 지원을 점차적으로 떨어 뜨리고 있음을 의미합니다. 그들은 미래 (최신) 버전의 PHP에는 존재하지 않을 것입니다! 이러한 함수를 계속 사용하면 먼 미래에 코드가 손상 될 수 있습니다.

    새로운! - ext / mysql은 deprecated

    Newer! ext / mysql 은 PHP 7에서 삭제되었습니다 .

  • 대신 준비된 문장을 배우십시오.

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

    자세한 내용 은 PHP에서 SQL 주입을 방지 하려면 어떻게합니까?를 참조하십시오 .

  • 레드 박스를 보시겠습니까?

    mysql 함수 매뉴얼 페이지로 가면, 더 이상 사용해서는 안되는 빨간색 상자가 보입니다.

  • PDO 또는 MySQLi 사용

    데이터베이스 상호 작용에 대한 완벽한 OOP 접근 방식을 제공하는 PDO 와 MySQL 관련 개선 사항 인 MySQLi 가 더 우수하고 견고하며 잘 구축 된 대안이 있습니다.







database