Na resposta muito útil que recebi para uma pergunta anterior , estou tentando escrever algum código JDBC que primeiro defina a função para um usuário específico antes de executar consultas subsequentes. Por segurança, gostaria de evitar ataques de injeção de SQL parametrizando a SET ROLE
instrução. Minha abordagem no Groovy (que usa JDBC) foi:
def sql = Sql.newInstance('jdbc:postgresql:mydb', 'mydbweb', 'mydbwebpass', 'org.postgresql.Driver')
sql.execute 'SET ROLE ?', user
mas isso gera um erro de sintaxe. A documentação diz que SET ROLE
pode levar uma string literal, mas não estou claro como posso passá-la validamente. Alguma sugestão?
Com base em um caso de teste simples que acabei de escrever:
Eu acho que o erro que você se refere é provavelmente:
A razão para isso é que o protocolo do PostgreSQL só pode vincular parâmetros de posicionamento para instruções "planejáveis", que são
SELECT
,INSERT
,UPDATE
eDELETE
algumas outras. Para esses tipos de instrução, ele suporta apenas parâmetros de posicionamento para valores literais, não para identificadores ou outros elementos de sintaxe.Para outros tipos de instrução, os literais devem ser substituídos pelo cliente. Alguns drivers oferecem suporte à emulação do lado do cliente para preparar essas instruções, portanto, eles parecem funcionar de forma transparente, mas o PgJDBC atualmente não oferece suporte a isso.
Na verdade, o PgJDBC oferece suporte à vinculação de parâmetros do lado do cliente se você estiver usando o protocolo legado v2, mas não expõe essa funcionalidade para conexões no protocolo v3. Mesmo se você não estiver usando instruções preparadas do lado do servidor, ele ainda usará a ligação de parâmetro do lado do servidor. Forçar o protocolo v2 funcionará, mas é uma solução ruim com muitas outras consequências em outros lugares - além disso, em algum momento, o PostgreSQL abandonará totalmente o suporte para o protocolo v2.
Eu acho que isso é um problema com o driver e/ou protocolo de fio do PostgreSQL. O usuário não deve se preocupar se o servidor suporta vinculação de parâmetros para algumas instruções e não para outras, o driver deve cuidar disso - ou melhor, o servidor deve apenas suportá-lo para todos os tipos de instrução.
Infelizmente, o PgJDBC também não expõe suas implementações internas de identificador seguro e escape literal para uso do aplicativo cliente. Se
standard_conforming_strings
estiver ativado, porém, a regra é muito simples:'
por''
; eO mesmo se aplica aos identificadores, com aspas duplas.
Você pode criar uma função que executa
SET ROLE
com SQL dinâmico, usandoformat
para inserir com segurança o identificador de função (%I
insere um identificador, colocando aspas duplas em torno dele, se necessário, e escapando aspas duplas na string, dobrando-as, se necessário). Algo na linha deVocê pode então executar essa função com algo como
SELECT setrole(?)
. Isso deve funcionar bem com os parâmetros.Aqui está um exemplo dele em uso em
psql
:Tanto quanto pude testar com ,
SET ROLE role_name
também pode ser obtido usando uma função set_config, por exemploSELECT set_config('role', 'role_name', TRUE)
, e desta forma os parâmetros podem ser usados:SELECT set_config($1, $2, TRUE)
.Há também uma nota na documentação do Postgres que
set_config
fornece a mesma funcionalidadeSET
(mas mesmo queROLE
não seja mencionada lá, ainda funcionou para mim).Versão do PostgreSQL: PostgreSQL 12.2 em x86_64-pc-linux-musl, compilado por gcc (Alpine 9.2.0) 9.2.0, 64 bits