This document describes how to use parameterized secure views in AlloyDB for PostgreSQL, which let you limit data access based on application-specific named parameters, like application user credentials. Parameterized secure views improve security and access control by extending the functionality of PostgreSQL views. These views also mitigate the risks of running untrusted queries from applications by enforcing a number of restrictions automatically on any query that is executed.
For more information, see the parameterized secure views overview and the parameterized secure views tutorial.
Before you begin
This document assumes that you created an AlloyDB cluster and instance. For more information, see Create a database.
Before you can use parameterized secure views, you must do the following:
Request access to parameterized secure views and wait until you receive the enablement confirmation before you begin.
Wait for the AlloyDB team to enable the
parameterized_views.enabled
database flag, which loads the required extension libraries. This database flag must be enabled before you can begin.After the AlloyDB team enables the
parameterized_views.enabled
database flag, your database restarts to make these changes take effect.Use AlloyDB Studio or psql to create the
parameterized_views
extension in any database where a parameterized view is created:-- Requires parameterized_views.enabled set to true CREATE EXTENSION parameterized_views;
When the extension is created, a schema named
parameterized_views
is also created by the system so that the APIs are contained in that schema's namespace, and so that the APIs don't conflict with existing APIs.
Create a parameterized secure view
To create a parameterized secure view, follow these steps:
Run the
CREATE VIEW
DDL command, as shown in the following example:CREATE VIEW secure_checked_items WITH (security_barrier) AS SELECT bag_id, timestamp, location FROM checked_items t WHERE customer_id = $@app_end_userid;
In the preceding example, the parameterized secure view allows access to three columns from a table named
/users/checked_items/
. The view limits the results to rows where/users.id/checked_items.customer_id/
matches a required parameter.Use the following attributes:
- Create the view using the
security_barrier
option. - To restrict application users so that they can only view the rows
they're allowed to access, add required parameters using the
$@PARAMETER_NAME
syntax in theWHERE
clause. A common use case is checking the value of a column usingWHERE COLUMN = $@PARAMETER_NAME
. $@PARAMETER_NAME
indicates a named view parameter. Its value is provided when you use theexecute_parameterized_query
API. Named view parameters have the following requirements:- Named view parameters must begin with a letter (a-z).
- You can use letters with diacritical marks and non-Latin
letters, and you can use an underscore (
_
). - Subsequent characters can be letters, underscores, or digits (
0
-9
). - Named view parameters can't contain
$
. - Named view parameters are case sensitive. For example,
$@PARAMETER_NAME
is interpreted differently than$@parameter_name
.
- Create the view using the
Grant
SELECT
on the view to any database user that is allowed to query the view.Grant
USAGE
on the schema that contains the tables defined in the view to any database user that is allowed to query the view.
For more information, see Secure and control access to application data using parameterized secure views.
Configure security for your application
To configure security for your applications using parameterized secure views, follow these steps:
- Create the secure parameterized views as an administrative user. This user is an AlloyDB database user that performs administrative operations for the application, including database setup and security administration.
Create a new database role for executing queries against parameterized secure views. This is an AlloyDB database role that the application uses to connect and sign into the database, and to execute queries against parameterized views.
- Grant the new role permissions to the secure views, which
typically includes
SELECT
privileges to the views andUSAGE
on the schemas. - Limit the objects that this role can access to the minimum required set of public functions and objects that the application needs. Avoid providing access to schemas and tables that aren't public.
When you query the views, the application provides the values of the required view parameters, which are tied to the application user identity.
For more information, see Create a database user.
- Grant the new role permissions to the secure views, which
typically includes
Query a parameterized secure view
To query a parameterized secure view, use one of the following options that best supports your use case:
- JSON-based: Use this API to run the query in one-shot and return JSON rows.
- CURSOR-based: Use this API when you have longer running queries or when
you have large queries and you want to fetch the result in batches. The
execute_parameterized_query
function provided by theparameterized_views
extension accepts a cursor name. PREPARE EXECUTE
statement: Use this for prepared statements that can be executed multiple times with different parameters values.
To query parameterized secure views, you use the execute_parameterized_query()
function provided by the parameterized_views
extension.
JSON API
This API has limitations because it declares a cursor for the given query. As
a result, the query must be compatible with PostgreSQL cursors.
For example, the CURSOR API doesn't support DO
or SHOW
statements.
This API also doesn't restrict the results by size or by the number of rows returned.
Run the execute_parameterized_query()
function, which has the following
syntax:
SELECT * FROM
parameterized_views.execute_parameterized_query(
query => SQL_QUERY,
param_names => ARRAY [PARAMETER_NAMES],
param_values => ARRAY [PARAMETER_VALUES]
)
Replace the following:
SQL_QUERY
: a SQL query whoseFROM
clause refers to one or more parameterized secure views.PARAMETER_NAMES
: a list of parameter names to pass in as strings.PARAMETER_VALUES
: a list of parameter values to pass in.- This list must be the same size as the
param_names
list, where the order of the values matches the order of the names. - The exact type of the values is inferred from the query and parameterized view definition. Type conversions are performed when needed and when possible for the given parameter value. In case of a type-mismatch, an error is thrown.
- This list must be the same size as the
The function returns a table of JSON objects. Each row in the table is
equivalent to the ROW_TO_JSON()
value of the original query result row.
Use the following example to query a parameterized secure view:
SELECT * FROM
parameterized_views.execute_parameterized_query(
query => 'SELECT * FROM secure_checked_items',
param_names => ARRAY ['app_end_userid'],
param_values => ARRAY ['40']
)
Using this API limits the size of the result set by size expressed in kilobytes
(kB) of the results and by the number of rows. You can configure these
limits by using parameterized_views.json_results_max_size
and
parameterized_views.json_results_max_rows
.
CURSOR API
Run the execute_parameterized_query()
function, which creates and returns a
transaction-scoped CURSOR that you use to retrieve query results:
SELECT * FROM
parameterized_views.execute_parameterized_query(
query => SQL_QUERY,
cursor_name => CURSOR_NAME,
param_names => ARRAY [PARAMETER_NAMES],
param_values => ARRAY [PARAMETER_VALUES]
)
Replace the following:
SQL_QUERY
: a SQL query whoseFROM
clause refers to one or more parameterized secure views.CURSOR_NAME
: name of the cursor to be declared.PARAMETER_NAMES
: a list of parameter names to pass in as strings.PARAMETER_VALUES
: a list of parameter values to pass in. This list must be the same size as theparam_names
list, where the order of the values matches the order of the names. The exact type of the values is inferred from the query and parameterized view definition. Type conversions are performed when needed and when possible for the given parameter value. In case of a type mismatch, an error is thrown.
Use the following example to query a parameterized secure view:
-- start a transaction as the that is the default lifetime of a CURSOR
BEGIN;
-- create a cursor called 'mycursor'
SELECT * FROM parameterized_views.execute_parameterized_query(
query => 'SELECT * FROM secure_checked_items',
cursor_name => 'mycursor'
param_names => ARRAY ['app_end_userid'],
param_values => ARRAY ['40']
);
-- then, to actually fetch the results
FETCH ALL FROM mycursor;
-- end the transaction, which will clean up the cursor
END;
The returned cursor is a NO SCROLL
cursor WITHOUT HOLD
. You can't use the
cursor to retrieve rows in a non-sequential fashion, for example, in a backward
direction. You can't use the cursor outside the transaction that created it.
PREPARE statement
Use the PREPARE .. AS RESTRICTED
command to create a prepared statement
that references parameterized views. These prepared statements support
positional parameters and enforce various restrictions when you execute them.
For more information, see Security mechanism.
This feature extends
the PREPARE
and EXECUTE commands
to support named view parameters. Use prepared
statements to avoid the overhead of parsing, analyzing, and rewriting each
time the statement is executed, which can result in significant performance
gains, especially for frequently executed or complex queries. A prepared
statement is a server-side object that can optimize performance by
pre-compiling and storing a parameterized SQL statement for later execution.
This API has limitations because the statement must be allowed in a PREPARE
statement, which means that only SELECT
and VALUES
statements are supported.
This API also doesn't restrict the results by size or number of rows returned.
To create a prepared statement that references parameterized views, run the
PREPARE .. AS RESTRICTED
command:
PREPARE pquery (/POSITIONAL_PARAM_TYPES/)
AS RESTRICTED query % a query that may refer to parameterized views
EXECUTE pquery (/POSITIONAL_PARAM_VALUES/)
WITH VIEW PARAMETERS (VIEW_PARAM_NAME1 = VIEW_PARAM_VALUE1[, ...]);
Replace the following:
POSITIONAL_PARAM_TYPES
: one or more positional parameters that are used in theRESTRICTED
query.POSITIONAL_PARAM_VALUES
: the actual values that are substituted for the positional parameters defined in thePREPARE
statement.VIEW_PARAM_NAME
: the name of the parameter expected by the parameterized views referenced in theRESTRICTED
query.VIEW_PARAM_VALUE
: the actual values being passed to the correspondingviewParamName
parameters of the parameterized views.
To include parameters in a prepared statement, you supply a list of data types
in the PREPARE
statement. In the statement that you prepare, you refer to
the parameters by position using, for example, $1
and $2
.
Use the EXECUTE .. WITH VIEW PARAMETERS
command to execute a previously
prepared statement that you created using the PREPARE .. AS RESTRICTED
command.
If the PREPARE
statement that created the statement specified positional
parameters, then you must pass a compatible set of parameters to the EXECUTE
statement. You must pass any named view parameters required by parameterized
views in the WITH VIEW PARAMETERS
clause.
Use the following example to query a parameterized secure view:
PREPARE pquery (timestamp) AS RESTRICTED SELECT * FROM secure_checked_items WHERE timestamp > $1;
EXECUTE pquery (current_date - 1) WITH VIEW PARAMETERS (app_end_userid = 40);
EXECUTE pquery (current_date - 30) WITH VIEW PARAMETERS (app_end_userid = 40);
Enforced restrictions on queries
The following lists the set of restricted operations for queries that you run using the options described in Query a parameterized secure view:
- Any recursive invocation of any APIs—
execute_parameterized_query
, or by usingEXECUTE .. WITH VIEW PARAMETERS
— is prohibited, so that only the values specified by the application are used. This restriction also prevents the query from being used to circumvent the security envelope of the given set of parameter values. - Some extensions that start a new background session are disallowed,
including the
dblink
,pg_cron
andpg_background
extensions. - The following lists the set of query constructs permitted that are
restricted:
- Read-only
SELECT
statements are allowed. - Read-only
SHOW
statements,CALL
statements, andDO
statements are allowed. - DML statements such as
INSERT
,UPDATE
, andDELETE
aren't allowed. - DDL statements such as
CREATE TABLE
andALTER TABLE
aren't allowed. - Other statement types such as
LOAD
,SET
,CLUSTER
,LOCK
,CHECKPOINT
, andEXPLAIN
aren't allowed.
- Read-only
EXPLAIN
statements are disallowed to avoid the possibility of covert channel attacks using query plans. For more information, see Covert channel.
List all parameterized views
Use the parameterized_views
extension to list all the parameterized
views in the database by using the all_parameterized_views
view. The output
of this view is the same as the pg_views
catalog view, but all_parameterized_views
only lists views with named view
parameters.
To list parameterized views, use the following example:
postgres=# select * from parameterized_views.all_parameterized_views ;
schemaname | viewname | viewowner | definition
-----------+--------------------+-----------+---------------------------------------------------------
public | checked_items_view | postgres | SELECT checked_items.bag_id, +
| | | checked_items."timestamp", +
| | | checked_items.location +
| | | FROM checked_items +
| | | WHERE (checked_items.customer_id = $@app_end_userid);
To list a parameterized view in all_parameterized_views
, make sure that the
parameterized view contains at least one named view parameter in its definition.
What's next
- Learn about parameterized secure views.
- Learn how to secure and control access to application data using parameterized secure views.