簡體   English   中英

PHP函數使用准備好的SQL語句-使用爆破輸入以逗號分隔的數組值作為函數的參數

[英]PHP function to use prepared SQL statements - using implode to enter values of array separated by commas as arguments for function

我最近問了一些有關針對SQL注入漏洞的安全性的問題。 我決定創建一個函數,該函數將使用准備好的語句執行sql查詢,因此我不必為每個查詢寫出這么多行代碼:

function secure_sql($query, $values, $datatypes){
    global $link;
    $stmt = mysqli_prepare($link, $query);
    foreach($values as &$value){
        $value = mysqli_real_escape_string($link, $value);
        mysqli_stmt_bind_param($stmt, substr($datatypes, 0), $value);
        $datatypes = substr($datatypes, -(strlen($datatypes)-1));
    }
    mysqli_stmt_execute($stmt);
    mysqli_stmt_bind_result($stmt, implode(", ", $values));
    $results = mysqli_stmt_get_result($stmt);
    return $results;
}

其中query是准備好的查詢(帶有?s),$ values是所有變量的數組(按順序替換占位符?s),$ datatypes是包含變量的所有數據類型的字符串。 $ link是一個數據庫連接。

所以我有兩個主要問題。

1)它不起作用。 我認為這一定是因為內爆可能無法在此情況下正確使用。 我將使用什么呢? 我不能使用call_user_func_array,因為我還需要使用$ stmt作為參數。 我嘗試使用array_unshift將$ stmt添加到參數的開頭,但是它不起作用。

2)如果我確實可以使用它,該如何改進呢? 我仍然是PHP和SQL的初學者。

編輯:我已經解決了問題。 謝謝大家的寶貴意見和答案!

我覺得沒有必要直接給出我想出的代碼,而是要解釋它是有必要的,看看您的某些思維方式是多么不正確。

首先,使用mysqli_stmt_bind_param()使我感到困惑-您的第二個參數表達式( substr($datatypes, 0) )返回所有數據類型的值,但您只綁定了一個。 我認為您的意思是:

mysqli_stmt_bind_param($stmt, $datatypes[0] /* <-- retrieves the first character */, $value);

但是更重要的是,您只應該調用一次 mysqli_stmt_bind_param() ,這給您帶來了更大的困難...要省略foreach循環,那么call_user_func_array()呢? (實際上,很有可能保留$stmt作為參數):

call_user_func_array("mysqli_stmt_bind_param", array_merge(array($stmt, $datatypes), $values)); 
//calls mysqli_stmt_bind_param($stmt, $datatypes, $values[0], $values[1]... values[n]);

如果您對以上內容感到困惑,請按照以下方式查看:

call_user_func_array             //a call to mysqli_stmt_bind_param, with all the appropriate parameters
(
   "mysqli_stmt_bind_param",     //the function to call
   array_merge                   //an array consisting of the statement, the datatype and all the values
   (
       array($stmt, $datatypes), //statement and datatype-parameters
       $values                   //all the values
   )
);

但是,這確實需要您的$values mysqli_stmt_bind_param包含引用,因為mysqli_stmt_bind_param期望您的值是這樣。 如果您仍然想將它們作為值傳遞給函數,則可以添加它,然后將$ref_values傳遞給call_user_func_array()函數:

foreach ($values as &$value) $ref_values[] = &$value;

現在我們來使用implode() ,這也是不正確的。 要從PHP手冊中引用:

放大(返回值):

返回一個字符串,其中包含以相同順序表示所有數組元素的字符串表示形式,並且每個元素之間都有粘合字符串。

mysqli_stmt_bind_result(第二個參數):

要綁定的變量。

因此,您要在此處執行的操作是使implode()返回的string成為變量,這沒有任何意義。 幸運的是, mysqli_stmt_get_result()返回一個在執行后獲取的對象,這意味着不需要bind_result-function。 因此,請嘗試刪除該行。

總之,我將像這樣重新編寫代碼:

function secure_sql($query, $values, $datatypes) {
    global $link;
    $stmt = mysqli_prepare($link, $query);
    foreach ($values as &$value) $ref_values[] = &$value;
    call_user_func_array("mysqli_stmt_bind_param", array_merge(array($stmt, $datatypes), $ref_values));
    mysqli_stmt_execute($stmt);
    return mysqli_stmt_get_result($stmt);
}

對我來說,聽起來應該可行,但是如果不行,請告訴我(我可能在某處缺少某些東西或在錯誤地思考)。

為了回答第二個問題,一切看起來都不錯,但是我不建議您使用mysqli_ ,因為它們最終會被刪除(如PHP手冊中所述 )。 如果您打算使用對象代替,則在我進行更改時,大多數情況都類似(除了需要使用對象屬性和對象方法來代替它們的事實……),但call_user_func_array()功能。 不過,幸運的是,也可以通過指定一個數組作為第一個參數來調用該方法,該數組由對象和方法名稱( call_user_func_array($prepared_obj, "bind_param") ...) )組成。

編輯:考慮到它的必要性,我做了一個功能相同的功能,但改為在mysqli對象上工作:

function secure_sql($query, $values, $datatypes) {
    global $mysqli_object; //declared with $mysql_object = new mysqli(...)
    $stmt = $mysqli_object->prepare($query);
    foreach ($values as &$value) $ref_values[] = &$value;
    call_user_func_array(array($stmt, "bind_param"), array_merge(array($datatypes), $ref_values));
    $stmt->execute();
    return $stmt->get_result();
 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM