Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wO6ID-001JPC-0C for pgsql-bugs@arkaria.postgresql.org; Sat, 16 May 2026 04:06:25 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wO6I9-002EZp-26 for pgsql-bugs@arkaria.postgresql.org; Sat, 16 May 2026 04:06:21 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wO6I9-002EZg-0w for pgsql-bugs@lists.postgresql.org; Sat, 16 May 2026 04:06:21 +0000 Received: from mail-qk1-x72d.google.com ([2607:f8b0:4864:20::72d]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wO6I6-00000000rV4-2y09 for pgsql-bugs@lists.postgresql.org; Sat, 16 May 2026 04:06:20 +0000 Received: by mail-qk1-x72d.google.com with SMTP id af79cd13be357-9116861f004so167408285a.3 for ; Fri, 15 May 2026 21:06:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1778904377; cv=none; d=google.com; s=arc-20240605; b=YSZHDqK027821+qALF9GO/08ACKNoa4OgQ97bAFyx4N4PWZqWg97kd0TH8IF/p/WQD GJuaJSoFxYFj70N02J5/ivZoFJTHrskuoXFJVriQUbnW1D2zzBAN6wOjqzmGAyPL4FdD A4svkFon+mmi8de5YhQp/uselbM8ppCdPzzkVNLqDJ3FRiduT6eUnAFYqm9n0KYQ8GC5 sPM9Z7od/Lxnms58FQYUMGwRfcoEoCUdZXxfLX19b2ZwZZocp0ADuAqJZ05lZoZI6aRr /USrFUod6/SOqrCn6NmQh6gnDvFj9pnJ3XjMj5T6BFLffz/2aieKvTt8M+OYu2zz/d5/ 9/QA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=OVAvNhpIRlxB5NoqFznncshlNA1JmklHGRF4DY3JXjU=; fh=3fNKDR9pIAX5vRqf7Cz4hvSU11GAEiBPbgtiQxqf2gM=; b=QzHr1zoXaYavmb8Gh7GuE7NbaEUsYC8ItfKPSp3VoqAWujvYU5QFIzQoEi10Bd305T hxCx/rvI6jjGuGxmszR5wpTM6I+wvlZgh+Ex0YVLezFNmaR8iQIXUrKG14yZuatU1zbJ poPtd+Fk/eJA3DP5gu1TCVjZbPqyFgM14tlRSbOipojZGIqNAhxmohN/9jnyZ4282BF6 C/uPaKl5u9JijAAnsdCHHCFIoqFaQoalFKyDXP7MjlP3kHxNYzrN+MLOhs9f9FvY0w07 wb+xwUUE2oVdsZ/AwGfklEN+K0YuywAQPuVmjr7hVRTLAkj03Hiq0ncgSa3YGumnxYEv 8saw==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778904377; x=1779509177; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=OVAvNhpIRlxB5NoqFznncshlNA1JmklHGRF4DY3JXjU=; b=TEkQUYDXNzYBOxRR7WwPmFwz2RDCVaKo4WOMfgSCT8mxdeflf45nrC3INLcZ0hjumB GWdXKEMEuXgmeSn9ElgaQjGZkaTEhtWdf9aXG1si4klHKimrkpCj5N4HE5WgsBfdVLF/ WrywisFBluxAg1Dgw7Do32BhgX+eqi8VLQAd9k9Ihhv7rO5CwnNUA9xFv5TfWsJ4GjwL +LguhIAJcVyP8wG85A0gV2fAUZv2VYemoavGVvN4rrSVD/pQGrrPKMFOzuE/0AuWHeWg A9rWP6djPMQSaoXkOqfq4N4axjeXfLumKijjJ02KEaCjpFnvjsxRT638G6av8OZAlx+w v6Ng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778904377; x=1779509177; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=OVAvNhpIRlxB5NoqFznncshlNA1JmklHGRF4DY3JXjU=; b=rW4hUV9Z5EwyBaOvLwfJF686HOb0t53qjWW9V3UIPGutePQ49thEdz1Q5OyIuyKJjb RtyYeoDu4AtyTkHkmNqM2ac6VMuxtO7z+CiNtiHQx9yy2iHgMLKImWv4oiwYr1EQqFoT zb4NZ7tWv5CES34NGA+iRWbk0NPh5X6fOVUrA6Hkbl4aeMO/zwRE9oJaGUdX6ZIiUROl RpDYBlNmd5+0S/fy+4awl0flvaBUwGmNAoMYHNl6YHDcDOKKTjBZtQBkCl49o73mkDZl +lxDU/cudpts8UEiaYsHLu1QHoAY8qqjhklU6mwvN8Tm8W+F7yX/C0lciRRWNUu8u2mJ FV6g== X-Gm-Message-State: AOJu0YxCWWeOgdiFr6TVTK0CXkUVbflHbVnOUB9mm816cwZmofkFHUgG +6dIon5v23TpM7l+MQFezDRgqXyynhU4TuqsThxVK1lRR+TRC8kqCqUPPxMlRISX7kZ5myxxyhk MZQ2F+we91CZKVSqkWA1IJlHnj3Gqe0E= X-Gm-Gg: Acq92OFjcs654D2wCIQ92Cfa00rkGU1QvUxopkCFgDQJyIY+TpgoFHPmkb4IysQMb73 hMTAOzVUoBK9lkDastYBtTrxdj3NX8weMz8T9dUXzJPizzFF26WckcRZqBEepIl4pI+EcPHRzs7 u8Y2WFjFt3PevSQwieO7uCwhh/7tVWuHt9uR4KbwhwH8IqCs/rmLsei+16ldn4NJCfzemxHBD6I BCBCogu9tUUKYAzP5FzcwY82Kp6eYA+XwXNdqq/YUzC7Mj7RrSftMfG+DzAArRBtmFlhpaIZcHT zFyu0UOrKHcQzf2QzdyYuiAlVY8LnK2YK1k2Ls8I4hc8YJFTBzJBAjmZnZdISnqh0mz36zZbzIt WurzKeEiqtOjahhk1o1p4RCXlPiM= X-Received: by 2002:a05:620a:258d:b0:8cf:c513:349c with SMTP id af79cd13be357-911cdd431f7mr1063741685a.9.1778904377023; Fri, 15 May 2026 21:06:17 -0700 (PDT) MIME-Version: 1.0 References: <19478-37289e8b0d1a1299@postgresql.org> In-Reply-To: From: Kirill Reshke Date: Sat, 16 May 2026 09:06:05 +0500 X-Gm-Features: AVHnY4LqzcXhWbh1n9n9VsyzuWHqPJnOqZlU1fAFBF-oCckvEeULHI5HUQsVBJw Message-ID: Subject: Re: BUG #19478: `dblink_close` can be used for injection. To: Japin Li Cc: PostgreSQL mailing lists , zengman Content-Type: multipart/alternative; boundary="0000000000007b61330651e773f8" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000007b61330651e773f8 Content-Type: text/plain; charset="UTF-8" On Sat, 16 May 2026, 06:24 Japin Li, wrote: > On Fri, 15 May 2026 at 01:29, PG Bug reporting form < > noreply@postgresql.org> wrote: > > The following bug has been logged on the website: > > > > Bug reference: 19478 > > Logged by: Man Zeng > > Email address: zengman@halodbtech.com > > PostgreSQL version: 18.4 > > Operating system: 24.04.1-Ubuntu > > Description: > > > > Hi all, > > > > I think we can impose stricter restrictions on the parameters of > > `dblink_close`. > > For example, when calling `dblink_close`, certain operations can be > achieved > > through SQL concatenation, > > which I believe is unexpected behavior. > > > > ```sql > > postgres@zxm-VMware-Virtual-Platform:~/Z-Xiao-M$ psql > > psql (19devel) > > Type "help" for help. > > > > postgres=# \c test > > You are now connected to database "test" as user "postgres". > > test=# CREATE EXTENSION IF NOT EXISTS dblink; > > CREATE EXTENSION > > test=# SELECT dblink_connect('c', 'dbname=' || current_database()); > > dblink_connect > > ---------------- > > OK > > (1 row) > > > > test=# SELECT dblink_open('c', 'cur', 'SELECT 1'); > > dblink_open > > ------------- > > OK > > (1 row) > > > > test=# -- CLOSE: CREATE TABLE > > test=# SELECT dblink_close('c', 'cur; CREATE TABLE hacked(id int); --'); > > dblink_close > > -------------- > > OK > > (1 row) > > > > test=# \d+ hacked > > Table "public.hacked" > > Column | Type | Collation | Nullable | Default | Storage | > Compression | > > Stats target | Description > > > --------+---------+-----------+----------+---------+---------+-------------+--------------+------------- > > id | integer | | | | plain | > | > > | > > Access method: heap > > > > test=# SELECT dblink_disconnect('c'); > > dblink_disconnect > > ------------------- > > OK > > (1 row) > > > > test=# SELECT dblink_connect('c', 'dbname=' || current_database()); > > dblink_connect > > ---------------- > > OK > > (1 row) > > > > test=# SELECT dblink_open('c', 'cur', 'SELECT 1'); > > dblink_open > > ------------- > > OK > > (1 row) > > > > test=# -- CLOSE: DROP TABLE > > test=# SELECT dblink_close('c', 'cur; DROP TABLE hacked; --'); > > dblink_close > > -------------- > > OK > > (1 row) > > > > test=# \d+ hacked > > Did not find any relation named "hacked". > > test=# > > ``` > > > > This is my SQL for reproducing the problem. > > ```sql > > CREATE EXTENSION IF NOT EXISTS dblink; > > > > SELECT dblink_connect('c', 'dbname=' || current_database()); > > SELECT dblink_open('c', 'cur', 'SELECT 1'); > > > > -- CLOSE: CREATE TABLE > > SELECT dblink_close('c', 'cur; CREATE TABLE hacked(id int); --'); > > > > SELECT dblink_disconnect('c'); > > \d+ hacked > > > > SELECT dblink_connect('c', 'dbname=' || current_database()); > > SELECT dblink_open('c', 'cur', 'SELECT 1'); > > > > -- CLOSE: DROP TABLE > > SELECT dblink_close('c', 'cur; DROP TABLE hacked; --'); > > > > \d+ hacked > > SELECT dblink_disconnect('c'); > > ``` > > > > The solution to this problem is also very simple. > > ``` > > postgres@zxm-VMware-Virtual-Platform:~/code/postgres/contrib$ git diff > > diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c > > index 9798cb535bc..0a9334aa160 100644 > > --- a/contrib/dblink/dblink.c > > +++ b/contrib/dblink/dblink.c > > @@ -543,7 +543,7 @@ dblink_close(PG_FUNCTION_ARGS) > > > > conn = rconn->conn; > > > > - appendStringInfo(&buf, "CLOSE %s", curname); > > + appendStringInfo(&buf, "CLOSE %s", quote_ident_cstr(curname)); > > > > /* close the cursor */ > > res = libpqsrv_exec(conn, buf.data, dblink_we_get_result); > > ``` > > > > This is the feedback from the security team. > > ``` > > Thanks for your report. We consider dblink_close() to be caller-trusted, > > and thus this is not considered a security vulnerability. Feel free to > > resubmit to pgsql-bugs@lists.postgresql.org. > > ``` > > > > Any thought? > > According to the documentation [1], it should be a cursor name. Wrapping > it > in quotes can prevent attacks like SQL injection. I think your > modification > is correct, and we should add test cases for it. > > [1] https://www.postgresql.org/docs/current/contrib-dblink-close.html > > > -- > > regards, > > Man Zeng > > -- > Regards, > Japin Li > ChengDu WenWu Information Technology Co., Ltd. > Well, is there any actual injection? I mean, if user can execute dblink_close, then user can do an SQL with dblink_open and simply do a SQL? Unless wierd case when we only granted with close function, I guess --0000000000007b61330651e773f8 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


On Sat, 16 May 2026, 06:24 Japin= Li, <japinli@hotmail.com>= wrote:
On Fri, 15 May 2026 at 01:2= 9, PG Bug reporting form <noreply@postgresql.org> wrote:
> The following bug has been logged on the website:
>
> Bug reference:=C2=A0 =C2=A0 =C2=A0 19478
> Logged by:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Man Zeng
> Email address:=C2=A0 =C2=A0 =C2=A0 zengman@halodbtech.com
> PostgreSQL version: 18.4
> Operating system:=C2=A0 =C2=A024.04.1-Ubuntu
> Description:=C2=A0 =C2=A0 =C2=A0 =C2=A0
>
> Hi all,
>
> I think we can impose stricter restrictions on the parameters of
> `dblink_close`.
> For example, when calling `dblink_close`, certain operations can be ac= hieved
> through SQL concatenation,
> which I believe is unexpected behavior.
>
> ```sql
> postgres@zxm-VMware-Virtual-Platform:~/Z-Xiao-M$ psql
> psql (19devel)
> Type "help" for help.
>
> postgres=3D# \c test
> You are now connected to database "test" as user "postg= res".
> test=3D# CREATE EXTENSION IF NOT EXISTS dblink;
> CREATE EXTENSION
> test=3D# SELECT dblink_connect('c', 'dbname=3D' || cur= rent_database());
>=C2=A0 dblink_connect
> ----------------
>=C2=A0 OK
> (1 row)
>
> test=3D# SELECT dblink_open('c', 'cur', 'SELECT 1&= #39;);
>=C2=A0 dblink_open
> -------------
>=C2=A0 OK
> (1 row)
>
> test=3D# -- CLOSE: CREATE TABLE
> test=3D# SELECT dblink_close('c', 'cur; CREATE TABLE hacke= d(id int); --');
>=C2=A0 dblink_close
> --------------
>=C2=A0 OK
> (1 row)
>
> test=3D# \d+ hacked
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 Table "public.hacked"
>=C2=A0 Column |=C2=A0 Type=C2=A0 =C2=A0| Collation | Nullable | Default= | Storage | Compression |
> Stats target | Description
> --------+---------+-----------+----------+---------+---------+--------= -----+--------------+-------------
>=C2=A0 id=C2=A0 =C2=A0 =C2=A0| integer |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0| plain=C2=A0 =C2=A0|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= |
> |
> Access method: heap
>
> test=3D# SELECT dblink_disconnect('c');
>=C2=A0 dblink_disconnect
> -------------------
>=C2=A0 OK
> (1 row)
>
> test=3D# SELECT dblink_connect('c', 'dbname=3D' || cur= rent_database());
>=C2=A0 dblink_connect
> ----------------
>=C2=A0 OK
> (1 row)
>
> test=3D# SELECT dblink_open('c', 'cur', 'SELECT 1&= #39;);
>=C2=A0 dblink_open
> -------------
>=C2=A0 OK
> (1 row)
>
> test=3D# -- CLOSE: DROP TABLE
> test=3D# SELECT dblink_close('c', 'cur; DROP TABLE hacked;= --');
>=C2=A0 dblink_close
> --------------
>=C2=A0 OK
> (1 row)
>
> test=3D# \d+ hacked
> Did not find any relation named "hacked".
> test=3D#
> ```
>
> This is my SQL for reproducing the problem.
> ```sql
> CREATE EXTENSION IF NOT EXISTS dblink;
>
> SELECT dblink_connect('c', 'dbname=3D' || current_data= base());
> SELECT dblink_open('c', 'cur', 'SELECT 1'); >
>=C2=A0 -- CLOSE: CREATE TABLE
> SELECT dblink_close('c', 'cur; CREATE TABLE hacked(id int)= ; --');
>
> SELECT dblink_disconnect('c');
> \d+ hacked
>
> SELECT dblink_connect('c', 'dbname=3D' || current_data= base());
> SELECT dblink_open('c', 'cur', 'SELECT 1'); >
>=C2=A0 -- CLOSE: DROP TABLE
> SELECT dblink_close('c', 'cur; DROP TABLE hacked; --')= ;
>
> \d+ hacked
> SELECT dblink_disconnect('c');
> ```
>
> The solution to this problem is also very simple.
> ```
> postgres@zxm-VMware-Virtual-Platform:~/code/postgres/contrib$ git diff=
> diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
> index 9798cb535bc..0a9334aa160 100644
> --- a/contrib/dblink/dblink.c
> +++ b/contrib/dblink/dblink.c
> @@ -543,7 +543,7 @@ dblink_close(PG_FUNCTION_ARGS)
>=C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0conn =3D rconn->conn;
>=C2=A0
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0appendStringInfo(&buf, "CLOSE %s&= quot;, curname);
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0appendStringInfo(&buf, "CLOSE %s&= quot;, quote_ident_cstr(curname));
>=C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* close the cursor */
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0res =3D libpqsrv_exec(conn, buf.data,= dblink_we_get_result);
> ```
>
> This is the feedback from the security team.
> ```
> Thanks for your report.=C2=A0 We consider dblink_close() to be caller-= trusted,
> and thus this is not considered a security vulnerability.=C2=A0 Feel f= ree to
> resubmit to pgsql-bugs@lists.postgresql.org.
> ```
>
> Any thought?

According to the documentation [1], it should be a cursor name.=C2=A0 Wrapp= ing it
in quotes can prevent attacks like SQL injection.=C2=A0 I think your modifi= cation
is correct, and we should add test cases for it.

[1] https://www.postgres= ql.org/docs/current/contrib-dblink-close.html

> --
> regards,
> Man Zeng

--
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.
=


W= ell, is there any actual injection? I mean, if user can execute dblink_clos= e, then user can do an SQL with=C2=A0dblink_open and simply do a SQL? Unles= s wierd case when we only granted with close function, I guess
--0000000000007b61330651e773f8--