用于防止SQL注入的dynamic列枚举

我有一个完全供内部使用的应用程序,但是我试图确保我的SQL基础覆盖了SQL注入。 除非有其他人有一些信息指向我,否则没有办法dynamic地指定列表中的列列表,而无需打开SQL注入的查询,因为准备好的语句bindparam不能用于指定列名称。

例如(不可能的,会抛出错误):

SELECT :column_name FROM tablename WHERE other_column_name = :value 

为了仍然dynamic允许调用列名,我直接必须传入一个variables(工作,但易受注入):

 SELECT $column_name FROM tablename WHERE other_column_name = :value 

为了防止注入,我使用PHP的in_array函数针对表中所有列名的白名单检查$ column_name的值。 如果它在列表中,我继续查询,但如果不是,我会抛出一个exception。

由于在应用程序中有许多不同的地方需要这样的查询,所以我添加了一个函数给我的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; } } 

当然这返回一个多维的logging数组,最终需要列举列表的对象的构造函数:

 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注入保护。 从我在testing中发现的信息来看,information_schema表只会返回数据库句柄用户实际可以访问的项的结果集,所以schema_list函数不能用来枚举数据库的schema布局,已经有权访问。

我正在使用一个公共的__get魔术函数,因为这个类只在应用程序内部使用,并被应用程序中的其他类包装。

这是一个理想的方式来防止SQL注入,同时获得dynamic指定列名称的能力? 这种方法有没有安全问题?

我没有读你的代码,但是,是的,白名单是在这里正确的解决scheme。

您可以手动创build一个白名单数组,从而用自己的漏洞照亮查询来获取白名单。