访问 MySQL 的客户端应用程序应使用以下指南,以避免错误地解释外部数据或泄露敏感信息。
访问 MySQL 的应用程序不应信任用户输入的任何数据,用户可能会尝试通过在 Web 表单、URL 或您构建的任何应用程序中输入特殊字符或转义字符序列来欺骗您的代码。如果用户试图通过在表单中输入类似 ; DROP DATABASE mysql;
的内容来执行 SQL 注入,请确保您的应用程序仍然安全。这是一个极端的例子,但如果您没有做好准备,黑客使用类似的技术可能会导致大规模的安全漏洞和数据丢失。
一个常见的错误是只保护字符串数据值。请记住也要检查数字数据。如果应用程序在用户输入值 234
时生成查询 SELECT * FROM table WHERE ID=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
修饰符。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 收到此类错误时,一种简单但不安全的做法是拦截该错误并将其逐字显示给客户端。但是,泄露错误信息是一种已知的应用程序漏洞类型 (CWE-209),应用程序开发人员必须确保应用程序没有此漏洞。
例如,显示如下消息的应用程序会向客户端公开数据库名称和表名,而客户端可能会尝试利用这些信息
ERROR 1146 (42S02): Table 'mydb.mytable' doesn't exist
相反,当应用程序从 MySQL 收到此类错误时,正确的行为是将适当的信息(包括错误信息)记录到只有受信任人员才能访问的安全审计位置。应用程序可以向用户返回更通用的内容,例如““内部错误””。