8.1.7 客户端编程 安全性指南
访问 MySQL 的客户端应用程序应遵循以下指南,以避免错误地解释外部数据或泄露敏感信息。
访问 MySQL 的应用程序不应信任用户输入的任何数据,用户可以尝试使用特殊或转义的字符序列在 Web 表单、URL 或其他应用程序中输入数据,以试图欺骗您的代码。例如,用户可以输入类似于 ; DROP DATABASE mysql;
的内容,以尝试执行 SQL 注入攻击。如果您不准备防止这种攻击,您可能会受到严重的安全漏洞和数据泄露。
常见的错误是只保护字符串数据值。请记住,也要检查数字数据。如果应用程序生成类似于 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 将自动将字符串转换为数字,并将所有非数字字符截断。
有时候,人们认为如果数据库只包含公开可用的数据,那么不需要保护。这是错误的。即使数据库中的数据是公开可用的,您仍然需要保护免受拒绝服务攻击(例如,基于前面paragraph 中描述的技术的攻击),否则您的服务器将变得对正当用户不响应。
检查列表:
-
启用严格的 SQL 模式,以告诉服务器变得更加严格地接受数据值。见 第 7.1.11 节,“Server SQL Modes”。
-
尝试在所有 Web 表单中输入单引号和双引号 (
'
和"
)。如果您收到任何 MySQL 错误,请立即调查问题。 -
尝试修改动态 URL 中的 %22 (
"
)、%23 (#
) 和 %27 ('
) 字符。您的应用程序应该能够安全地处理这些攻击。 -
尝试将动态 URL 中的数据类型从数字类型更改为字符类型,使用前面示例中的字符。您的应用程序应该能够安全地处理这些攻击。
-
尝试在数字字段中输入字符、空格和特殊符号。您的应用程序应该将它们删除,或者生成错误。将未经检查的值传递给 MySQL 是非常危险的!
-
在将数据传递给 MySQL 之前,请检查数据的大小。
-
让您的应用程序使用一个与您用于管理目的不同的用户名连接到数据库。不要将应用程序授予它们不需要的访问权限。
许多应用编程接口提供了在数据值中转义特殊字符的方式。正确使用这种方法可以防止应用程序用户输入值,以生成与您所意图不同的语句:
-
MySQL SQL 语句:使用预编译语句,并仅通过占位符接受数据值;见第15.5节,“预编译语句”。
-
MySQL C API:使用
mysql_real_escape_string_quote()
API 调用。Alternatively, 使用 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()
是字符集感知的;其他函数可以在使用无效多字节字符集时被“bypassed”。 -
Perl DBI:使用占位符或
quote()
方法。 -
Java JDBC:使用
PreparedStatement
对象和占位符。
其他编程接口可能具有相似功能。
应用程序负责拦截在执行 SQL 语句时与 MySQL 数据库服务器发生的错误,并对其进行处理。
MySQL 错误信息的返回结果不是无关紧要的,因为这些信息是调试 MySQL 应用程序的关键。例如,在调试一个10个连接的SELECT
语句时,需要提供与问题相关的数据库、表和对象的信息。因此,MySQL 错误信息中必须包含这些对象的名称引用。
当 MySQL 发生错误时,一个简单但不安全的方法是,让应用程序拦截错误并将其原样显示给客户端。然而,泄露错误信息是一种已知的应用程序漏洞类型(CWE-209),应用程序开发者必须确保应用程序不具备这种漏洞。
例如,一个显示以下信息的应用程序会暴露数据库名称和表名称给客户端,这些信息可能会被客户端尝试利用:
ERROR 1146 (42S02): Table 'mydb.mytable' doesn't exist
相反,当 MySQL 发生错误时,应用程序的正确行为是将错误信息等信息记录到一个只有信任的人员可访问的安全审核位置。应用程序可以将类似于““Internal Error””这样的信息返回给用户。