Best practices for row-level security in BigQuery

This document explains best practices when usingrow-level security.

Before you read this document, familiarize yourself withrow-level security by readingIntroduction to BigQuery row-level securityandWorking with row-level security.

Restrict user permissions to limit side-channel attacks

Best practice: Don't grant sensitive permissions to users who should only see filtered data.

A side-channel attack is a security attack based on information gained from thesystem itself. An attacker with broader permissions than necessary canmount side-channel attacks, and learn sensitive data byobserving orsearching billing, logging, or system messages.

To mitigate such opportunities, BigQuery hidessensitive statistics on all queries against tables with row-level security.These sensitive statistics include the number of bytes and partitions processed,the number of bytes billed, and the query plan stages.

We recommend that admins should refrain from granting the following permissionsto users who should only see filtered data, to avoid giving access to sensitivedata.

PermissionsSensitive data
Project OwnerProject owners can view bytes processed and related data only in audit logs.The billing metadata cannot be viewed from the job details. There's no specificpermission or role to grant viewer access to this billing metadata.
BigQuery Data Edit, Owner, or Viewer rolesView error messages on queries.
Cloud Billing viewer permissionsView BigQuery billing.

Examples

  • Through repeatedobservation of query duration when querying tables withrow-level access policies, a user could infer the values of rowsthat otherwise might be protected by row-level access policies. This type ofattack requires many repeated attempts over a range of key valuesin partitioning or clustering columns. Even though there is inherentnoise when observing or measuring query duration, with repeated attempts, anattacker could obtain a reliable estimate.If you are sensitive to this levelof protection, we recommend using separate tables to isolate rows withdifferent access control requirements, instead.
  • An attacker couldsearch for the bytes processed by aquery by monitoring the errors that occur when the query job limits (such as maximumbytes billed or custom cost controls) are exceeded. However, this attack requires a high volume of queries.
  • Through repeated queries andobserving the BigQuery billingamount in Cloud Billing, a user could infer the values of rows that otherwisemight be protected by row-level access policies. This type of attack requiresmany repeated attempts over a range of key values in partitioningor clustering columns.If you are sensitive to this level of protection, we recommend that you limit access to billing data forqueries.

We also recommend that admins monitorCloud Audit Logs(/bigquery/docs/reference/auditlogs)for suspicious activity on tables with row-level security, such as unexpectedadditions, modifications, and deletions of row-level access policies.

Restrict user permissions to limit data tampering

Best practice: Don't grant table write permissions to users who should only see filtered data.

Users with write permissions to a table can insert data into the table with thebq load command or withthe BigQuery Storage Write API. This can allow the user withwrite permissions to alter the query results of other users.

We recommend that admins create separate Google groups for table write accessand row-levels access policies. Users that should only see filtered tableresults shouldn't have write access to the filtered table.

Avoid inadvertent access when re-creating row-level access policies

Best practice: If there is only one row-level access policy on a table,don't recreate that row-level access policy with theCREATE OR REPLACE command. Instead, first remove all access to thetable with table access controls, recreate the policies as needed, and thenre-enable access.

When you add a row access policy on a table for the first time, you immediatelybegin filtering data in query results. When you remove the last row-level accesspolicy on a table, even if you intend to only re-create the row-level accesspolicy, you may inadvertently grant unfiltered access to a wider-than-intendedaudience.

We recommend that admins pay special attention when recreating the lastrow-level access policy on a table, by following these guidelines:

  1. First remove all access to the table, by usingtable access controls.
  2. Remove all row-level access policies.
  3. Re-create the row-level access policies.
  4. Re-enable access to the table.

Alternatively, you can first create new row-level access policies on the table,then delete the earlier row-level access policies that are no longer needed.

Use row-level security only within organizations, not across organizations

Best practice: Only use row-level security within your organization.

Don't use the row-level security feature across organizations, to help preventdata leakage through side-channel attacks, and to maintain greater control overaccess to sensitive data.

For subquery row-level access policies, create and search tables withinorganizations or projects. This leads to better security and simpler ACLconfiguration, as grantees must have thebigquery.tables.getData permission onthe target and referenced tables in policies, as well as any relevantcolumn-level security permissions.

We recommend using row-level security feature for within-organizationsecurity constraints only (such as for sharing data within anorganization/enterprise/company), and not for cross-organizational or publicsecurity.

Example

Outside of your organization, you have less control over who has access to data.Within your organization, you can control who has been granted access to billinginformation of queries against tables with row-level access policies. Billinginformation is a vector forside-channel attacks.

Manage theFiltered Data Viewer role through row-level access policies

Best practice:bigquery.filteredDataViewer is a system-managed role granted through row-level access policies. Manage the role only through row-level access policies. Don't apply the role through Identity and Access Management (IAM).

When youcreate a row-level access policy,the principals in the policy are automatically granted thebigquery.filteredDataViewer role. You can only add or remove principals fromthe access policywith a DDL statement.

Thebigquery.filteredDataViewer rolemust not be granted throughIAM to a higher-level resource, suchas a table, dataset, or project. Granting the role in this way lets usersview rows defined byall row-level access policies within that scope,regardless of intended restrictions. While the union of row-level access policyfilters might not encompass the entire table, this practice poses a significantsecurity risk and undermines the purpose of row-level security.

We recommend managing thebigquery.filteredDataViewer role exclusively throughrow-level access policies. This method ensures that principals are granted thebigquery.filteredDataViewer role implicitly and correctly, respecting thedefined filter predicates for each policy.

Performance impact of filters on partitioned columns

Best practice: Try to avoid making row access policies that filter on clustered and partitioned columns.

Row-level access policy filters don't participate in querypruning on partitioned and clustered tables.

If your row-level access policy names a partitioned column, your query does notreceive the performance benefits of query pruning.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-12-15 UTC.