source

레퍼런스:MySQL 확장자를 사용하는 완벽한 코드 샘플은 무엇입니까?

bestscript 2022. 11. 2. 22:47

레퍼런스:MySQL 확장자를 사용하는 완벽한 코드 샘플은 무엇입니까?

커뮤니티 학습 자원을 만들기 위해서입니다.목표는 복사/붙여넣기 PHP 코드에서 자주 볼 수 있는 끔찍한 실수를 반복하지 않는 좋은 코드의 예를 갖는 것입니다.커뮤니티 위키로 만들 것을 요청했습니다.

이것은 코딩 경연대회로 의도한 것이 아니다.이것은 질문을 하는 가장 빠르거나 가장 간단한 방법을 찾는 것이 아니라, 특히 신입사원들에게 읽기 쉽고 좋은 레퍼런스를 제공하는 것입니다.

매일매일 악성코드 스니펫을 사용한 질문이 쇄도하고 있습니다.mysql_*PDO PDO가 예를 들어 등)이미 가 되지 않을 수도 있습니다.일반적으로 PDO를 사용하는 것이 가장 좋지만, PDO가 불가능하거나(예: 레거시 소프트웨어 상속) 현실적인 기대(사용자가 이미 프로젝트에서 사용하고 있음)가 불가능할 수 있습니다.

mysql_*라이브러리에는 다음이 포함됩니다.

  • SQL 주입(값
  • LIMIT 구 및 동적 테이블 이름에 SQL 주입
  • 오류 보고 없음("이 쿼리가 작동하지 않는 이유")")
  • 파손된 에러 리포트(즉, 코드가 실전 가동에 투입되어도 에러는 항상 발생합니다)
  • Cross-Site Scripting(XSS; 사이트 간 스크립팅)의 값 출력 주입

mySQ를 사용하여 다음을 수행하는 PHP 코드 샘플을 작성합시다.L_* 함수 패밀리:

  • 값, 2개의 POST 값을 .id 및 (기호) »name (끈)
  • 합니다.tablename의 , 「」name[ ] 의 [ ] ( ) ]id
  • 장애가 발생하면 정상적으로 종료하되, 자세한 오류는 프로덕션 모드에서만 표시합니다. trigger_error()합니다.또선선
  • 출력합니다.$name이렇게 하다'

또, 상기의 약점은 일절 표시되지 않습니다.

그것은 가능한 한 간단해야 한다.이상적으로는 기능이나 클래스가 포함되어 있지 않습니다.목표는 복사/붙여넣기 가능한 라이브러리를 만드는 것이 아니라 데이터베이스 쿼리를 안전하게 하기 위해 수행해야 할 최소한의 작업을 보여주는 것입니다.

좋은 코멘트에 대한 보너스 포인트.

이 질문의 목적은 (질문의 초점은 전혀 아니지만) 코드가 불량한 질문자를 만났을 때 사용자가 링크할 수 있는 리소스로 만드는 것입니다.

PDO에 대한 논의를 시작하려면:

이 에게 PDO PDO, PDO로 .그것이 선택사항이라면, 우리는 그렇게 해야 한다.코드를 하고 있거나 이 에서 큰 을 이루었기 지금 할 수 것은 .질문자가 레거시 코드를 사용하고 있거나 이미 이 라이브러리에서 많은 성과를 거두고 있어 현재로선 변경할 수 없습니다. ,,mysql_*기능군은 올바르게 사용한다면 완벽하게 안전합니다.따라서 "PDO 사용" 답변은 여기에 입력하지 마십시오.

내가 찌른거야.가능한 한 심플하게 하려고 노력하면서, 현실 세계의 편리함을 유지하려고 노력했습니다.

Unicode를 처리하고 읽기 쉽도록 느슨한 비교를 사용합니다.착하게 굴어라;-)

<?php

header('Content-type: text/html; charset=utf-8');
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
// display_errors can be changed to 0 in production mode to
// suppress PHP's error messages

/*
Can be used for testing
$_POST['id'] = 1;
$_POST['name'] = 'Markus';
*/

$config = array(
    'host' => '127.0.0.1', 
    'user' => 'my_user', 
    'pass' => 'my_pass', 
    'db' => 'my_database'
);

# Connect and disable mysql error output
$connection = @mysql_connect($config['host'], 
    $config['user'], $config['pass']);

if (!$connection) {
    trigger_error('Unable to connect to database: ' 
        . mysql_error(), E_USER_ERROR);
}

if (!mysql_select_db($config['db'])) {
    trigger_error('Unable to select db: ' . mysql_error(), 
        E_USER_ERROR);
}

if (!mysql_set_charset('utf8')) {
    trigger_error('Unable to set charset for db connection: ' 
        . mysql_error(), E_USER_ERROR);
}

$result = mysql_query(
    'UPDATE tablename SET name = "' 
    . mysql_real_escape_string($_POST['name']) 
    . '" WHERE id = "' 
    . mysql_real_escape_string($_POST['id']) . '"'
);

if ($result) {
    echo htmlentities($_POST['name'], ENT_COMPAT, 'utf-8') 
        . ' updated.';
} else {
    trigger_error('Unable to update db: ' 
        . mysql_error(), E_USER_ERROR);
}

나는 성급하게 뭔가를 내세우기로 결심했다.시작부터 해야죠.에러에 대해서 예외를 발생시킵니다.

function executeQuery($query, $args) {
    $cleaned = array_map('mysql_real_escape_string', $args);

    if($result = mysql_query(vsprintf($query, $cleaned))) {
        return $result;
    } else {
        throw new Exception('MySQL Query Error: ' . mysql_error());
    }
}

function updateTablenameName($id, $name) {
    $query = "UPDATE tablename SET name = '%s' WHERE id = %d";

    return executeQuery($query, array($name, $id));
}

try {
    updateTablenameName($_POST['id'], $_POST['name']);
} catch(Exception $e) {
    echo $e->getMessage();
    exit();
}
/**
 * Rule #0: never trust users input!
 */

//sanitize integer value
$id = intval($_GET['id']);
//sanitize string value;
$name = mysql_real_escape_string($_POST['name']);
//1. using `dbname`. is better than using mysql_select_db()
//2. names of tables and columns should be quoted by "`" symbol
//3. each variable should be sanitized (even in LIMIT clause)
$q = mysql_query("UPDATE `dbname`.`tablename` SET `name`='".$name."' WHERE `id`='".$id."' LIMIT 0,1 ");
if ($q===false)
{
    trigger_error('Error in query: '.mysql_error(), E_USER_WARNING);
}
else
{
    //be careful! $name contains user's data, remember Rule #0
    //always use htmlspecialchars() to sanitize user's data in output
    print htmlspecialchars($name).' updated';
}

########################################################################
//Example, how easily is to use set_error_handler() and trigger_error()
//to control error reporting in production and dev-code
//Do NOT use error_reporting(0) or error_reporting(~E_ALL) - each error
//should be fixed, not muted
function err_handler($errno, $errstr, $errfile, $errline)
{
    $hanle_errors_print = E_ALL & ~E_NOTICE;

    //if we want to print this type of errors (other types we can just write in log-file)
    if ($errno & $hanle_errors_print)
    {
        //$errstr can contain user's data, so... Rule #0
        print PHP_EOL.'Error ['.$errno.'] in file '.$errfile.' in line '.$errline
              .': '.htmlspecialchars($errstr).PHP_EOL;
    }
    //here you can write error into log-file
}

set_error_handler('err_handler', E_ALL & ~E_NOTICE & E_USER_NOTICE & ~E_STRICT & ~E_DEPRECATED);

코멘트에 대한 설명:

//1. using `dbname`. is better than using mysql_select_db()

.mysql_select_db는 mysql_select_db를 사용하여 를 찾아 수정하는 것은 쉽지 않습니다.
예를 들어, 일부 스크립트에서는 db1을 데이터베이스로 설정하지만, 일부 기능에서는 db2를 데이터베이스로 설정해야 합니다.
이 함수를 호출하면 데이터베이스가 전환되고 스크립트의 다음 쿼리가 모두 중단되거나 잘못된 데이터베이스(테이블과 열의 이름이 일치하는 경우)의 일부 데이터가 중단됩니다.

//2. names of tables and columns should be quoted by "`" symbol 

일부 열의 이름은 SQL 키워드가 될 수 있으며 "'" 기호를 사용하면 도움이 됩니다.
또한 쿼리에 삽입된 모든 문자열 값은 ' 기호로 따옴표로 묶어야 합니다.

//always use htmlspecialchars() to sanitize user's data in output
XSS 공격을 방지하는 데 도움이 됩니다.

<?  
mysql_connect(); 
mysql_select_db("new"); 
$table = "test"; 
if($_SERVER['REQUEST_METHOD']=='POST') {
  $name = mysql_real_escape_string($_POST['name']); 
  if ($id = intval($_POST['id'])) { 
    $query="UPDATE $table SET name='$name' WHERE id=$id"; 
  } else { 
    $query="INSERT INTO $table SET name='$name'"; 
  } 
  mysql_query($query) or trigger_error(mysql_error()." in ".$query); 
  header("Location: http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);  
  exit;  
}  
if (!isset($_GET['id'])) {
  $LIST=array(); 
  $query="SELECT * FROM $table";  
  $res=mysql_query($query); 
  while($row=mysql_fetch_assoc($res)) $LIST[]=$row; 
  include 'list.php'; 
} else {
  if ($id=intval($_GET['id'])) { 
    $query="SELECT * FROM $table WHERE id=$id";  
    $res=mysql_query($query); 
    $row=mysql_fetch_assoc($res); 
    foreach ($row as $k => $v) $row[$k]=htmlspecialchars($v); 
  } else { 
    $row['name']=''; 
    $row['id']=0; 
  } 
  include 'form.php'; 
}  
?>

폼.클라이언트

<? include 'tpl_top.php' ?>
<form method="POST">
<input type="text" name="name" value="<?=$row['name']?>"><br>
<input type="hidden" name="id" value="<?=$row['id']?>">
<input type="submit"><br>
<a href="?">Return to the list</a>
</form>
<? include 'tpl_bottom.php' ?>

list.displaces를 표시합니다.

<? include 'tpl_top.php' ?>
<a href="?id=0">Add item</a>
<? foreach ($LIST as $row): ?>
<li><a href="?id=<?=$row['id']?>"><?=$row['name']?></a>
<? endforeach ?>
<? include 'tpl_bottom.php' ?>

이랬다
몇 가지 하지 못하지만 홀더를 처리하는 할 수 ).

mysql 쿼리를 안전하고 편리하게 하기 위해 간결한 솔루션을 게시하려는 시도가 있습니다.

오래전에 작성한 기능으로, 법인 표준 OOP 베이스의 솔루션으로 이행할 때까지 큰 도움이 되었습니다.
추구해야 할 목표는 보안과 사용 편의성 두 가지였습니다.

첫 번째는 플레이스 홀더를 구현함으로써 달성됩니다.
두 번째는 플레이스 홀더와 다양한 결과 유형을 구현함으로써 달성됩니다.

그 기능은 확실히 이상적이지 않다.단점은 다음과 같습니다.

  • ★★★★★★★★★★★★★★★★ 없음%구문을 사용하고 .
  • 여러 연결이 지원되지 않습니다.
  • 식별자에 대한 자리 표시자가 없습니다(다른 많은 편리한 자리 표시자도 마찬가지).
  • 다시, 식별자 자리 표시자가 없습니다! "ORDER BY $field"!!은!!!!!!!! !!!!!!
  • 물론 OOP 구현은 훨씬 더 유연하며 추악한 "모드" 변수 대신 깔끔하게 구별되는 방법을 사용할 수 있습니다.

그러나, 라이브러리 전체를 설치할 필요는 없고, 안전하고, 간결합니다.

function dbget() {
  /*
  usage: dbget($mode, $query, $param1, $param2,...);
  $mode - "dimension" of result:
  0 - resource
  1 - scalar
  2 - row
  3 - array of rows
  */
  $args = func_get_args();
  if (count($args) < 2) {
    trigger_error("dbget: too few arguments");
    return false;
  }
  $mode  = array_shift($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);
  if (!$query) return false;

  $res = mysql_query($query);
  if (!$res) {
    trigger_error("dbget: ".mysql_error()." in ".$query);
    return false;
  }

  if ($mode === 0) return $res;

  if ($mode === 1) {
    if ($row = mysql_fetch_row($res)) return $row[0];
    else return NULL;
  }

  $a = array();
  if ($mode === 2) {
    if ($row = mysql_fetch_assoc($res)) return $row;
  }
  if ($mode === 3) {
    while($row = mysql_fetch_assoc($res)) $a[]=$row;
  }
  return $a;
}
?>

사용 예

$name = dbget(1,"SELECT name FROM users WHERE id=%d",$_GET['id']);
$news = dbget(3,"SELECT * FROM news WHERE title LIKE %s LIMIT %d,%d",
              "%$_GET[search]%",$start,$per_page);

위의 예에서 알 수 있듯이 Stackoverflow에 게시된 모든 코드와의 주요 차이점은 안전 루틴과 데이터 검색 루틴이 모두 함수 코드에 캡슐화되어 있습니다.따라서 수동 바인딩, 이스케이프/인용 또는 캐스팅은 물론 수동 데이터 검색도 필요하지 않습니다.

다른 도우미 기능과 조합하여

function dbSet($fields,$source=array()) {
  $set = '';
  if (!$source) $source = &$_POST;
  foreach ($fields as $field) {
    if (isset($source[$field])) {
      $set.="`$field`='".mysql_real_escape_string($source[$field])."', ";
    }
  }
  return substr($set, 0, -2); 
}

이렇게 사용하다

$fields = explode(" ","name surname lastname address zip phone regdate");
$_POST['regdate'] = $_POST['y']."-".$_POST['m']."-".$_POST['d'];
$sql = "UPDATE $table SET ".dbSet($fields).", stamp=NOW() WHERE id=%d";
$res = dbget(0,$sql, $_POST['id']);
if (!$res) {
  _503;//calling generic 503 error function
}

OP의 사례를 포함하여 거의 모든 니즈를 커버할 수 있습니다.

언급URL : https://stackoverflow.com/questions/6198104/reference-what-is-a-perfect-code-sample-using-the-mysql-extension