2017-04-12 168 views
1

我是數據庫新手,我寫了很多PHP代碼來訪問使用MySQL的數據庫。將常規mysql轉換爲預處理語句

我沒有考慮到SQL注入攻擊,所以我不得不重新編寫所有的PHP代碼使用mysql準備語句。

看如何使用準備SQL語句的視頻後,執行只有一個SQL命令需要一大堆的「準備」語句。我現有的代碼在各處都有許多不同的SQL語句,更改所有代碼打包並解壓每個「準備」語句命令的所有必需準備工作將是一場噩夢。

是否有某種包裝的,我可以用它來防止轉向常規的SQL的一行到6個或7行預處理語句?

例如用做SQL

SELECT * from users where userid=10 

需要準備的SQL語句的更多的線這條線行,尤其是如果有很多其他的SQL語句太現在變得非常複雜。

是否有某種單行包裝,我可以調用它接受模板SQL字符串,再加上參數,它還執行命令並返回結果只包含一行包裝爲不同類型的MYSQL語句它會很好,代碼會更容易混淆,而且容易出錯。

例如

$users=WrapAndExecute($db,"SELECT * from users where userid=?","s",$userid); 

$data=WrapAndExecute($db,"UPDATE table SET username=?,city=?","ss",$name,$city); 

$result=WrapAndExecute($db,"DELETE from table where id=?","s",$userid); 

$result=WrapAndExecute($db,"INSERT into ? (name,address) VALUES(?,?)","ss","users",$name,$address); 

每個上面的那些行會創建一個準備好的報表模板,做綁定,執行它並返回結果,一個普通MYSQL語句會。這會對現有代碼產生最小的影響。

任何人知道如何做到這一點,或者一些簡單的PHP庫或類已經存在要做到這一點,那我就可以導入並開始使用它?

感謝

+0

我不知道你在問什麼,但有疑問,這裏是[PDO :: prepare](https://secure.php.net/manual/en/pdo.prepare.php)PHP文檔。 – Artemis

+0

對不起,我沒有提供足夠的信息,我只是更新了我原來的帖子,感謝您的評論 – Programmer2

+1

_需要更多的準備好的SQL語句行_:更多的行,給出更好,更安全的代碼恕我直言。並沒有那麼多,這很容易閱讀/理解/維護...我沒有看到**任何理由**爲什麼一個人不會出汗一點(很多?)有一些好/優秀的代碼^^ – OldPadawan

回答

2

你並不需要一個查詢更改爲準備好的語句,如果它沒有PHP變量的原因。如果它只有常量表達式,那麼它可以安全地執行SQ​​L注入。

$sql = "SELECT * from users where userid=10"; // Safe! 
$stmt = $pdo->query($sql); 
$data = $stmt->fetchAll(); 

你並不需要更改包含PHP變量的查詢,只要該變量的值是在你的代碼中指定一個常數。如果它不從任何外部來源獲得它的價值,那它是安全的。

$uid = 10; 
$sql = "SELECT * from users where userid=$uid"; // Safe! 
$stmt = $pdo->query($sql); 
$data = $stmt->fetchAll(); 

你並不需要更改包含PHP變量的查詢,只要你可以過濾值,以保證它不會冒險的SQL注入。一個簡單快捷的方法是把它轉換爲一個整數(如果它應該是一個整數)。

$uid = (int) $_GET['uid']; 
$sql = "SELECT * from users where userid=$uid"; // Safe! 
$stmt = $pdo->query($sql); 
$data = $stmt->fetchAll(); 

那你在哪裏使用「不可信」的價值觀,這可能源自用戶輸入,或閱讀文件,甚至可以從數據庫讀取離開的情況。在這些情況下,參數是保護自己最可靠的方法。這是很容易:

$sql = "SELECT * from users where userid=?"; // Safe! 

// two lines instead of the one line query() 
$stmt = $pdo->prepare($sql); 
$stmt->execute([$_GET['uid']]); 

$data = $stmt->fetchAll(); 

的情況下的一個子集,你需要一個代碼附加行比你通常會使用。

所以不要發牢騷! ;-)


請重新發表您的關於在mysqli中執行準備語句的註釋。

它們綁定變量的方式比PDO更難使用。我不喜歡在http://php.net/manual/en/mysqli.prepare.php

給出的例子下面是用mysqli的一個簡單的方法:

$sql = "SELECT * from users where userid=?"; // Safe! 

$stmt = $mysqli->prepare($sql); 
$stmt->bind_param('i', $_GET['uid']); 
$stmt->execute(); 
$result = $stmt->get_result(); 

$data = $result->fetch_all(); 

我不喜歡他們在自己的例子做bind_result()的東西,這是混淆和不必要的。只需使用get_result()。因此,使用mysqli,與PDO相比,您需要兩行代碼。

我已經寫了mysqli的查詢包裝器,模擬PDO的​​函數的方便性。將數組映射到bind_param()的變量參數樣式是一個PITA。

見我的回答https://stackoverflow.com/a/15933696/20860https://stackoverflow.com/a/7383439/20860

+0

比爾,謝謝你,但你不必也告訴它每個參數的數據類型,例如「ssddd」參數1是字符串(s)parameter2也是一個字符串(s)parmater 3是一個小數(d)等等 – Programmer2

+1

不,不是在MySQL的情況下。在其他PDO驅動程序中可能需要其他品牌的SQL數據庫,但在MySQL的情況下,可以跳過'bindParam()'廢話並僅將一組參數值傳遞給'execute()'。這很簡單。 –

+0

我在SQL的「準備語句」教程中看到,演示者使用6或7行額外的PHP語句來執行一行常規PHP,我就像那樣瘋狂,我的代碼將是7倍大和更多複雜的加上所有的!但它似乎不是那樣的 – Programmer2

-1

解決方案在PDO擴展在PHP看看 - http://php.net/manual/en/intro.pdo.php:它是針對固定由於準備好的發言注射;此外,它還允許您連接到許多不同的數據庫(例如MySQL,MSSQL等)。

然後你可以建立你自己的包裝,因爲你希望保持它的清潔;例如您自己的包裝可能如下: (下面的例子將返回用戶行作爲對象)

// connect to DB 
$GLOBALS['default_db'] = new DB('localhost','db_name','username','password') ; 

// Get users and output results 
$query = new DBQuery('SELECT * FROM users WHERE userid = ?',array(10)) ; 
var_dump($query -> results()) ; 
var_dump($query -> num_rows()) ; 

// DB connection 
class DB { 

    public $connection; 

    public function __construct($host , $dbname , $username , $password) { 
     $this->connection = new \PDO('mysql:host=' . $host . ';dbname=' . $dbname , $username , $password); 
    } 

} 

// Wrapper 
class DBQuery { 

    private $num_rows = 0; 
    private $results = array(); 

    public function __construct($query , $params = null , $class_name = null , DB $db = null) { 

     if (is_null($db)) { 
      $db = $GLOBALS['default_db']; 
     } 

     $statement = $db->connection->prepare($query); 
     $statement->execute($params); 

     $errors = $statement->errorInfo(); 
     if ($errors[2]) { 
      throw new \Exception($errors[2]); 
     } 

     $fetch_style = ($class_name ? \PDO::FETCH_CLASS : \PDO::FETCH_OBJ); 
     $this->results = $class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style); 
     $this->num_rows += $statement->rowCount(); 

     while ($statement->nextrowset()) { 
      $this->results = array_merge($this->results,$class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style)); 
      $this->num_rows += $statement->rowCount(); 
     } 
    } 

    public function num_rows() { 
     return $this->num_rows; 
    } 

    public function results() { 
     return $this->results; 
    } 

} 
+0

謝謝sammyaglam – Programmer2

+0

NP :)希望你找到有用的包裝。 – sammysaglam

+0

我只是意識到,不幸的是我使用mysqli,不知道你寫的代碼可以與mysqli – Programmer2

-1

由於關鍵的要求似乎是,你可以用你目前的代碼庫,它會影響極小實現這個如果您告訴我們您當前使用什麼界面來運行您的查詢,這會非常有幫助。

雖然你可以使用PDO:

  • ,如果你還沒有使用PDO
  • 這意味着一個可怕的很多工作
  • PDO的例外是可怕

假設你正在使用的程序mysqli(並有一個很好的理由不使用mysqli_prepare())它不難寫出一些東西(未測試!):

function wrapAndExecute() 
{ 
    $args=func_get_args(); 
    $db=array_shift($args); 
    $stmt=array_shift($args); 
    $stmt_parts=explode('?', $stmt); 
    if (count($args)+1!=count($stmt_parts)) { 
     trigger_error("Argument count does not match placeholder count"); 
     return false; 
    } 
    $real_statement=array_shift($stmt_parts); 
    foreach ($args as $k=>$val) { 
     if (isnull($val)) { 
     $val='NULL'; 
     } else if (!is_numeric($val)) { 
     $val="'" . mysqli_real_escape_string($db, $val) . "'"; 
     } 
     $real_statement.=$val . array_shift($stmt_parts); 
    } 
    return mysqli_query($db, $real_statement); 
} 

請注意,這並不處理IS [NOT] NULL很好,也不是一個文字'?'在聲明中還是布爾(但這些都是微不足道的修復)。

+0

合作謝謝! – Programmer2

+0

我可以請你詳細說一下你對pdo異常的看法嗎?我真的很困惑 –

0

我在同一條船上,我寫了such a wrapper,它的工作方式與你想要的完全一樣,除了它是一個類,而不是一個函數。

$user = $sdb->getRow("SELECT * from users where userid=?s", $userid); 
$sdb->query("UPDATE table SET username=?s, city=?s", $name, $city); 
$sdb->query("DELETE from table where id=?s", $userid); 
$sdb->query("INSERT into ?n (name,address) VALUES(?s,?s)","users", $name, $address); 

上面是工作代碼,只要你有地方在引導文件

$db = mysqli_connect(...); 
... 
require 'safemysql.class.php'; 
$sdb = new SafeMySQL('mysqli' => $db); 

注意,沒有其他的建議可以做這樣的事情。

另請注意,如果我今天寫它,我會使用PDO,因爲此類複製了PDO中已存在的許多功能。

+0

謝謝!我決定放棄加密,因爲我不能上課,因爲我沒有上課 – Programmer2