Mysql

防止 SQL 注入的動態列列舉

  • June 27, 2016

我有一個完全供內部使用的應用程序,但我試圖確保我的基礎涵蓋了 SQL 注入。除非其他人有一些資訊可以指出我,否則沒有辦法在不打開查詢到 SQL 注入的情況下動態指定語句中的列列表,因為準備好的語句 bindparam 不能用於指定列名。

例如(不可能,會拋出錯誤):

SELECT :column_name FROM tablename WHERE other_column_name = :value

為了仍然動態地允許呼叫列名,我必須直接傳入一個變數(有效,但容易受到注入):

SELECT $column_name FROM tablename WHERE other_column_name = :value

為了防止注入,我使用 PHP 的 in_array 函式根據表中所有列名的白名單檢查 $column_name 的值。如果它在列表中,我會繼續查詢,但如果不是,我會拋出異常。

由於應用程序中有許多不同的地方需要這樣的查詢,我在我的 PDO 包裝器中添加了一個函式,以使用 information_schema 數據庫中的數據自動列舉這些列列表:

public function schema_list($database_name = NULL, $table_name = NULL, $column_name = NULL)
{   
   $query = 'SELECT table_schema AS database_name, table_name, column_name 
       FROM `information_schema`.`columns` 
       WHERE table_schema = :database_name ';
   $parameters = array();

   // Use this object's database name unless manually specified
   if (!is_null($database_name)) {
       $parameters[':database_name'] = $database_name;
   } else {
       $parameters[':database_name'] = $this->database_detail['dbname'];
   }

   // Add details for table_name if specified
   if (!is_null($table_name)) {
       $query .= 'AND table_name = :table_name ';
       $parameters[':table_name'] = $table_name;
   }

   // Add details for column_name if specified
   if (!is_null($column_name)) {
       $query .= 'AND column_name = :column_name ';
       $parameters[':column_name'] = $column_name;
   }

   $result = $this->query_select($query, $parameters);

   if ($result['sth_count'] == 0) {
       return FALSE;
   } else {
       return $result;
   }
}

當然,這會返回一個多維記錄數組,最終需要列舉列列表的對象的建構子會:

protected $table_process_columnlist;    // List of valid columns for the process table

public function __construct(dblink $global_dblink = NULL, job $global_job = NULL)
{

// other constructor secret sauce //

// Initialize valid column list
$this->table_process_columnlist = array();
$schema_list = $this->dblink->schema_list(NULL, $this->table_process);
foreach ($schema_list['sth_result'] as $table_column) {
   $this->table_process_columnlist[] = $table_column['column_name'];
}

然後由以下人員使用:

public function __get($column)
{
   if (in_array($column, $this->table_process_columnlist)) {
       $query = 'SELECT '.$column.'  
           FROM '.$this->table_process.' 
           WHERE idprocess = :idprocess
           LIMIT 1';
       $parameters = array (
           ':idprocess' => $this->idprocess,
       );

       $result = $this->dblink->query_select($query, $parameters);

       return $result['sth_result'][0][$column];
   } else {
       $e_message = 'Could not get specified column';
       throw new ACException($e_message, 99);
   }
}

請記住,我並沒有真正使用準備好的語句來獲得性能,它們僅用於 SQL 注入保護。根據我在測試中發現的資訊,information_schema 表只會返回數據庫句柄使用者實際有權訪問的項目的結果集,因此 schema_list 函式不能用於列舉數據庫使用者沒有的數據庫的架構佈局t 已經可以訪問了。

我正在使用公共 __get 魔術函式,因為此類僅在應用程序內部使用,並且由應用程序中的其他類進一步包裝。

這是在獲得動態指定列名的能力的同時防止 SQL 注入的明智方法嗎?這種方法有任何安全問題嗎?

我沒有閱讀您的程式碼,但是是的,白名單是正確的解決方案。

您可以手動創建一個白名單數組,從而用自身的漏洞說明查詢以獲取白名單。

引用自:https://serverfault.com/questions/504917