Thread: Row-level Security vs Application-level authz

Row-level Security vs Application-level authz

From
Darin Gordon
Date:
I'm trying to understand the extent that row level security in postgresql 9.5 may replace, or augment, application-level access control.

I have a fully implemented application-level access control policy.  It's not clear to me how I will integrate or replace it with RLS.

Craig Ringer mentioned in a blog post:
 "Most importantly, row-security is pluggable – in addition to looking policies up from the system catalogs, it’s also possible to use a policy hook to supply arbitrary policy from extensions. "

It seems that my options will be to record authorization into the catalog or write an extension?


Thanks

Darin


Re: Row-level Security vs Application-level authz

From
Stephen Frost
Date:
Darin,

* Darin Gordon (darinc@gmail.com) wrote:
> I'm trying to understand the extent that row level security in postgresql
> 9.5 may replace, or augment, application-level access control.

Neat!

> I have a fully implemented application-level access control policy.  It's
> not clear to me how I will integrate or replace it with RLS.

There's not very much information to go on here but there's a couple of
different ways to either integrate or replace what you have at the
application level with a combination of the PostgreSQL GRANT and POLICY
systems.

> Craig Ringer mentioned in a blog post:
>  "Most importantly, row-security is pluggable – in addition to looking
> policies up from the system catalogs, it’s also possible to use a policy
> hook to supply arbitrary policy from extensions. "
>
> It seems that my options will be to record authorization into the catalog
> or write an extension?

It's not entirely clear to me what else you'd do, but perhaps I can help
clarify by explaining what is meant by "looking policies up from the
system catalogs".  Those are policies which are implemented using the
new CREATE POLICY command available in 9.5.  Those policies can be
either specific (such as to a particular user or role) or generic (by
looking up the current role using a table, or using the currently logged
in user, and then looking up if the current record is allowed to be seen
or operated on by the user in another table).

More insight into what your current system looks like and what the
requirements are would help move this discussion from high-level
generalities to specific analysis of your use-case.

    Thanks!

        Stephen

Attachment

Re: Row-level Security vs Application-level authz

From
David G Johnston
Date:
Darin Gordon wrote
> I'm trying to understand the extent that row level security in postgresql
> 9.5 may replace, or augment, application-level access control.
>
> I have a fully implemented application-level access control policy.  It's
> not clear to me how I will integrate or replace it with RLS.
>
> Craig Ringer mentioned in a blog post:
>  "Most importantly, row-security is pluggable – in addition to looking
> policies up from the system catalogs, it’s also possible to use a policy
> hook to supply arbitrary policy from extensions. "
>
> It seems that my options will be to record authorization into the catalog
> or write an extension?

My quick take-away from RLS compared to traditional multi-tenant security
policies is that with RLS you move the security logic into the database and
leverage the native database roles.  Your model likely makes use of a single
user associated with an application and that application applies the
security logic during its interactions with the client-users that it
maintains separately.

A session is associated with one user in the database world while your
application can keep track of session state in a much less burdensome manner
and act as a proxy from the database's perspective which allows for somewhat
more flexible resource usage with a significant increase in architecture
complexity.

The disadvantage is that you cannot allow clients to access the database
directly because it has no inherent concept of what they are allowed or not
allowed to see.  If you desire that capability then having a secondary
security system based upon RLS would make sense.  Otherwise, you'd need to
decide whether adding an additional layer of protection - to catch buggy
coding in the proxy-application security layer - is worth the cost of
introducing end-user (customer) identities into the production database and
having your middle-tier manage connections and sessions with customer
identities instead of its own existing proxy identity.

David J.



--
View this message in context:
http://postgresql.nabble.com/Row-level-Security-vs-Application-level-authz-tp5839069p5839126.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: Row-level Security vs Application-level authz

From
Stephen Frost
Date:
* David G Johnston (david.g.johnston@gmail.com) wrote:
> My quick take-away from RLS compared to traditional multi-tenant security
> policies is that with RLS you move the security logic into the database and
> leverage the native database roles.  Your model likely makes use of a single
> user associated with an application and that application applies the
> security logic during its interactions with the client-users that it
> maintains separately.

Note that you could still use RLS even with a single application user
logging into PG.  This can be done by having an authentication mechanism
which is implemented in the database using a security definer function
which updates a table (most likely unlogged, as it's for current
sessions only and needs to be performant) that indicates which user is
logged in for the current database connection.  The RLS policies would
then refer to that table to determine which rows can be operated on.
The table would need to be cleaned up at the end of the session, but
that should be reasonably straight-forward to do (again, with a security
definer function).

Another option might be an extension which provides a GUC that can be
updated with a security definer function (but not otherwise) and which
is cleared at DISCARD ALL.  That requires the application to still
handle the user authentication (instead of having the security definer
function handle that), but as that's already happening today, it might
not be an issue and would still allow removal of most of the
application-side authorization complexity in favor of using RLS
policies.

    Thanks!

        Stephen

Attachment

Re: Row-level Security vs Application-level authz

From
"David G. Johnston"
Date:
On Mon, Feb 23, 2015 at 6:01 PM, Stephen Frost <sfrost@snowman.net> wrote:
* David G Johnston (david.g.johnston@gmail.com) wrote:
> My quick take-away from RLS compared to traditional multi-tenant security
> policies is that with RLS you move the security logic into the database and
> leverage the native database roles.  Your model likely makes use of a single
> user associated with an application and that application applies the
> security logic during its interactions with the client-users that it
> maintains separately.

Note that you could still use RLS even with a single application user
logging into PG.  This can be done by having an authentication mechanism
which is implemented in the database using a security definer function
which updates a table (most likely unlogged, as it's for current
sessions only and needs to be performant) that indicates which user is
logged in for the current database connection.  The RLS policies would
then refer to that table to determine which rows can be operated on.
The table would need to be cleaned up at the end of the session, but
that should be reasonably straight-forward to do (again, with a security
definer function).

​Does this still require actual roles to be created for the users in question?

I take it that the table has to be permanent otherwise you would have suggested
and unlogged temporary table as the target...

An example in the wiki of this idea would be welcomed by at least one member
of the community.

David J.
 

Re: Row-level Security vs Application-level authz

From
Stephen Frost
Date:
* David G. Johnston (david.g.johnston@gmail.com) wrote:
> On Mon, Feb 23, 2015 at 6:01 PM, Stephen Frost <sfrost@snowman.net> wrote:
>
> > * David G Johnston (david.g.johnston@gmail.com) wrote:
> > > My quick take-away from RLS compared to traditional multi-tenant security
> > > policies is that with RLS you move the security logic into the database
> > and
> > > leverage the native database roles.  Your model likely makes use of a
> > single
> > > user associated with an application and that application applies the
> > > security logic during its interactions with the client-users that it
> > > maintains separately.
> >
> > Note that you could still use RLS even with a single application user
> > logging into PG.  This can be done by having an authentication mechanism
> > which is implemented in the database using a security definer function
> > which updates a table (most likely unlogged, as it's for current
> > sessions only and needs to be performant) that indicates which user is
> > logged in for the current database connection.  The RLS policies would
> > then refer to that table to determine which rows can be operated on.
> > The table would need to be cleaned up at the end of the session, but
> > that should be reasonably straight-forward to do (again, with a security
> > definer function).
> >
>
> ​Does this still require actual roles to be created for the users in
> question?

No.

> I take it that the table has to be permanent otherwise you would have
> suggested
> and unlogged temporary table as the target...

A temporary table would have to be recreated each time and that'd be
less than ideal.  You can use a single unlogged table which includes the
backend pid (which can be acquired through a function call) to keep
track of which user is logged in on a given backend at a given point in
time.

> An example in the wiki of this idea would be welcomed by at least one member
> of the community.

It's been my intention to build that; perhaps I can find resources in
the near future to turn that into a reality.

    Thanks!

        Stephen

Attachment

Re: Row-level Security vs Application-level authz

From
David Steele
Date:
On 2/23/15 8:16 PM, Stephen Frost wrote:
> * David G. Johnston (david.g.johnston@gmail.com) wrote:
>> I take it that the table has to be permanent otherwise you would have
>> suggested
>> and unlogged temporary table as the target...
>
> A temporary table would have to be recreated each time and that'd be
> less than ideal.  You can use a single unlogged table which includes the
> backend pid (which can be acquired through a function call) to keep
> track of which user is logged in on a given backend at a given point in
> time.

It's not clear to me why creating a temp table per session would be less
than ideal.  I've certainly used session-scope temp tables to good
effect a number of times.  Transaction-scope would be another story of
course.

Am I missing something?

--
- David Steele
david@pgmasters.net


Attachment

Re: Row-level Security vs Application-level authz

From
Stephen Frost
Date:
* David Steele (david@pgmasters.net) wrote:
> On 2/23/15 8:16 PM, Stephen Frost wrote:
> > * David G. Johnston (david.g.johnston@gmail.com) wrote:
> >> I take it that the table has to be permanent otherwise you would have
> >> suggested
> >> and unlogged temporary table as the target...
> >
> > A temporary table would have to be recreated each time and that'd be
> > less than ideal.  You can use a single unlogged table which includes the
> > backend pid (which can be acquired through a function call) to keep
> > track of which user is logged in on a given backend at a given point in
> > time.
>
> It's not clear to me why creating a temp table per session would be less
> than ideal.  I've certainly used session-scope temp tables to good
> effect a number of times.  Transaction-scope would be another story of
> course.
>
> Am I missing something?

The problem with a temporary table is, well, it goes away. :)  There are
further concerns that, because it's created in some fashion by the
single application user, it might be less secure.  Really, though, I'd
want it to be real so that it could have constraints be on it which
reference other appropriate tables, so the web user doesn't have to have
rights in any fashion to create objects, and so that it can be
referenced from RLS policies.  A table as transient as a temporary table
doesn't strike me as the right solution for that.

    Thanks!

        Stephen

Attachment

Re: Row-level Security vs Application-level authz

From
David Steele
Date:
On 2/24/15 3:07 PM, Stephen Frost wrote:
> * David Steele (david@pgmasters.net) wrote:
>> On 2/23/15 8:16 PM, Stephen Frost wrote:
>>> * David G. Johnston (david.g.johnston@gmail.com) wrote:
>>>> I take it that the table has to be permanent otherwise you would have
>>>> suggested
>>>> and unlogged temporary table as the target...
>>>
>>> A temporary table would have to be recreated each time and that'd be
>>> less than ideal.  You can use a single unlogged table which includes the
>>> backend pid (which can be acquired through a function call) to keep
>>> track of which user is logged in on a given backend at a given point in
>>> time.
>>
>> It's not clear to me why creating a temp table per session would be less
>> than ideal.  I've certainly used session-scope temp tables to good
>> effect a number of times.  Transaction-scope would be another story of
>> course.
>>
>> Am I missing something?
>
> The problem with a temporary table is, well, it goes away. :)  There are
> further concerns that, because it's created in some fashion by the
> single application user, it might be less secure.  Really, though, I'd
> want it to be real so that it could have constraints be on it which
> reference other appropriate tables, so the web user doesn't have to have
> rights in any fashion to create objects, and so that it can be
> referenced from RLS policies.  A table as transient as a temporary table
> doesn't strike me as the right solution for that.

Temp tables go away at the end of the session, sure.  It seems like
exactly the time when you'd want them to do so.

If the temp table is created by a security definer function (as was
suggested earlier) then no special user privs are required.

Being referenced from RLS polices is a good argument, though.  I guess
that's not possible with a temp table?  Are they pre-parsed?

--
- David Steele
david@pgmasters.net


Attachment

Re: Row-level Security vs Application-level authz

From
Stephen Frost
Date:
* David Steele (david@pgmasters.net) wrote:
> On 2/24/15 3:07 PM, Stephen Frost wrote:
> > The problem with a temporary table is, well, it goes away. :)  There are
> > further concerns that, because it's created in some fashion by the
> > single application user, it might be less secure.  Really, though, I'd
> > want it to be real so that it could have constraints be on it which
> > reference other appropriate tables, so the web user doesn't have to have
> > rights in any fashion to create objects, and so that it can be
> > referenced from RLS policies.  A table as transient as a temporary table
> > doesn't strike me as the right solution for that.
>
> Temp tables go away at the end of the session, sure.  It seems like
> exactly the time when you'd want them to do so.
>
> If the temp table is created by a security definer function (as was
> suggested earlier) then no special user privs are required.
>
> Being referenced from RLS polices is a good argument, though.  I guess
> that's not possible with a temp table?  Are they pre-parsed?

Actually, it is possible, but it creates a dependency on the temporary
table and when the temporary table goes away, so will the policy.  This
isn't a huge issue for RLS, of course, as if there's no policy then the
default-deny policy will kick in, but you can't have a policy against a
temporary table continue past the end of that session.

Following the earlier discussion, I suppose you could create both the
temporary table and the policies in the security definer function, but
it feels a lot cleaner to have a real table for all of that, in my view,
to keep that security definer function nice and simple.

Further, there's lots of other reasons to have a session table anyway,
from an application standpoint, and so this feels like an approach which
is more in-line with how the application likely wants to operate anyway.
It's also handy to be able to log into the database and see all the
current sessions, similar to how we have pg_stat_activity.

    Thanks!

        Stephen

Attachment

Re: Row-level Security vs Application-level authz

From
David Steele
Date:
On 2/24/15 3:31 PM, Stephen Frost wrote:
> * David Steele (david@pgmasters.net) wrote:
>> On 2/24/15 3:07 PM, Stephen Frost wrote:
>>> The problem with a temporary table is, well, it goes away. :)  There are
>>> further concerns that, because it's created in some fashion by the
>>> single application user, it might be less secure.  Really, though, I'd
>>> want it to be real so that it could have constraints be on it which
>>> reference other appropriate tables, so the web user doesn't have to have
>>> rights in any fashion to create objects, and so that it can be
>>> referenced from RLS policies.  A table as transient as a temporary table
>>> doesn't strike me as the right solution for that.
>>
>> Temp tables go away at the end of the session, sure.  It seems like
>> exactly the time when you'd want them to do so.
>>
>> If the temp table is created by a security definer function (as was
>> suggested earlier) then no special user privs are required.
>>
>> Being referenced from RLS polices is a good argument, though.  I guess
>> that's not possible with a temp table?  Are they pre-parsed?
>
> Actually, it is possible, but it creates a dependency on the temporary
> table and when the temporary table goes away, so will the policy.  This
> isn't a huge issue for RLS, of course, as if there's no policy then the
> default-deny policy will kick in, but you can't have a policy against a
> temporary table continue past the end of that session.

It's not good if policies go away, even with a default.

> Following the earlier discussion, I suppose you could create both the
> temporary table and the policies in the security definer function, but
> it feels a lot cleaner to have a real table for all of that, in my view,
> to keep that security definer function nice and simple.

Then you'd have a policy for each user which sounds messy, or some crazy
locking scheme which sounds worse.  I agree that a real table sounds
like a better solution.

> Further, there's lots of other reasons to have a session table anyway,
> from an application standpoint, and so this feels like an approach which
> is more in-line with how the application likely wants to operate anyway.
> It's also handy to be able to log into the database and see all the
> current sessions, similar to how we have pg_stat_activity.

Well, I am a fan of monitoring.

So I guess my last question is if you are inserting rows into a table to
track user connections, how do you clean them out when the client does
not disconnect cleanly?  Or is this table intended to be append-only?

--
- David Steele
david@pgmasters.net


Attachment

Re: Row-level Security vs Application-level authz

From
Stephen Frost
Date:
* David Steele (david@pgmasters.net) wrote:
> So I guess my last question is if you are inserting rows into a table to
> track user connections, how do you clean them out when the client does
> not disconnect cleanly?  Or is this table intended to be append-only?

It wouldn't be intended to be append-only but I agree that, ideally,
there'd be a way to address clients disconnect uncleanly.  One way to
address that would be by having the security definer function that's
called on entry check to see if there was a prior session for its pid
and log an error when found.  With a connection pooler, that'd probably
turn up any issues pretty quickly as the set of pids would be relatively
small (compared to the overall potential pid space).  Another approach
would be to have it check for all backends by joining against
pg_stat_activity, but that might result in false positives.  A cron job
could also be used to check for sessions beyond a certain expected
lifetime (PHP and other systems do this at the filesystem level; it's
not ideal but it does seem to work).

    Thanks!

        Stephen

Attachment

Re: Row-level Security vs Application-level authz

From
Adam Hooper
Date:
On Tue, Feb 24, 2015 at 8:37 PM, Stephen Frost <sfrost@snowman.net> wrote:
> * David Steele (david@pgmasters.net) wrote:
>> So I guess my last question is if you are inserting rows into a table to
>> track user connections, how do you clean them out when the client does
>> not disconnect cleanly?  Or is this table intended to be append-only?
>
> It wouldn't be intended to be append-only but I agree that, ideally,
> there'd be a way to address clients disconnect uncleanly.

This is starting to sound like a web app, which I have experience
with. The cardinal rule: assume everybody disconnects randomly, and
code accordingly :).

The goal here isn't to make the session table reflect the number of
users who are currently logged in. Rather, it's to ensure the session
table doesn't grow infinitely.

* You can set a restriction like, "a user can only be logged in once".
During login, delete other sessions associate with that user. The
session table's maximum size is the size of the user table.

* You can use an expiry-check function. Best is a definitive "this
session is disconnected"; if you can't do that, you can try a rule
such as "user cannot be logged in more than 20 times and sessions
older than two weeks are invalid". During login, run the expiry
checker on that user and delete expired rows. With the right function,
you can constrain the session table to a reasonable size.

* You can simply limit the size of the session table. If your limit is
100 and a user starts a 101st session, delete the first session.

The world of websites involves lots of users and loads of short-lived
sessions. A website doesn't check whether the user has access to a
row: it checks whether the user has access to an endpoint with the
given parameters. Postgres RLS seems like a bad approach for that use
case.

Enjoy life,
Adam

--
Adam Hooper
+1-613-986-3339
http://adamhooper.com


Re: Row-level Security vs Application-level authz

From
Stephen Frost
Date:
Adam,

* Adam Hooper (adam@adamhooper.com) wrote:
> On Tue, Feb 24, 2015 at 8:37 PM, Stephen Frost <sfrost@snowman.net> wrote:
> > * David Steele (david@pgmasters.net) wrote:
> >> So I guess my last question is if you are inserting rows into a table to
> >> track user connections, how do you clean them out when the client does
> >> not disconnect cleanly?  Or is this table intended to be append-only?
> >
> > It wouldn't be intended to be append-only but I agree that, ideally,
> > there'd be a way to address clients disconnect uncleanly.
>
> This is starting to sound like a web app, which I have experience
> with. The cardinal rule: assume everybody disconnects randomly, and
> code accordingly :).

Haha, I like it. :)

> The goal here isn't to make the session table reflect the number of
> users who are currently logged in. Rather, it's to ensure the session
> table doesn't grow infinitely.

Agreed.

> The world of websites involves lots of users and loads of short-lived
> sessions. A website doesn't check whether the user has access to a
> row: it checks whether the user has access to an endpoint with the
> given parameters. Postgres RLS seems like a bad approach for that use
> case.

Right, that all certainly makes sense to me, but I'm not sure the idea,
as it relates to RLS, was clear.

Consider that you *already* have per-user data in the system.  This
might be in the form of facebook friends, with perhaps a friend mapping
table:

CREATE TABLE friendships (
  friend_source    text,
  friend_dest      text,
  primary key (friend_source, friend_dest)
);

Then you have a sessions table, ala:

CREATE TABLE sessions (
  pg_pid           integer,
  username         text,
  attribute1       text,
  attribute2       text,
  etc
);

Now, you want a given DB session to only be able to see their friends
and not the friends of others, so you might have a policy on friendships
like so:

CREATE POLICY friend_policy ON friendships USING (
  friend_source = (
    SELECT username FROM sessions WHERE pg_pid = pg_backend_pid()
  )
);

The sessions table isn't where RLS is really being used, it's on the
other tables.  The session table is used just as a way to figure out
which user is currently logged in for the purposes of the filter which
is applied via RLS.  Having a GUC or server-side variable of some kind
would work too, provided it had the right characteristics (which is
mostly about making sure that the web app can't somehow "fake" the
user's credentials and become whichever user it wants- this would be
done in the above approach by having a security definer function which
requires credentials from the user to be passed in and only if that
matches is the session table updated to indicate that user as the one
who is logged in; that's not a complete fail-safe, of course, but it's a
lot better than the usual case of the web application having unfettered
access to the data in the database).

Note that the above is all off the cuff for this discussion and may have
syntax or other issues with it. :)

    Thanks!

        Stephen

Attachment

Re: Row-level Security vs Application-level authz

From
"Karsten Hilbert"
Date:
> * David Steele (david@pgmasters.net) wrote:
> > So I guess my last question is if you are inserting rows into a table to
> > track user connections, how do you clean them out when the client does
> > not disconnect cleanly?  Or is this table intended to be append-only?
>
> It wouldn't be intended to be append-only but I agree that, ideally,
> there'd be a way to address clients disconnect uncleanly.  One way to
> address that would be by having the security definer function that's
> called on entry check to see if there was a prior session for its pid
> and log an error when found.  With a connection pooler, that'd probably
> turn up any issues pretty quickly as the set of pids would be relatively
> small (compared to the overall potential pid space).  Another approach
> would be to have it check for all backends by joining against
> pg_stat_activity, but that might result in false positives.  A cron job
> could also be used to check for sessions beyond a certain expected
> lifetime (PHP and other systems do this at the filesystem level; it's
> not ideal but it does seem to work).

One could use an exclusive lock. When the client goes away
so does the lock.

If the security definer func can get the lock on an existing
entry the entry is bogus.

Karsten


Re: Row-level Security vs Application-level authz

From
Dowwie
Date:
Thanks, Stephen and David for your responses.   My setup is as you described
it.  Thanks for clarifying.














--
View this message in context:
http://postgresql.nabble.com/Row-level-Security-vs-Application-level-authz-tp5839069p5839291.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.