繁体   English   中英

在PHP的SQL查询中将变量转换为整数

[英]Casting variables to integers in SQL queries in PHP

首先, 我完全了解SQL注入漏洞,并且将PDO用于我用PHP开发的较新应用程序。

长话短说,我正在工作的组织目前无法承担任何人力资源,无法将所有工作切换到我目前正在使用的大型应用程序的PDO,因此我不得不使用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 ,所以我不得不在那做=== FALSE 。)

这个临时解决方案有什么问题吗?

更新:

  • 变量不仅包含主键,而且还包含booleantinyintintbigint等类型的任何字段,这意味着零是要搜索的完全可接受的值。
  • 我们正在使用PHP 5.3.2

是的,如果您确实虔诚地使用正确的功能来验证数据并在数据与预期不符的情况下正确地阻止查询运行,那么我看不到任何漏洞。 ctype_digit用途非常有限且明确:

如果字符串文本中的每个字符都是十进制数字,则返回TRUE否则返回FALSE

基本上没有任何问题可以使用此功能,因此可以安全使用。 它甚至会在一个空字符串上返回false (从PHP 5.1开始)。 请注意, is_numeric 不太值得信赖。 我可能仍会添加范围检查以确保数字在预期范围内,我不确定溢出整数会发生什么。 如果在此检查之后还强制转换为(int) ,则没有注入的机会。

警告虚空:与所有非本地参数化查询一样,如果您遇到任何带有连接字符集的假名,仍然有注入的机会。 可能ctype_digit的字节范围受到ctype_digit严格限制,但您永远不知道该怎么办。

使用mysql_real_escape_string并将$id括在单引号中。 单引号确保了安全性并避免了SQL注入的可能性。

例如, SELECT * FROM table WHERE id = 'escaped string'不能被黑客入侵,例如: SELECT * FROM table WHERE id = 1; DROP table; SELECT * FROM table WHERE id = 1; DROP table; '1; DROP table;' '1; DROP table;' 将被视为WHERE的输入参数。

是的,它将起作用。 如果该值不是数字字符串,则您的代码将引发异常,您只需捕获该异常并向用户显示错误消息即可。

当心ctype_digit($foo)

  • 如果$foo在PHP 5.1之前为空,则将返回true (请参阅doc);
  • 对于[48, 57]间隔(ASCII数字)之外的$foo所有int值,将返回false

因此,如果您打算使用ctype_digit($foo) 需要检查$foo是否为非空string

长话短说,我正在工作的组织目前无法委派任何人力资源来将所有内容切换到PDO

我看不出问题出在哪里。 根据发布的代码,您已经在使用某种DB包装程序,并且已经计划更改每个数字参数的调用代码。 为什么不更改该DB包装器以使其支持准备好的语句,并更改调用代码以使用它呢?

旧的mysql ext没问题-可以用它来模仿准备好的语句。

我完全知道SQL注入漏洞。

您的“全面意识”有点夸张。 不幸的是,大多数人不了解真正的注射来源以及准备好的陈述的真正目的。

从查询中分离数据的方法很不错,但是完全没有必要。 尽管准备好的声明的真正价值在于它的必然性 ,但与手动格式化的本质任意性相反。

另一个您的过错是对字符串的单独处理-它的一部分在查询中格式化(添加引号),另一部分在外部(转义特殊字符),这再次是调用灾难。

当您决定坚持使用手动格式设置时,请早晚享受注射。 您的想法对人工完全控制的沙箱示例很有帮助。 但是,在现实生活中,许多人都在忙于此事。 您不是要求程序格式化数据,而是要求人们这样做。 具有所有明显的后果。

这使我想知道为什么PHP用户无法从错误中吸取教训,而仍然急切地设计实践,而这在很久以前就被证明是不可靠的。

刚刚在您的推理中发现了另一个谬论

用户通过HTTP GET 提供的值,因此显然此代码容易受到攻击。

您必须了解, 任何未格式化的值都会使该代码易受攻击,无论其HTTP GET,FTP PUT还是文件已读取。 不仅臭名昭著的“用户输入”必须正确格式化,而且任何输入也必须正确格式化。 这就是为什么必须使DB驱动程序成为唯一发生格式化的地方的原因。 格式化数据的应该不是开发人员,而是程序。 您的想法与这样的基石原则相矛盾。

对于$id大多数整数值, ctype_digit()将返回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值。

尽管(int)显然消耗更少的资源,但我在简单情况下使用intval() 例:

$sql =
    "SELECT * FROM categories WHERE category_id = " .
    intval($_POST['id']) .
    " LIMIT 1";

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM