访问 MySQL 的客户端应用程序应该遵循以下指南,以避免错误地解释外部数据或泄露敏感信息。
访问 MySQL 的应用程序不应该信任用户输入的任何数据,因为用户可能会尝试欺骗代码,例如在 Web 表单、URL 或其他应用程序中输入特殊或转义字符序列。确保您的应用程序在用户尝试执行 SQL 注入攻击时保持安全,例如输入 ; DROP DATABASE mysql;
到表单中。这是一个极端的例子,但如果您不采取防范措施,可能会导致严重的安全漏洞和数据丢失。
一个常见的错误是只保护字符串数据值。记住也要检查数字数据。如果应用程序生成查询,例如 SELECT * FROM table WHERE ID=234
,当用户输入值 234
时,用户可以输入值 234 OR 1=1
,导致应用程序生成查询 SELECT * FROM table WHERE ID=234 OR 1=1
。结果,服务器将检索表中的每一行,并导致服务器负载过高。保护这种攻击的最简单方法是使用单引号将数字常量括起来:SELECT * FROM table WHERE ID='234'
。如果用户输入额外的信息,它将成为字符串的一部分。在数字上下文中,MySQL 会自动将字符串转换为数字,并从中删除任何尾随的非数字字符。
有时人们认为,如果数据库只包含公开可用的数据,那么不需要保护。这是错误的。即使允许显示数据库中的任何行,您仍然需要保护免受拒绝服务攻击(例如,基于前一段中描述的技术,导致服务器浪费资源)。否则,您的服务器将对合法用户变得无响应。
检查清单:
-
启用严格的 SQL 模式,以告诉服务器对接受的数据值更加严格。见 第 7.1.11 节,“服务器 SQL 模式”。
-
尝试在所有 Web 表单中输入单引号 (
'
) 和双引号 ("
)。如果您遇到任何 MySQL 错误,请立即调查问题。 -
尝试修改动态 URL,添加
%22
("
),%23
(#
) 和%27
('
)。 -
尝试修改动态 URL 中的数据类型,从数字类型到字符类型,使用前面的示例字符。您的应用程序应该对这些和类似的攻击保持安全。
-
尝试在数字字段中输入字符、空格和特殊符号,而不是数字。您的应用程序应该在将它们传递给 MySQL 之前删除它们,否则生成错误。将未经检查的值传递给 MySQL 是非常危险的!
-
检查数据大小,然后将其传递给 MySQL。
-
使用不同的用户名连接到数据库,而不是用于管理目的的用户名。不要给您的应用程序授予它们不需要的访问权限。
许多应用程序编程接口提供了一种对数据值中的特殊字符进行转义的方法。正确使用这种方法可以防止应用程序用户输入值,导致应用程序生成意外的语句:
-
MySQL SQL 语句:使用 SQL 预备语句,并仅通过占位符接受数据值;见 第 15.5 节,“预备语句”。
-
MySQL C API:使用
mysql_real_escape_string_quote()
API 调用。或者,使用 C API 预备语句接口,并仅通过占位符接受数据值;见 C API 预备语句接口。 -
MySQL++:使用
escape
和quote
修饰符 для query 流。 -
PHP:使用
mysqli
或pdo_mysql
扩展,而不是旧的ext/mysql
扩展。首选的 API 支持改进的 MySQL 认证协议和密码,以及带占位符的预备语句。另见 MySQL 和 PHP。如果必须使用旧的
ext/mysql
扩展,那么对于转义,请使用mysql_real_escape_string_quote()
函数,而不是mysql_escape_string()
或addslashes()
,因为只有mysql_real_escape_string_quote()
是字符集感知的;其他函数可能在使用(无效的)多字节字符集时被““绕过””。 -
Perl DBI:使用占位符或
quote()
方法。 -
Java JDBC:使用
PreparedStatement
对象和占位符。
其他编程接口可能具有类似的功能。
应用程序负责截获 MySQL 数据库服务器执行 SQL 语句时发生的错误,并适当地处理它们。
MySQL 错误信息中返回的信息不是多余的,因为这些信息是调试 MySQL 应用程序的关键。例如,调试一个常见的 10 路连接 SELECT
语句时,需要提供有关哪些数据库、表和其他对象存在问题的信息。因此,MySQL 错误信息有时必须包含这些对象的名称。
应用程序在从 MySQL 接收到错误时采取的简单但不安全的方法是截获它并将其verbatim 显示给客户端。然而,泄露错误信息是一种已知的应用程序漏洞类型 (CWE-209),应用程序开发者必须确保应用程序不具有这种漏洞。
例如,应用程序显示以下消息时,会将数据库名称和表名称泄露给客户端,这是客户端可能尝试利用的信息:
ERROR 1146 (42S02): Table 'mydb.mytable' doesn't exist
相反,应用程序在从 MySQL 接收到错误时的正确行为是将适当的信息,包括错误信息,记录到仅供信任人员访问的安全审核位置。应用程序可以将更通用的信息,例如 “内部错误””,返回给用户。