CertSafari
    Snowflake SnowPro Advanced: Security Engineer (SEA-C01)· Lessons

    Domain 3 · Lesson 12/21

    Audit data access with QUERY_HISTORY, ACCESS_HISTORY and LOGIN_HISTORY

    Monitor data security.

    13 min read
    6% of exam
    5 sources
    Published 5 Oct 2026
    Docs as of 4 Oct 2026

    What you will be able to do

    • Choose the right ACCOUNT_USAGE view, and allow for its latency and retention, when investigating data access
    • Correlate a login, the queries it ran and the objects those queries touched using shared keys
    • Read direct versus base objects, policy references and DDL records to spot unauthorized access and changes to secure objects
    • Detect authentication anomalies and identify Snowpark Container Services and agent activity
    • Produce compliance evidence and give auditors read access without ACCOUNTADMIN
    • Investigate unauthorized access attempts manually from LOGIN_HISTORY, and tell which framework mappings (GDPR, HIPAA, etc.) the documentation supports

    Key concept

    query_id as the audit join key — Every statement gets one system-generated query_id. That same identifier appears in QUERY_HISTORY (who ran what, when, and from which session) and in ACCESS_HISTORY (which objects and columns were read or written), so it is the link that turns separate logs into one evidence trail.

    1.Three audit views, three latencies

    A data-security investigation in Snowflake usually starts in three SNOWFLAKE.ACCOUNT_USAGE views. QUERY_HISTORY records each statement by time range, session, user and warehouse. ACCESS_HISTORY records which tables, views, columns, functions and stages each statement read or wrote. LOGIN_HISTORY records every login attempt, whether it succeeded or failed. All three keep 365 days of history.

    Retention is the first trap. QUERY_HISTORY also exists as an Information Schema table function, but that function only covers the last week. If an investigation goes back further than that, you need the Account Usage view.

    Each view has its own documented latency. QUERY_HISTORY can lag by up to 45 minutes, LOGIN_HISTORY by up to 120 minutes, and ACCESS_HISTORY by up to 180 minutes. ACCESS_HISTORY holds data from February 22, 2021 onward.

    Latency matters most when you build a feed out of these views. Say a collector loads LOGIN_HISTORY every 15 minutes and starts each run from the largest EVENT_TIMESTAMP it has already loaded. A login that happened earlier but only reaches the view later will have a timestamp below that watermark, so the collector never picks it up. Any incremental pull has to reach back at least as far as the view's latency. For ACCESS_HISTORY, Snowflake also recommends filtering on query_start_time and using narrow time ranges to keep queries fast.

    Checkpoint 1 of 7· Check yourself

    An investigator needs every query a contractor ran four months ago. Which source can still return them?

    You do not need a scanner to investigate unauthorized access attempts. You can do it manually by querying these views yourself. LOGIN_HISTORY is the place to start: it records login attempts by Snowflake users for 365 days, including failed ones. Filter on IS_SUCCESS to find the failures, read ERROR_CODE and ERROR_MESSAGE to see why each attempt failed, and group by USER_NAME and CLIENT_IP to decide whether the attempts look unauthorized. If an attempt did succeed, follow it into QUERY_HISTORY and ACCESS_HISTORY to see what that session touched.

    The same views are also the evidence you map to security frameworks such as GDPR, HIPAA, etc. Be careful about what the documentation actually supports. For ACCESS_HISTORY it names GDPR and CCPA directly. It gives no mapping for HIPAA or any other framework, so any such mapping is your own work and not a documented Snowflake feature.

    Sources1234

    2.Correlating logins, queries and data touched

    Correlation works through shared keys. Each QUERY_HISTORY row has authn_event_id, which is the same value as event_id in LOGIN_HISTORY. That lets you go from a suspicious login to every statement that authentication ran. QUERY_HISTORY also has session_id, user_name and role_name, which help when you want to group activity by session. The query_id then takes you from QUERY_HISTORY into ACCESS_HISTORY, where you can see the objects and columns each statement touched.

    Stored procedures and multi-statement requests add a step. ACCESS_HISTORY has two lineage columns. parent_query_id is the query ID of the parent job. root_query_id is the topmost job in the chain. They cover reads and writes made by a procedure, including nested procedure calls. So when a CALL issues several SELECT statements and each gets its own query ID, root_query_id ties them all back to the CALL the user ran.

    The same applies to client batching. The Python connector and the SQL API combine multiple SQL statements into a single request and return one query_id. That ID is actually the parent of the individual statements. These two columns start recording data on January 15–16, 2024, so earlier records will not have them.

    Checkpoint 2 of 7· Fill the gap

    A Python connector request returned query_id 6789. Fill in the column that returns each statement inside that request.

    SELECT query_id, parent_query_id, direct_objects_accessed FROM snowflake.account_usage.access_history WHERE  ?  = 6789;

    Checkpoint 3 of 7· Exam question

    An auditor asks which users read the SSN column of the base table PROD.HR.EMPLOYEES during the last month. Analysts only ever query the secure view PROD.HR_SHARE.V_EMPLOYEES that is built on that table. Which ACCOUNT_USAGE approach answers the question?

    Sources134

    3.Direct objects, base objects and secure-object changes

    ACCESS_HISTORY keeps two views of every read. direct_objects_accessed lists what the query named: tables, views, columns, UDFs and stored procedures, including columns picked up through a *. base_objects_accessed lists the underlying objects that actually supplied the data. Take a chain base_table » view_1 » view_2 » view_3 and a query on view_2. The record shows view_2 as the direct object and base_table as the base object. view_1 and view_3 do not appear.

    This is what makes secure views auditable. When someone queries a secure view, the record still contains the underlying base table. A user who can only see the view still leaves evidence of which base table and columns they reached. Filter columns count as well: a column used only in a WHERE clause is recorded in base_objects_accessed.

    Three more columns help with security investigations. objects_modified records write targets, with column lineage in baseSources and directSources. policies_referenced records which masking and row access policies were enforced, including policies on intermediate objects. object_modified_by_ddl records DDL on databases, schemas, tables, views and columns. That includes setting a row access or masking policy, changing tags, and GRANT or REVOKE to a share. This is the column to watch when you need to know who changed the protection on a secure object. A policy reference looks like this:

    policies_referenced example: a masking policy on column SSN and a row access policy on view V1json
    [ { "columns": [ { "columnId": 68610, "columnName": "SSN", "policies": [ { "policyName": "governance.policies.ssn_mask", "policyId": 68811, "policyKind": "MASKING_POLICY" } ] } ], "objectDomain": "VIEW", "objectId": 66564, "objectName": "GOVERNANCE.VIEWS.V1", "policies": [ { "policyName": "governance.policies.rap1", "policyId": 68813, "policyKind": "ROW_ACCESS_POLICY" } ] } ]

    There are coverage gaps to know about. Not every QUERY_HISTORY row has a matching ACCESS_HISTORY row; whether one is recorded depends on how the SQL statement is structured. A USING clause in a join can record columns the query never referenced, and Snowflake's workaround is JOIN ... ON. ACCESS_HISTORY also records operations that external engines run through the Horizon Iceberg REST Catalog. You can isolate those with event_source = 'horizon_irc'.

    Checkpoint 4 of 7· Match them up

    Match each ACCESS_HISTORY column to what it records

    Tap a term, then the definition that fits it.

    Sources43

    4.Authentication anomalies, services and agents

    For brute-force and unauthorized-access detection, LOGIN_HISTORY is the source to query directly. A brute-force attempt typically shows up as many rows with IS_SUCCESS false for the same USER_NAME or CLIENT_IP in a short window. Rows with a NULL USER_NAME are their own signal: authentication failed before Snowflake could identify the user, for example because OAuth client credentials were invalid or a key-pair attempt named an unknown user.

    LOGIN_HISTORY columns and what they reveal in an investigation
    ColumnInvestigative use
    IS_SUCCESS, ERROR_CODE, ERROR_MESSAGEWhether the attempt failed and why
    USER_NAMENULL when authentication failed before the user could be identified
    CLIENT_IPWhere the request came from; INTERNAL_SNOWFLAKE_IP/0.0.0.0 for internal operations such as Snowsight worksheets
    FIRST_AUTHENTICATION_FACTOR, SECOND_AUTHENTICATION_FACTORHow the user authenticated; the second factor is NULL when MFA was not used
    REPORTED_CLIENT_TYPEClient software as reported by the client; this value is not authenticated
    LOGIN_DETAILSMalicious IP protection category, risk category and blocking status

    AI/ML workloads on Snowpark Container Services leave their own traces. When a service runs a query, QUERY_HISTORY sets user_type to SNOWFLAKE_SERVICE and fills user_database_name and user_schema_name with the service's database and schema. That tells you which service read the data. You can then join on query_id to ACCESS_HISTORY to see which tables and columns it read. Do not try to trace a service by IP address: when a service logs in, LOGIN_HISTORY masks its client IP to INTERNAL_SNOWFLAKE_IP/0.0.0.0.

    Agents are tracked the same way. QUERY_HISTORY.agent_type shows whether a Cortex Agent, a lite agent or an external agent ran the query. ACCESS_HISTORY.agents_info lists the chain of agents from the nearest one to the top-level one. The older invoker_identity column is scheduled for removal in the 2026_07 bundle.

    Checkpoint 5 of 7· Check yourself

    You need to find which tables a Snowpark Container Services service read last week. Which approach works?

    Sources23

    5.Compliance evidence and auditor access

    Snowflake positions ACCESS_HISTORY as compliance evidence because it links the user, the query, the object, the column and the data in one record. The documentation names GDPR and CCPA directly: you can identify who performed a write operation on a table or stage, and when. Column lineage goes further. It lets privacy officers count how many objects a sensitive column has been copied into, which they can use to show compliance with standards such as GDPR. policies_referenced shows that masking or row access policies were actually enforced on each read.

    The sources here do not map this evidence to HIPAA or other frameworks, and they do not describe a ready-made query for columns tagged as PHI. What they do support is a general pattern: filter ACCESS_HISTORY on the columns in scope for the regulation, over the audit period, and pull in policies_referenced to show those columns were protected.

    Auditors need access to this evidence without receiving ACCOUNTADMIN. The SNOWFLAKE database is imported into every account, and access to its Account Usage views is controlled by four SNOWFLAKE database roles: OBJECT_VIEWER, USAGE_VIEWER, GOVERNANCE_VIEWER and SECURITY_VIEWER. Each role grants SELECT on a specific set of views. GOVERNANCE_VIEWER covers data-governance information and SECURITY_VIEWER covers security information. You grant the relevant role to the auditor's role, which gives them read access to just those views. Check Snowflake's role-to-view table for the exact views each role includes.

    Checkpoint 6 of 7· Exam question

    A SOC needs to alert within minutes on repeated failed logins, but SNOWFLAKE.ACCOUNT_USAGE.LOGIN_HISTORY can lag by up to two hours. Which data source gives the lowest-latency view of recent login attempts?

    Checkpoint 7 of 7· Check yourself

    An external auditor must query security-related Account Usage views but must not hold ACCOUNTADMIN. What fits?

    Sources45

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.Every statement in QUERY_HISTORY has a matching row in ACCESS_HISTORY, so ACCESS_HISTORY alone is a complete log.Why is that wrong?

      Whether ACCESS_HISTORY records a statement depends on how the statement is structured. Some QUERY_HISTORY rows have no ACCESS_HISTORY counterpart.

      Covered in Direct objects, base objects and secure-object changes

    2. 2.Querying through a secure view hides the underlying table from the audit log.Why is that wrong?

      For secure views, ACCESS_HISTORY records the underlying base table in base_objects_accessed.

      Covered in Direct objects, base objects and secure-object changes

    3. 3.The Information Schema QUERY_HISTORY function and the Account Usage view return the same history.Why is that wrong?

      The table function covers only the past 7 days. The Account Usage view covers 365 days.

      Covered in Three audit views, three latencies

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Latency for the view may be up to 45 minutes.”
      ↩︎ Three audit views, three latencies
      “This ID corresponds to the value in the event_id column in the LOGIN_HISTORY view.”
      ↩︎ Correlating logins, queries and data touched
      “the table function restricts the results to activity over the past 7 days, versus 365 days for the Account Usage view”
      ↩︎ Exam trap 3
      “the table function restricts the results to activity over the past 7 days, versus 365 days for the Account Usage view”
      ↩︎ Checkpoint
      “If a Snowpark Container Services service executes the query, the user type is SNOWFLAKE_SERVICE.”
      ↩︎ Checkpoint
    2. 2.
      “Latency for the view may be up to 120 minutes (2 hours).”
      ↩︎ Three audit views, three latencies
      “This Account Usage view can be used to query login attempts by Snowflake users within the last 365 days (1 year).”
      ↩︎ Three audit views, three latencies
      “Error message returned to the user, if the request was not successful.”
      ↩︎ Three audit views, three latencies
      “Failed authentication attempts where the user could not be identified appear with a NULL USER_NAME.”
      ↩︎ Authentication anomalies, services and agents
      “When a Snowpark Container Services service logs into Snowflake, the client IP is masked to INTERNAL_SNOWFLAKE_IP/0.0.0.0.”
      ↩︎ Authentication anomalies, services and agents
    3. 3.
      “The view displays data starting from February 22, 2021.”
      ↩︎ Three audit views, three latencies
      “filter queries on the query_start_time column and choose narrower time ranges”
      ↩︎ Three audit views, three latencies
      “The query ID of the topmost job in the chain or NULL if the job does not have a parent.”
      ↩︎ Correlating logins, queries and data touched
      “These operations also include statements that specify a row access policy on a table or view, a masking policy on a column”
      ↩︎ Direct objects, base objects and secure-object changes
      “An ordered JSON array of invoking agents, from the nearest agent to the top-level agent.”
      ↩︎ Authentication anomalies, services and agents
      “This value is also mentioned in the query history.”
      ↩︎ Key concept
      “The log record contains the underlying base table (i.e. base_objects_accessed) to generate the view.”
      ↩︎ Exam trap 2
      “Latency for the view may be up to 180 minutes (3 hours).”
      ↩︎ Prediction
      “A JSON array that specifies the objects that were associated with a write operation in the query.”
      ↩︎ Checkpoint
    4. 4.
      “to meet compliance regulations, such as GDPR and CCPA.”
      ↩︎ Three audit views, three latencies
      “A query that performs a read or write operation on an object that calls a stored procedure, including nested stored procedure calls.”
      ↩︎ Correlating logins, queries and data touched
      “This number is actually a parent query id for all of the individual statements.”
      ↩︎ Correlating logins, queries and data touched
      “base_table in the base_objects_accessed column because that is the original source of the data in view_2.”
      ↩︎ Direct objects, base objects and secure-object changes
      “You can identify IRC-sourced records by filtering on event_source = 'horizon_irc'”
      ↩︎ Direct objects, base objects and secure-object changes
      “to meet compliance regulations, such as GDPR and CCPA.”
      ↩︎ Compliance evidence and auditor access
      “data privacy officers can prove how they satisfy regulatory compliance standards”
      ↩︎ Compliance evidence and auditor access
      “Records in the Account Usage QUERY_HISTORY view do not always get recorded in the ACCESS_HISTORY view.”
      ↩︎ Exam trap 1
    5. 5.
      “ACCOUNT_USAGE schemas have four defined SNOWFLAKE database roles, each granted the SELECT privilege on specific views.”
      ↩︎ Compliance evidence and auditor access
      “The SECURITY_VIEWER role provides visibility into security-based information.”
      ↩︎ Checkpoint

    Continue to page 2 of 2

    Security alerts with Trust Center, Snowflake alerts and external observability tools

    Spotted a mistake, or was something unclear? Tell us.