pgsql-hackers
❮
Add SECURITY_INVOKER_VIEWS option to CREATE DATABASE
- Jump to comment-1Steve Chavez<steve@supabase.io>Jan 27, 2026, 4:37 PM UTCHello hackers,
Currently views are not secure by default since they bypass RLS. PostgreSQL
15 introduced the `WITH (security_invoker = true)` option for this but it's
easy to miss on every new view created.
It's also inconsistent with functions, which default to SECURITY INVOKER.
I propose adding an option: `CREATE DATABASE .. SECURITYINVOKERVIEWS
<bool>` (false by default to maintain backwards compat), so a database will
have newly created views as SECURITY INVOKER.
Let me know what you think.
Best regards,
Steve Chavez- Jump to comment-1David G. Johnston<david.g.johnston@gmail.com>Jan 27, 2026, 6:02 PM UTCOn Tuesday, January 27, 2026, Steve Chavez <steve@supabase.io> wrote:
Hello hackers,
I’d be more inclined to change this incompatibility than try to affect
Currently views are not secure by default since they bypass RLS.
PostgreSQL 15 introduced the `WITH (security_invoker = true)` option for
this but it's easy to miss on every new view created.
It's also inconsistent with functions, which default to SECURITY INVOKER.
action at a distance with a database setting. But suspect the status-quo
is likely to prevail. Maybe we need a view of views that reference RLS
relations that aren’t security_invoker? Add something to the docs? If one
knows enough to enable a database setting they can institute different less
problematic solutions as well. Maybe we provide an event trigger example.
David J. - Jump to comment-1Laurenz Albe<laurenz.albe@cybertec.at>Jan 27, 2026, 5:21 PM UTCOn Tue, 2026-01-27 at 11:36 -0500, Steve Chavez wrote:
Currently views are not secure by default since they bypass RLS. PostgreSQL 15 introduced the
`WITH (security_invoker = true)` option for this but it's easy to miss on every new view created.
It's also inconsistent with functions, which default to SECURITY INVOKER.I propose adding an option: `CREATE DATABASE .. SECURITYINVOKERVIEWS <bool>` (false by default
to maintain backwards compat), so a database will have newly created views as SECURITY INVOKER.
I don't like it.
Let me know what you think.
First of all, such a setting won't guarantee that all views get created with "security_invoker"
set - the user is still free to explicitly set "security_invoker = off".
Second, and more importantly, that is a setting that changes the behavior of SQL statements,
which is something that the project has learned to fear. It is problematic if the same SQL
statement has different semantics with different settings. If somebody runs a DDL script in
a database created with SECURITYINVOKERVIEWS TRUE, it could happen that the resulting schema
causes unexpected "permission denied" errors in the application.
Yours,
Laurenz Albe- Jump to comment-1Steve Chavez<steve@supabase.io>Jan 27, 2026, 5:46 PM UTCHi Laurenz,
First of all, such a setting won't guarantee that all views get created
with "security_invoker"
set - the user is still free to explicitly set "security_invoker = off"
Yes, but that would be a conscious decision. The idea is to provide a sane
default.If somebody runs a DDL script in
a database created with SECURITYINVOKERVIEWS TRUE, it could happen that
the resulting schema
causes unexpected "permission denied" errors in the application.
IMO that's much better than leaking information by default, which views do
with security_definer.
One problem is that it could indeed be confusing if an ALTER DATABASE
modified SECURITYINVOKERVIEWS and then all queries start failing.
So one enhancement could be to only allow SECURITYINVOKERVIEWS at
creation time, like with the LOCALE option.
Best regards,
Steve Chavez
On Tue, 27 Jan 2026 at 12:21, Laurenz Albe <laurenz.albe@cybertec.at> wrote:On Tue, 2026-01-27 at 11:36 -0500, Steve Chavez wrote:
Currently views are not secure by default since they bypass RLS.
PostgreSQL 15 introduced the
`WITH (security_invoker = true)` option for this but it's easy to miss
on every new view created.
It's also inconsistent with functions, which default to SECURITY INVOKER.
I propose adding an option: `CREATE DATABASE .. SECURITYINVOKERVIEWS
<bool>` (false by default
to maintain backwards compat), so a database will have newly createdviews as SECURITY INVOKER.
Let me know what you think.
I don't like it.
First of all, such a setting won't guarantee that all views get created
with "security_invoker"
set - the user is still free to explicitly set "security_invoker = off".
Second, and more importantly, that is a setting that changes the behavior
of SQL statements,
which is something that the project has learned to fear. It is
problematic if the same SQL
statement has different semantics with different settings. If somebody
runs a DDL script in
a database created with SECURITYINVOKERVIEWS TRUE, it could happen that
the resulting schema
causes unexpected "permission denied" errors in the application.
Yours,
Laurenz Albe- Jump to comment-1Laurenz Albe<laurenz.albe@cybertec.at>Jan 27, 2026, 7:12 PM UTCOn Tue, 2026-01-27 at 12:46 -0500, Steve Chavez wrote:
If somebody runs a DDL script in
a database created with SECURITYINVOKERVIEWS TRUE, it could happen that the resulting schema
causes unexpected "permission denied" errors in the application.
That's what you think. Other people who use views with "security_barrier = on" to allow
IMO that's much better than leaking information by default, which views do with security_definer.
unprivileged users a restricted view on confidential data would be very unhappy indeed if
their CREATE VIEW statements would suddenly create views that give the users an unexpected
"permisson denied".
You might argue that they should set "security_invoker = off" explicitly if they want to
be sure that that cannot happen, but then I'm going to answer that the same applies to your
use case.One problem is that it could indeed be confusing if an ALTER DATABASE modified SECURITYINVOKERVIEWS and then all queries start failing.
I am slightly confused. I had understood your proposal to be that SECURITYINVOKERVIEWS only
So one enhancement could be to only allow SECURITYINVOKERVIEWS at creation time, like with the LOCALE option.
applies at CREATE VIEW time anyway. If you want the setting to override any "security_invoker"
setting on existing views, I like that even less, because it would prevent people from
explicitly opting out (unless you propose to change "security_invoker" into a ternary setting
with values "on", "off" and "unset").
Yours,
Laurenz Albe- Jump to comment-1Steve Chavez<steve@supabase.io>Jan 28, 2026, 7:19 PM UTCHi David, Laurenz,
Other people who use views with "security_barrier = on"
CMIIW, but wasn't security_barrier a mechanism to do RLS before actual RLS
(policies) were introduced? So in a way aren't they superseded by RLS?I’d be more inclined to change this incompatibility than try to affect
action at a distance with a database setting.
Could we instead have a shortcut for view creation like `CREATE SECURE
VIEW` (would be the same as WITH (security_invoker = true)`) ? This at
least makes it harder to forget specifying the option and also denotes that
by default views are insecure (since they're most likely created by
security_definer=superuser)
Best regards,
Steve
On Tue, 27 Jan 2026 at 14:12, Laurenz Albe <laurenz.albe@cybertec.at> wrote:On Tue, 2026-01-27 at 12:46 -0500, Steve Chavez wrote:
If somebody runs a DDL script in
a database created with SECURITYINVOKERVIEWS TRUE, it could happenthat the resulting schema
causes unexpected "permission denied" errors in the application.
IMO that's much better than leaking information by default, which viewsdo with security_definer.
That's what you think. Other people who use views with "security_barrier
= on" to allow
unprivileged users a restricted view on confidential data would be very
unhappy indeed if
their CREATE VIEW statements would suddenly create views that give the
users an unexpected
"permisson denied".
You might argue that they should set "security_invoker = off" explicitly
if they want to
be sure that that cannot happen, but then I'm going to answer that the
same applies to your
use case.One problem is that it could indeed be confusing if an ALTER DATABASE
modified SECURITYINVOKERVIEWS and then all queries start failing.
So one enhancement could be to only allow SECURITYINVOKERVIEWS at
creation time, like with the LOCALE option.
I am slightly confused. I had understood your proposal to be that
SECURITYINVOKERVIEWS only
applies at CREATE VIEW time anyway. If you want the setting to override
any "security_invoker"
setting on existing views, I like that even less, because it would prevent
people from
explicitly opting out (unless you propose to change "security_invoker"
into a ternary setting
with values "on", "off" and "unset").
Yours,
Laurenz Albe- Jump to comment-1Laurenz Albe<laurenz.albe@cybertec.at>Jan 28, 2026, 8:28 PM UTCOn Wed, 2026-01-28 at 14:19 -0500, Steve Chavez wrote:
CMIIW, but wasn't security_barrier a mechanism to do RLS before actual RLS (policies) were introduced? So in a way aren't they superseded by RLS?
I wouldn't say so.
Creating a view that allows a user to see only certain columns of a table
is something quite different from restricting the rows a user can operate on.
Yours,
Laurenz Albe- Jump to comment-1Steve Chavez<steve@supabase.io>Jan 28, 2026, 8:44 PM UTCBut that is a property of just regular views not necessarily
security_barrier right? i.e. "to be able to hide certain columns".Please don't top-post.
My bad, to be honest I don't understand how I can reply to different
paragraphs in a more structured way.
Are there guidelines/examples on the pg docs about this rule?
Best regards,
Steve
On Wed, 28 Jan 2026 at 15:28, Laurenz Albe <laurenz.albe@cybertec.at> wrote:On Wed, 2026-01-28 at 14:19 -0500, Steve Chavez wrote:
CMIIW, but wasn't security_barrier a mechanism to do RLS before actual
RLS (policies) were introduced? So in a way aren't they superseded by RLS?
I wouldn't say so.
Creating a view that allows a user to see only certain columns of a table
is something quite different from restricting the rows a user can operate
on.
Yours,
Laurenz Albe- Jump to comment-1Laurenz Albe<laurenz.albe@cybertec.at>Jan 29, 2026, 6:25 AM UTCOn Wed, 2026-01-28 at 15:43 -0500, Steve Chavez wrote:
But that is a property of just regular views not necessarily security_barrier right? i.e. "to be able to hide certain columns".
Right, but without "security_barries = on" it may be that a sneaky attacker
can subvert the security. With that setting, only LEAKPROOF functions and
operators are can be pushed into the view definition.
But we are getting off-topic. My point is that your proposed database setting
would change the behavior of such a view so that it wouldn't work any more.
Yours,
Laurenz Albe
- Jump to comment-1David G. Johnston<david.g.johnston@gmail.com>Jan 28, 2026, 7:40 PM UTCOn Wed, Jan 28, 2026 at 12:19 PM Steve Chavez <steve@supabase.io> wrote:
I’d be more inclined to change this incompatibility than try to affect
action at a distance with a database setting.
Please don't top-post.
Could we instead have a shortcut for view creation like `CREATE SECURE
VIEW` (would be the same as WITH (security_invoker = true)`) ? This at
least makes it harder to forget specifying the option and also denotes that
by default views are insecure (since they're most likely created by
security_definer=superuser)
Inventing alternative syntax with the same fundamental issue, just an
arguably different failure threshold, is unappealing.
David J.