首先, 我完全了解SQL injection vulnerabilities ,我正在将PDO用于我在PHP中开发的新应用程序。
长话短说,我工作的组织目前无法委托任何人力资源将所有人力资源转换为PDO以用于我目前正在开展的相当大的应用程序,所以我和#39; m在此期间坚持使用mysql_*
函数。
无论如何,我想知道使用数据验证功能是否安全"消毒"插值查询中使用的数字参数。我们确实使用mysql_real_escape_string()
作为字符串(是的,我也知道那里的限制)。这是一个例子:
public function foo($id) {
$sql = "SELECT * FROM items WHERE item_id = $id";
$this->query($sql); // call mysql_query and does things with result
}
$id
通过HTTP GET识别用户提供的值,因此很明显此代码容易受到攻击。如果我这样做会好的吗?
public function foo($id) {
if (!ctype_digit($id)) {
throw new \InvalidArgumentException("ID must be numeric");
}
$sql = "SELECT * FROM items WHERE item_id = $id";
$this->query($sql); // call mysql_query and does things with result
}
我很清楚,ctype_digit
与检查\d+
的正则表达式相同。
(还有filter_var($id, FILTER_VALIDATE_INT)
,但在松散类型的比较中,可能会返回int(0)
评估为FALSE
,所以我必须{ {1}}那里。)
这个临时解决方案有问题吗?
更新
=== FALSE
,boolean
,tinyint
,int
等类型的任何字段,这意味着零是完美的值得接受的值。答案 0 :(得分:3)
是的,如果你确实虔诚地使用正确的函数来验证数据,并且如果数据不符合预期,则正确地阻止查询运行,我没有看到任何漏洞。 ctype_digit
目的非常有限且明确:
如果字符串文本中的每个字符都是十进制数字,则返回
TRUE
,否则返回FALSE
。
此功能基本上没有任何问题,因此使用起来非常安全。它甚至会在空字符串上返回false
(自PHP 5.1起)。请注意,is_numeric
不是如此值得信赖。我可能仍然会添加范围检查以确保数字在预期范围内,我不确定溢出整数会发生什么。如果您在检查后另外投入(int)
,则无法注射。
警告:与所有非原生参数化查询一样,如果您进入任何带有连接字符集的恶作剧,仍有机会注入。可能漏掉的字节范围受到ctype_digit
的严格限制,但你永远不知道可以提出什么。
答案 1 :(得分:1)
使用mysql_real_escape_string
并将$id
包装在单引号中。单引号确保安全性并避免SQL注入的可能性。
例如,SELECT * FROM table WHERE id = 'escaped string'
无法被黑客攻击:SELECT * FROM table WHERE id = 1; DROP table;
因为'1; DROP table;'
将被视为WHERE
的输入参数。
答案 2 :(得分:1)
是的,它会起作用。如果值不是数字字符串,您的代码将引发异常,您只需捕获该异常并向用户显示错误消息。
请注意ctype_digit($foo)
:
true
为空,则会返回$foo
(请参阅文档); false
间隔之外的所有int
$foo
值(ASCII数字)返回[48, 57]
。如果您打算使用$foo
string
是否为非空ctype_digit($foo)
答案 3 :(得分:1)
长话短说,我工作的组织目前无法委托任何人力资源将所有人员转移到PDO
我不知道这里的问题在哪里。根据您发布的代码,您已经在使用某种DB包装器,并且已经计划更改每个数字参数的调用代码。为什么不改变该DB包装器以使其支持预处理语句,并改变调用代码来使用它?
旧的mysql ext不是问题 - 可以用它来模拟准备好的语句。
我完全了解SQL注入漏洞。
您的全面意识"有点夸张。不幸的是,大多数人不理解真正的注射源,以及准备好的声明的真正目的。
将数据与查询分开是一件好事,但完全没必要。虽然准备语句的真正价值在于其必然性,而不是手动格式化的基本任意性。
另一个你的错是对字符串的分离处理 - 它在查询中部分格式化(添加引号),部分在外部(转义特殊字符),这又是一次呼叫灾难。
当您决定坚持手动格式化时,请迟早享受您的注射。您的想法适用于人工,完全控制的沙箱示例。然而,在现实生活中,事情变得与众不同,很多人都在努力。您要求人们这样做,而不是要求程序格式化您的数据。带来所有明显的后果。
这让我想知道为什么PHP用户无法从他们的错误中吸取教训,并且仍然热切地设计实践,这在很久以前就被证明是不可靠的。
刚刚在你的推理中发现了另一个谬论
通过HTTP GET用户提供的值显然这段代码很容易受到攻击。
您必须了解任何无格式值会使此代码容易受到攻击,无论其HTTP GET,FTP PUT还是文件读取。它不仅是臭名昭着的用户输入和#34;必须正确格式化,但任何输入。这就是为什么必须使DB驱动程序成为格式化的唯一地方。 应该不是开发人员格式化数据而是程序。您的想法与这样的基石原则相矛盾。
答案 4 :(得分:0)
ctype_digit()
的大多数整数值, $id
将返回false。如果要使用该函数,请先将其强制转换为字符串:
public function foo($id) {
$id = (string)$id;
if (!ctype_digit($id)) {
throw new \InvalidArgumentException("ID must be numeric");
}
$sql = "SELECT * FROM items WHERE item_id = $id";
$this->query($sql); // call mysql_query and does things with result
}
这是因为整数被解释为ASCII值。
答案 5 :(得分:-1)
我将intval()
用于简单案例,尽管(int)
显然占用的资源较少。例如:
$sql =
"SELECT * FROM categories WHERE category_id = " .
intval($_POST['id']) .
" LIMIT 1";