为了对抗 SQL 注入,我们实现了表名验证器。它检查字符是否都是字母数字,或者如果找到其他字符,则检查它是否存在于 JDBC 驱动程序的 getExtraNameCharacters 方法返回的字符串中:
public static void assertNameValidity(String name, Connection conn)
throws SQLException {
String suspect = name.replaceAll("[a-zA-Z0-9_]", "");
if (suspect.isEmpty()) {
/*
* XXX if the name begins with a number, it may
* XXX still be invalid, but we only care for
* XXX SQL-injection attempts anyway...
*/
logger.debug("{} is alphanumeric, a valid name", name);
return;
}
DatabaseMetaData md = conn.getMetaData();
logger.debug("'{}' contains suspect character{}: '{}'. " +
"Checking, if {} valid for a name in {}.",
name, suspect.length() == 1 ? "" : 's', suspect,
suspect.length() == 1 ? "it is" : "they are",
md.getDatabaseProductName());
/*
* Deal with the non-alphanumeric characters remaining -- they
* may still be suitable in names in certain databases.
*/
String additional = md.getExtraNameCharacters();
for (int i = 0; i < suspect.length(); i++) {
char c = suspect.charAt(i);
if (additional.indexOf(c) >= 0) {
logger.debug("Character '{}' is in '{}', " +
"thus valid part of name", c, additional);
continue;
}
logger.error("Found invalid character '{}' in \"{}\". " +
"Neither alphanumeric, nor otherwise valid",
c, name);
System.exit(3); /* XXX throw SecurityException instead? */
}
}
但是,正如经验所示,对于 SQL Server 数据库,
getExtraNameCharacters()
仅返回这三个字符:“$#@”,这会导致像 [dbo].[myTable]
这样的字符串被拒绝。作为解决方法,我们将以下块添加到上述函数中:
if (md.getDatabaseProductName() == "Microsoft SQL Server" &&
additional.indexOf('[') == -1) {
logger.info("Adding square brackets and dot to the " +
"list of acceptable characters, because Microsoft " +
"are lying to us returning only: '{}'", additional);
additional += "[].";
}
一切正常,但为什么
getExtraNameCharacters()
的结果不首先包含点和方括号?
DatabaseMetaData.getExtraNameCharacters()
的结果做出了错误的假设。其文档指出:
检索可在不带引号的标识符名称中使用的所有“额外”字符(a-z、A-Z、0-9 和 _ 之外的字符)。
ISO/IEC 9075-2:2023(SQL 标准)仅指定不带引号的标识符中允许的 a-z、A-Z、0-9 和 _,但大多数 DBMS 也允许其他字符。通过
getExtraNameCharacters()
,您可以找出这些额外的字符是什么。
但是,
[dbo].[myTable]
是一个标识符链(它不是简单的单个标识符!),由两个引用标识符组成,即[dbo]
和[myTable]
。这些方括号是 SQL Server 的默认引号。因此,不带引号的标识符中不允许使用这些方括号,也不允许使用分隔标识符链中标识符的句点 (.
),因此这些字符不包含在 getExtraNameCharacters()
的值中。
顺便说一句,我真的很想知道您正在维护什么样的具有用户提供的对象名称的动态系统,您需要验证它以防止 SQL 注入。