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

Postgresql 我可以让Ecto记录原始SQL吗?,postgresql,elixir,ecto,Postgresql,Elixir,Ecto,我正在构建一个类似以下内容的外部查询: from item in query, where: like(item.description, ^"%#{text}%") PREPARE bydesc(text) AS SELECT i0."id", i0."store_id", i0."title", i0."description" FROM "items" AS i0 WHERE (i0."description" LIKE $1); EXECUTE bydesc('foo')

我正在构建一个类似以下内容的外部查询:

from item in query,
where:  like(item.description, ^"%#{text}%")
PREPARE bydesc(text) AS SELECT i0."id", 
  i0."store_id", i0."title", i0."description" 
  FROM "items" AS i0 WHERE (i0."description" LIKE $1);
EXECUTE bydesc('foo');
我担心这会允许在文本中进行SQL注入。在试图解决这个问题之前,我想看看查询实际上是如何发送到数据库的

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

例如,检查查询会显示以下内容:

{"SELECT i0.\"id\", i0.\"store_id\", i0.\"title\", i0.\"description\" 
  FROM \"items\" AS i0 WHERE (i0.\"description\" LIKE $1)",
 ["%foo%"]}
当我将此查询传递给Repo.all时,它会记录以下内容:

SELECT i0."id", i0."store_id", i0."title", i0."description"
  FROM "items" AS i0 WHERE (i0."description" LIKE $1) ["%foo%"]
但如果我将其复制并粘贴到psql中,PostgreSQL会给我一个错误:

错误:42P02:没有参数$1

看起来外太空人可能真的在做一个实验,像这样:

from item in query,
where:  like(item.description, ^"%#{text}%")
PREPARE bydesc(text) AS SELECT i0."id", 
  i0."store_id", i0."title", i0."description" 
  FROM "items" AS i0 WHERE (i0."description" LIKE $1);
EXECUTE bydesc('foo');
如果是这样,我认为这将阻止SQL注入。但我只是猜测这就是埃克托所做的


如何才能看到EXTO正在执行的实际SQL?

EXTO只使用准备好的语句。使用EXTO查询语法时,不可能引入SQL注入。查询语法在编译时验证不可能进行SQL注入

准确显示执行的查询可能很困难,原因有两个:

Postgrex和Ecto使用postgresql二进制协议,而不是最常见但效率较低的文本协议,因此PREPARE查询实际上从来没有作为字符串存在。 在大多数情况下,您只会看到一个初始准备64237612638712636123。。。像后来执行了很多64237612638712636123。。。这没什么帮助。试图把彼此联系起来是可怕的。
根据我的经验,大多数此类软件都使用prepare语句并记录它们,而不是原始查询,因为这对理解系统的行为更有帮助。

是的,这正是由Ecto执行的SQL,它在内部通过包使用准备好的查询,在该代码中不可能进行SQL注入。这可以通过在postgresql.conf中将log_语句更改为all来打开所有已执行SQL查询的日志来验证:

然后重新启动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" = $1)
DETAIL:  parameters: $1 = '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" = $1)
DETAIL:  parameters: $1 = '2'

查询语法在编译时验证不可能进行SQL注入。你是说当Ecto像eitem.description一样编译时,^%{text}%,它会为文本添加转义吗?或者说安全仅仅是因为它使用了预处理语句,所以PostgreSQL提前知道它将执行一个SELECT,而不是一个SELECT后跟一个DROP表?它不进行转义,而是使用参数foo%从来不是查询的一部分,因此它永远不能向查询中注入任何内容。它作为参数完全独立地传递给prepared语句;放下桌子——postgres会说你想把两个表达变成一个。