Mysql
防止 SQL 注入的動態列列舉
我有一個完全供內部使用的應用程序,但我試圖確保我的基礎涵蓋了 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 注入的明智方法嗎?這種方法有任何安全問題嗎?
我沒有閱讀您的程式碼,但是是的,白名單是正確的解決方案。
您可以手動創建一個白名單數組,從而用自身的漏洞說明查詢以獲取白名單。