在PHP中SQL查询中将变量强制转换为整数
首先,我完全了解,我正在使用PDO开发用PHP开发的新应用程序 长话短说,我工作的公司目前无法为我目前正在开发的相当大的应用程序委派任何人力资源,将一切都切换到PDO,因此我一直坚持同时使用在PHP中SQL查询中将变量强制转换为整数,php,mysql,sql,sql-injection,Php,Mysql,Sql,Sql Injection,首先,我完全了解,我正在使用PDO开发用PHP开发的新应用程序 长话短说,我工作的公司目前无法为我目前正在开发的相当大的应用程序委派任何人力资源,将一切都切换到PDO,因此我一直坚持同时使用mysql.*函数 无论如何,我想知道使用数据验证函数“清理”插值查询中使用的数值参数是否安全。我们确实对字符串使用了mysql\u real\u escape\u string()(是的,我也知道其中的限制)。以下是一个例子: public function foo($id) { $sql = "S
mysql.*
函数
无论如何,我想知道使用数据验证函数“清理”插值查询中使用的数值参数是否安全。我们确实对字符串使用了mysql\u real\u escape\u 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
id通过HTTP获取用户提供的值,显然此代码易受攻击。如果我这样做可以吗
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\u digit
与检查\d+
的正则表达式相同
(还有filter\u var($id,filter\u VALIDATE\u INT)
,但在松散类型的比较中,它可能返回INT(0)
,其计算结果为FALSE
,因此我必须在那里执行==FALSE
)
这个临时解决方案有什么问题吗
更新:
- 变量不仅包括主键,还包括任何类型为
、boolean
、tinyint
、int
的字段,这意味着零是一个完全可以接受的搜索值bigint
- 我们使用的是PHP5.3.2
mysql\u real\u escape\u字符串
并将$id
用单引号括起来。单引号确保了安全性并避免了SQL注入的可能性
例如,
SELECT*FROM-table,其中id='escaped string'
不能被黑客攻击为:SELECT*FROM-table,其中id=1;升降台代码>作为'1;放下桌子
将被视为的输入参数,其中
是,它将工作。如果值不是数字字符串,代码将引发异常,您只需捕获该值并向用户显示错误消息
注意ctype\u digit($foo)
:
- 如果PHP5.1之前的
$foo
为空,则返回true
(参见文档)李>
- 对于
$foo
的所有int
值,在[48,57]
间隔(ASCII数字)之外,将返回false
因此,如果您计划使用ctype\u digit($foo)
是的,那么您还需要检查$foo
是否为非空的字符串,如果您确实认真地使用正确的函数来验证数据,并且在数据不符合预期的情况下正确地阻止查询运行,那么我看不到任何漏洞ctype_digit
的用途非常有限且明确:
如果字符串文本中的每个字符都是十进制数字,则返回TRUE
,否则返回FALSE
这个函数基本上不会出错,所以使用起来很安全。它甚至会在空字符串上返回false
(从PHP5.1开始)。请注意,是数字的
不太可信。我可能仍然会添加一个范围检查,以确保数字在预期范围内,我不确定溢出的整数会发生什么。如果您在该检查后额外强制转换为(int)
,则没有注入的机会
注意:与所有非本机参数化查询一样,如果您使用连接字符集进行任何恶作剧,仍然有注入的机会。可能漏掉的字节范围受到ctype\u digit
的严格限制,但您永远不知道可以得到什么。ctype\u 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值
长话短说,我所在的组织目前无法承担任何人力资源的委托,无法将所有工作移交给PDO
我看不出问题出在哪里。根据您发布的代码,您已经在使用某种DB包装器,并且已经计划更改每个数值参数的调用代码。为什么不修改DB包装以使其支持准备好的语句,并修改调用代码以使用它呢
旧的mysql ext不是问题——可以用它来模拟准备好的语句
我完全了解SQL注入漏洞
你的“完全意识”有点夸张。不幸的是,大多数人不了解注射的真正来源,以及准备好的声明的真正目的
从查询中分离数据是一个不错的技巧,但完全没有必要。而准备好的语句的真正价值在于它的必然性,而不是手动格式化的本质任意性
您的另一个错误是字符串的分离处理——它部分在查询中格式化(添加引号),部分在查询外部格式化(转义特殊字符),这同样是一个调用灾难
当你决定坚持手动格式化,然后享受你的注射,迟早。您的想法适用于人工、完全控制的沙箱示例。然而,现实生活中的应用程序发生了变化,许多人都在使用它。你不是要求程序格式化你的数据,而是要求人们这样做。后果显而易见
这让我想知道为什么PHP用户不能从他们的错误中吸取教训,仍然热衷于设计实践,而这在很久以前就被证明是不可靠的
刚刚在你的推理中发现了另一个谬误
a用户提供的v
$sql =
"SELECT * FROM categories WHERE category_id = " .
intval($_POST['id']) .
" LIMIT 1";