Problem
I want to prevent a database user from discovering other tables through the use of queries like select * from pg_tables; by adding sanitization in the application that executes the query.
I'm busy investigating how to limit access to a PostgreSQL database for queries coming through an application layer where I can still sanitize a query with code. The user does not connect straight to the database. They can execute queries through an application layer (e.g. query is passed through as text and then executed by my application).
I've already enabled Row Security Policies (row level security) and the user is accessing their data through a view, so "access to data" has been solved (I believe). The problem I'm trying to solve now is to prevent them from "seeing" other tables in the database, especially the built-in PG tables.
The only grant the user has is grant select on my_view to my_user_role;
Assumption / attempted solution
My assumption is that a user can't access a table without explicitly writing it into the query, so if I were to look for certain keywords in the query string, I can reject a query without executing it. E.g. if the clause/characters "pg_tables" are anywhere in the query, then I can reject it. But, this feels naive.
const query = "select * from pg_tables;";
const flagged = query.includes("pg_tables");
if (flagged) throw Error("Not allowed!");
// Continue to run the user's query
^ This would work reliably if the only way to access a table like pg_tables is to type that out explicitly.
Question
Is there a way to access a table like pg_tables without naming it explicitly, in PostgreSQL [given the context above]?
An example of a similar situation in javascript is if you have a function fooBar(), then you can access it indirectly by calling global["foo" + "Bar"]() – hence not using the text "fooBar" exactly.
Related
I know this sounds like an "opinion" question, but I don't think it is.
Normally, I would consider not using "user" as a table name as it is a reserved word, and I would rather not bother having to deal with it when I write native SQL queries.
But I am not writing native SQL queries, but am having Symfony and the Doctrine ORM perform all the queries. And when I execute php bin/console make:user, Symfony prompts me to select the class name User which results with user as the table name. Furthermore, most of the tutorials I have read also use the name user. If using the name user is what is most common for other developers, I would rather stay consistent.
Most of the time, I don't have any issues, but every now and then, Doctrine crashes because it is querying the Postgres internal user table and not public.user. As a workaround, I tried adding * #ORM\Table(schema="public") to the User entity, but then when making migrations, it tries to duplicate the record resulting in errors. Maybe Symfony/Doctrine needs to be configured somewhere as using the public schema?
Thanks
Since my question was "Should the table name “user” be used in a Symfony project?", the following doesn't answer the question, but I am still posting it should it be helpful for others. Perhaps I will change the title to "How to use the table name “user” in a Doctrine project?", but not sure whether doing so is kosher.
I've since discovered other's experiencing issues resulting from this topic:
https://github.com/doctrine/dbal/issues/1222
https://github.com/symfony/maker-bundle/pull/545
Also found the following at doctrine-adding-mapping:
Be careful not to use reserved SQL keywords as your table or column
names (e.g. GROUP or USER). See Doctrine’s Reserved SQL keywords
documentation for details on how to escape these. Or, change the table
name with #ORM\Table(name="groups") above the class or configure the
column name with the name="group_name" option.
Which directed me to quoting-reserved-words which directed me to escape the name using ticks.
* #ORM\Table(name="`user`")
There is no table user in PostgreSQL.
Tutorials that use user as a table name are not trustworthy. However, if you consistently use double quotes, there should be no problem. Since you claim to have problems with native queries, you might have forgotten that occasionally.
One possible source of confusion is that there is a function named user in PostgreSQL. So if you use user in a context where a function is possible, you'll get a function call. If you use it with schema qualification, you will get an error that there is no such object. Otherwise, you will get a syntax error:
test=> SELECT user;
user
---------
laurenz
(1 row)
test=> SELECT * FROM user;
user
---------
laurenz
(1 row)
test=> TABLE user;
ERROR: syntax error at or near "user"
LINE 1: TABLE user;
^
test=> SELECT * FROM public.user;
ERROR: relation "public.user" does not exist
LINE 1: SELECT * FROM public.user;
^
I have a web app backed by Postgres.
Each web app request should only read/write data for the current logged-in user.
Every table with user data has a user_id column.
I occasionally have bugs where I forget to add user_id = ? to the WHERE clause of an SQL request. To protect against this problem in a general way, I'm looking into Postgres row-level security (article):
Set a policy on every user data table: CREATE POLICY table_policy ON table USING (user_id::TEXT = current_setting('app.user_id'))
In the web app, when a request begins, set the current logged-in user ID on the request's connection: SET app.user_id = ?.
This allows me to completely ignore user_id when writing SELECT and UPDATE requests.
My remaining problem is INSERTs. Is there a way to avoid having to provide user_id on INSERTs?
Just having a look at the manual :
Existing table rows are checked against the expression specified in USING, while new rows that would be created via INSERT or UPDATE are
checked against the expression specified in WITH CHECK
it seems that you just have to add a WITH CHECK clause to your policy in addition of the USING clause, and which will apply to the INSERT and UPDATE statements.
I have created the following materialized query table:
CREATE TABLE SCHEMA.TABLE AS
(SELECT * FROM SCHEMA.TABLEEXAMPLE)
DATA INITIALLY DEFERRED
REFRESH DEFERRED
MAINTAINED BY USER
DISABLE QUERY OPTIMIZATION;
When I execute a REFRESH TABLE SCHEMA.TABLE it get locked for others users to read from it.
Reading this doc from IBM https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000977.html
I tried to execute this statement:
REFRESH TABLE SCHEMA.TABLE ALLOW READ ACCESS
But I get the following error: SQL State: 42601 Unexpected keyword ALLOW
What I'm missing on statement? Is there other way to allow read access to materialized query table while it is beign updated?
MQTs on Db2 for IBM i lag behind the functionality available in Db2 for LUW.
I've never bother with them, instead an encoded vector index (EVI) with computed columns meets every need I've every considered. (Note that Db2 LUW doesn't have EVIs)
Per Mao's comment, you might try deleting an recreating the MQT with the following:
CREATE TABLE SCHEMA.TABLE AS
(SELECT * FROM SCHEMA.TABLEEXAMPLE)
DATA INITIALLY DEFERRED
REFRESH DEFERRED
MAINTAINED BY USER
DISABLE QUERY OPTIMIZATION
with NC;
But I think a refresh would still require exclusive access to the MQT.
The only options I can think of for "refreshing" an MQT while it is being used
programmatically , using either triggers on the base tables or perhaps a process that uses SQL to update a few rows at a time.
removing the DISABLE QUERY OPTIMIZATION and not accessing the MQT directly. Instead depend on the optimizer to access it when appropriate. Now you can create a version of it every few hours and the Db should start using the newer version for new queries. Once the older one is no longer being used, you delete it (or REFRESH it)
I'm in trouble with grant in postgresql (version 9.3).
I'm trying to restrict a ROLE 'client_1'. I want it to be able to do only select for one table. But there is inheritance between tables.
Here is my table structure:
CREATE TABLE public.table_a (...);
CREATE TABLE table_a_partitions.child_1 (...) INHERITS (public.table_a);
CREATE TABLE table_a_partitions.child_2 (...) INHERITS (public.table_a);
GRANT SELECT ON table_a_child_1 TO client_1;
It's okay when I do a select on child_2, there is an error, but if I do a SELECT * FROM table_a; for example it also reads the forbidden table child_2. I would my client access only child_1 (and some other in the future) results when he does SELECT * FROM table_a;.
Is there a simple way to solve this problem ?
Thank you
You would need to use a VIEW in PostgreSQL 9.3 to solve this problem. If you upgrade to 9.5, however, you could use row-level security.
As a note as to why, the grant check only occurs on the level of the initial relation queried. This means if you query a view, you need access to the view's contents, but the view owner (NOT YOU) needs access to the underlying relations. This allows a view to be useful for information hiding. Similarly with inheritance, this structure allows you to forbid rows to be inserted or queried directly from partitions of a table, but to allow different queries via the parent table. So this is a consequence of design priorities, not a bug.
Before row-level security, you would basically create a view and fold in user privilege criteria into the view (with partitioning/inheritance this is also a good idea for other reasons since your insert/update/delete triggers can return exactly what the db would do even though it cannot on a table).
As for row-level security, PostgreSQL 9.5 does allow you to specify row-level policies (conditions that get appended to insert/select/update/delete queries) and that provides something a little more manageable in some cases than the view approach.
I am using Orable DB in my application and SQL Developer Query Browser for UI Purpose.
Today I faced a very strage issue, I run a query in the query browser which gives me records successfully. But under the connection -> Table tree hierarchy, no tables are displayed.
From stackoverflow I got a solution from here : SQLDeveloper displays no tables under connections where it says tables
But under "Other Users" tree, it displays large list of schemas. So I am confused which schema is used by me query.
Any suggestion ??
Assuming that you are running a query that involves an unqualified identifier
SELECT *
FROM some_object
and that you haven't done something odd like changing the current_schema of the session, the object is either something that exists in your schema or is a public synonym.
SELECT owner, object_type, object_name
FROM all_objects
WHERE object_name = 'SOME_OBJECT'
will show you all the objects that you have access to with that name.