我想通过带有 Dremio 的 Spring Java 应用程序查询包含 parquet 文件的 S3 存储。这些是具有用户给定参数的动态查询。 我使用 Apache Arrow SQl 驱动程序,只需通过使用以下属性中的
JdbcTemplate
实例化的 DataSource
来运行查询:
driver-class-name: org.apache.arrow.driver.jdbc.ArrowFlightJdbcDriver
url: jdbc:arrow-flight-sql://localhost:32010/?useEncryption=false
username: user
password: pwd
对于 sql 字符串,我使用使用用户给定值格式化的字符串:
"SELECT * FROM "my-s3-storage".table t WHERE t.description = '%s';".formatted(userInput)
它运行良好,但不用说它有多大的 SQL 注入机会。如果我尝试使用 Prepared Statement:
String sql = "SELECT * FROM "my-s3-storage".table t WHERE t.description = ?"
jdbcTemplate.query(sql, ps -> ps.setString(1, userInput), rs -> {
//handling the result set
});
我收到以下错误:
cfjd.org.apache.arrow.flight.FlightRuntimeException: Cannot convert RexNode to equivalent Dremio expression. RexNode Class: org.apache.calcite.rex.RexDynamicParam, RexNode Digest: ?0
我在网上发现了含糊不清的信息。在一些论坛上可以找到 Dremio 不支持准备好的语句的说法,但所有这些评论都有好几年了*,而且 Dremio 官方网站有一篇文章推荐使用准备好的语句..
据我所知,Dremio 在底层使用 ANSI SQL,我相信它支持准备好的语句。或者这取决于数据库引擎而不是方言?谁能确认 Dremio 仍然不可能?那我就不再继续追求了。
如果是这种情况,我将转义不安全字符,使用字典来编码和解码用户给定的字符等。 如果您有其他建议或经验来缓解没有准备好的语句的 SQL 注入,我也将不胜感激!
谢谢!
*我发现的最新更新:https://community.dremio.com/t/sql-parameterization-support/1733/5
我将发布我的发现作为答案,也许它对同一条船上的人有用:
由于缺乏任何其他想法,我走上了编码/解码之路。
最初的想法是使用自己的字典,但我认为十六进制编码应该足够了。 幸运的是,Dremio SQL 有一个
FROM_HEX
函数,它可以 返回给定十六进制字符串的 BINARY 值。
有了它,我(到目前为止..)可以安全地构建动态查询,将任何类型的用户给定输入转换为十六进制字符串,然后在执行时转换回来,该函数的行为有点像包装器。
尽管如此,这并不是一个完全平静的解决方案,但据我所知,目前确实没有更好的选择。我听说 Dremio 将实施准备好的声明(令人震惊的是他们还没有实施),在那之前,让我们期待最好的结果!