我有一些代码利用参数化查询来防止注入,但我还需要能够动态构造查询,而不管表的结构如何。这样做的正确方法是什么?
这是一个例子,假设我有一个包含名称,地址,电话列的表格。我有一个网页,我在其中运行显示列并使用它们作为选项填充选择下拉列表。
接下来,我有一个名为搜索的文本框。此文本框用作参数。
目前我的代码看起来像这样:
result = pquery('SELECT * FROM contacts WHERE `' + escape(column) + '`=?', search);但是我从中得到了一种icky的感觉。我使用参数化查询的原因是为了避免使用转义。此外,转义可能不是为转义列名而设计的。
我怎样才能确保这个按照我的意图运作?
修改: 我需要动态查询的原因是架构是用户可配置的,我不会修复硬编码的任何内容。
答案 0 :(得分:6)
不要传递列名,只需使用硬编码表传递代码将转换为列名的标识符。这意味着您无需担心传递恶意数据,因为所有数据都是合法翻译的,或者已知无效。 Psudoish代码:
@columns = qw/Name Address Telephone/;
if ($columns[$param]) {
$query = "select * from contacts where $columns[$param] = ?";
} else {
die "Invalid column!";
}
run_sql($query, $search);
答案 1 :(得分:0)
诀窍是对你的逃避和验证程序充满信心。我使用自己的SQL转义函数,它为不同类型的文字重载。我无处可直接从用户输入插入表达式(与引用的文字值相反)。
仍然可以这样做,我建议使用一个单独的 - 严格 - 函数来验证列名。允许它只接受一个标识符,例如
/^\w[\w\d_]*$/
您必须依赖关于自己的列名的假设。
答案 2 :(得分:0)
我使用ADO.NET以及SQL命令和SQLParameters来处理那些处理Escape问题的命令。因此,如果您也在Microsoft工具环境中,我可以说我非常有用地构建动态SQL并保护我的参数
祝你好运答案 3 :(得分:0)
根据另一个查询的结果将列设置为枚举可能的架构值的表。在第二个查询中,您可以将select硬编码为用于定义架构的列名。如果没有返回任何行,则输入的列无效。
答案 4 :(得分:0)
在标准SQL中,用双引号括起分隔标识符。这意味着:
SELECT * FROM "SomeTable" WHERE "SomeColumn" = ?
将从名为SomeTable的表中选择显示的大小写(不是名称的大小写转换版本),并将条件应用于名为SomeColumn的列,并显示大写字母。
本身,这不是很有帮助,但是......如果你可以使用双引号将escape()技术应用于通过网络表单输入的名称,那么你可以合理地自信地建立你的查询。
当然,你说你想避免使用转义 - 事实上你不必在你提供的参数上使用它吗?占位。但是,如果要将用户提供的数据放入查询中,则需要保护自己免受恶意攻击者的侵害。
不同的DBMS有不同的方式提供分隔标识符。例如,MS SQL Server似乎使用方括号[SomeTable]而不是双引号。
答案 5 :(得分:0)
某些数据库中的列名可以包含空格,这意味着您必须引用列名,但如果您的数据库不包含此类列,则只需在拼接之前通过正则表达式或某种检查运行列名SQL:
if ( $column !~ /^\w+$/ ) {
die "Bad column name [$column]";
}