我可以让 Ecto 记录原始 SQL 吗?

Can I get Ecto to log raw SQL?

我正在构建这样的 Ecto 查询:

from item in query,
where:  like(item.description, ^"%#{text}%")

我担心这允许 SQL 在 text 中注入。在尝试解决这个问题之前,我想看看查询实际上是如何发送到数据库的。

如果我 或查看记录的内容,我会看到一些 SQL,但它无效。

例如,检查查询显示如下:

{"SELECT i0.\"id\", i0.\"store_id\", i0.\"title\", i0.\"description\" 
  FROM \"items\" AS i0 WHERE (i0.\"description\" LIKE )",
 ["%foo%"]}

当我将此查询传递给 Repo.all 时,它记录如下:

SELECT i0."id", i0."store_id", i0."title", i0."description"
  FROM "items" AS i0 WHERE (i0."description" LIKE ) ["%foo%"]

但是如果我将其复制并粘贴到 psql,PostgreSQL 会给我一个错误:

ERROR: 42P02: there is no parameter

似乎 Ecto 实际上可能在做 parameterized query,像这样:

PREPARE bydesc(text) AS SELECT i0."id", 
  i0."store_id", i0."title", i0."description" 
  FROM "items" AS i0 WHERE (i0."description" LIKE );
EXECUTE bydesc('foo');

如果是这样,我认为这将阻止 SQL 注入。但我只是猜测这就是 Ecto 所做的。

如何查看 Ecto 正在执行的实际 SQL?

Ecto 仅使用准备好的语句。使用 ecto 查询语法时,无法引入 SQL 注入。查询语法在编译时验证没有 SQL 注入是可能的。

由于以下几个原因,准确显示执行的查询可能很困难:

  • Postgrex(以及 Ecto)使用 postgresql 二进制协议(而不是最常见但效率较低的文本协议),因此 PREPARE 查询实际上从未作为字符串存在。
  • 在大多数情况下,您只会看到一个首字母 PREPARE 64237612638712636123(...) AS ...,然后是很多 EXECUTE 64237612638712636123(...),这没什么用。试图将一个与另一个联系起来会很可怕。

根据我对大多数此类软件的经验,使用准备语句并记录它们而不是原始查询,因为它更有助于理解系统的行为。

是的,这正是 Ecto 正在执行的 SQL(它在内部通过 db_connection 包使用准备好的查询)并且在该代码中不可能有 SQL 注入.这可以通过在 postgresql.conf:

中将 log_statement 更改为 all 来打开所有已执行的 SQL 查询的日志记录来验证
...
log_statement = 'all'
...

然后重新启动 PostgreSQL 和 运行 查询。对于以下查询:

Repo.get(Post, 1)
Repo.get(Post, 2)

已记录:

LOG:  execute ecto_818: SELECT p0."id", p0."title", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."id" = )
DETAIL:  parameters:  = '1'
LOG:  execute ecto_818: SELECT p0."id", p0."title", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."id" = )
DETAIL:  parameters:  = '2'