Java 查询字符串中需要动态 table 变量时的 SQL 注入问题

SQLinjection concerns when dynamic table variable is needed in Java query string

场景:

我有大约 50 tables 我希望能够在每次用户单击 Java "update all" 按钮时从本地数据库中提取数据并刷新。 我无法写入远程数据库,或者我只能使用准备好的语句或存储过程创建视图。

为每个 table 创建一个自定义准备语句似乎很难维护,但我也不想让我的代码容易受到 SQLInjection 的攻击。

为了降低风险,我有一个 table 名称的白名单(来自本地数据库),有限的字符串长度,指定的 tables(数据和日期),并添加了标记 table_name 到字符串连接中。

我希望通过添加标记字段,大多数 SQL 注入都会因语法错误而失败。这些将通过 try catch 处理,伤口不会造成伤害(可能是错误的)。

虽然可以更改 table 名称,但我不太担心,因为它必须是具有适当结构的合法 table,而且我正在获取所有table 无论如何。

问题:

使用下面的预准备语句我还应该注意哪些其他 SQL 注入问题?

List<String> tableNamesWhiteList; 
...

  for (String t:tableNamesWhiteList)
  {       
    if(t.length < 30)
{
  try{
      String stm = "select '"+t+"' as table_name, data, date from " + t +" where data = ?";
      pst = con.prepareStatement(stm);
      pst.setInt(1, 100);
      rs = pst.executeQuery();
....            

希望这是有道理的

我将通过指出我不是安全分析师来开始我要说的内容。

我不认为你在这里所做的是有效的或必要的。首先,SQL 注入依赖于解析器读取到第一个有效的 SQL 语句的末尾并在继续之前执行它。因此,如果上面的代码完全 是易受攻击的,(我不认为是,但我稍后会介绍,)您的查询

select '"+t+"' as table_name, data, date from " + t +" where data = ?

可以通过设置 t 等于

来攻击
1' from pg_users; drop table pg_users;

并且解析器将执行 SELECT,然后执行 DROP,然后在 AS 上阻塞,但到那时为时已晚。这就是 SQL 注入的工作原理。

不过,您使用的是准备好的语句这一事实意味着,在执行任何查询字符串之前,必须从一端到另一端解析整个查询字符串,并且任何通过 [=13 注入的尝试=] 几乎肯定会在查询的末尾留下无效的语法,这会在解析器执行之前阻塞解析器。即使情况并非如此,您的 table 名称也来自您自己的代码,从预设列表中挑选,如果它被泄露,您需要担心的问题比 SQL 注入更大。

编写安全代码固然好,但为代码镀上一层装甲会使它变得缓慢且难以维护,而且很少有正当理由。

不过,您可能会考虑一个替代方案,它用于我几年前开发的商业产品。另一种方法是将 50 个 table 的查询存储在另一个 table 中,可能由 table 名称索引。代码可能看起来像这样:

String stm = "SELECT query FROM table_queries WHERE table_id = ?";
pst = con.prepareStatement(stm);
pst.setInt(1, tableID);
rs = pst.executeQuery();
stm = rs.first().getString("query");
pst = con.prepareStatement(stm);
pst.setInt(1, 100);
rs = pst.executeQuery();