Your SIEM is Blind to Data Exfiltration
Security tools watch for GRANTs and failed logins while insiders exfiltrate your entire user table with a single SELECT *. Logs are blind to query cost and row counts—the only metrics that matter for catching insider threats.
Venkat Sakamuri
DeepSQL R&D · Ex Oracle Query Engine Team · YC & CMU

TL;DR
- Your SIEM and audit logs see
GRANTandLOGIN_FAILED. They are blind to the most common data exfiltration vector: anomalousSELECTqueries from a seemingly trusted source. - A compromised credential running
SELECT *is indistinguishable in raw logs from a normalSELECT *from an admin tool. Context—like rows returned, query cost, and historical access patterns per role—is what separates signal from noise. - Data exfiltration attempts often manifest as performance problems first (e.g., massive sequential scans, buffer cache eviction). Security tools that ignore query execution metrics are functionally useless for this threat class.
We had a breach a few years back. Not at DeepSQL, but at a past life. It wasn't the result of a zero-day or a sophisticated SQL injection. It was simpler and far more common.
An engineer left the company. Standard offboarding procedure. But a service account key they had generated for a prototype was still active, embedded in a forgotten cron job on a disused EC2 instance. Someone found it. For three weeks, they used that key to run a simple script, pulling down the customers table, 10,000 rows at a time, once every few hours. It was slow, deliberate, and completely under the radar.
Our multi-million dollar security platform saw nothing. Why? Because the queries looked 'normal.' It was a trusted service account role, customer_api_readonly, executing a SELECT against customers. No DDL, no privilege escalation, no failed logins. To the log parser in our SIEM, every query was a legitimate event.
The only sign that something was wrong was buried in the database itself. A service account that normally executed thousands of Index Scan queries per hour, each returning 1 row, suddenly started executing a few Sequential Scan queries that returned 10,000.
Nobody was looking at that level. And that’s the problem.
The SIEM Fallacy: Parsing Logs isn't Understanding Behavior
Database security monitoring, for most companies, means shipping logs to a SIEM. You ship the Postgres or MySQL audit logs, and a security engineer writes some rules. These rules are almost always based on simple string matching and regex.
They are good at catching obvious, high-ceremony events:
ALERT ON 'GRANT DBA'ALERT ON 'DROP DATABASE'ALERT ON 'LOGIN_FAILED' > 10 times in 1 minute from same IP
This is child's play. Important, but it only covers the front door. The real damage happens inside. The vast majority of database activity—over 99% in most OLTP systems—is SELECT statements. And existing tools have no meaningful way to differentiate a good SELECT from a catastrophic one.
Consider this rule: alert on SELECT * FROM users. It's useless. Every junior developer writing a quick debugging script will trigger it. Your admin dashboards will trigger it. The alert fatigue would be overwhelming, and the rule would be disabled within hours.
Now consider a better rule: alert on SELECT FROM users WHERE rows_returned > 1,000,000. This is getting closer to something useful. But a SIEM can't implement this rule. The rows_returned metric doesn't exist in the text of the log file. It’s an execution-time metric, a result of the query plan. Your SIEM has no access to it.
The Anatomy of an Anomalous SELECT
Let's make this concrete. Your application has a microservice, user_profile_service, which has a role with SELECT permissions on the users table. 99.9% of the time, it runs this query:
-- Normal query from the 'user_profile_service' role
SELECT id, email, username, last_active
FROM users WHERE id = 'f47ac10b-58cc-4372-a567-0e02b2c3d479';
Postgres will service this with an Index Scan on the users_pkey primary key index. It will read one page from the buffer cache. The query will return in under a millisecond. This is normal, healthy behavior.
Now, an attacker compromises the credentials for this service. They run a different query:
-- Malicious query from the SAME 'user_profile_service' role
SELECT * FROM users;
To the audit logger, this is just another SELECT from a trusted role. But look at what happens inside the database. Here's what EXPLAIN ANALYZE shows in Postgres for this query on a table with a few million rows:
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Seq Scan on users (cost=0.00..65432.10 rows=2500000 width=412) (actual time=0.041..895.331 rows=2500000 loops=1)
Planning Time: 0.120 ms
Execution Time: 1051.455 ms
The two critical numbers are rows=2500000 and Execution Time: 1051.455 ms. The database just spent over a second scanning and returning 2.5 million rows. This query read the entire table, likely several gigabytes of data. This is an exfiltration event.
Tools like pg_stat_statements offer a glimpse of this, but they are designed for performance tuning, not security. They aggregate statistics per normalized query hash. They can tell you that the SELECT * FROM users query is expensive, but they can't tell you that this specific execution, by this specific role, was a wild outlier compared to its historical behavior.
The Cost of Blindness is Performance, then Data
The first sign of a major data exfiltration event is often not a security alert; it's a performance degradation ticket. That massive Seq Scan doesn't just read data for the attacker. It has side effects.
In Postgres, it pollutes shared_buffers. In MySQL, the innodb_buffer_pool. It evicts the hot, frequently accessed index pages that your normal application traffic relies on. Suddenly, those sub-millisecond Index Scan queries for individual user lookups are now taking 10-20ms because they have to go to storage. Your application-level latency metrics spike. The on-call engineer gets paged at 3 AM for a performance problem.
They start debugging. They check for long-running queries, look at pg_stat_activity, inspect autovacuum settings, maybe increase work_mem. They are treating the symptoms. The root cause—an anomalous, malicious SELECT—is invisible to them because they're thinking like performance engineers, not security analysts. And the security analysts can't help because their tools are blind to execution plans and performance metrics.
This gap between performance and security is where data is lost.
What DeepSQL does about this
This isn't an unsolvable problem. You just can't solve it by parsing logs. DeepSQL instruments the database's query execution layer directly. We build a dynamic baseline of normal behavior for every unique combination of user, role, and query fingerprint. This baseline isn't just the query text; it's a multi-dimensional profile including metrics like rows read, rows returned, CPU time, and data processed.
When a query executes that deviates significantly—like our user_profile_service role suddenly executing a query that returns 2.5 million rows instead of its usual single row—it’s flagged as an anomalous access event in real time. We don't need a static rule. We detect the statistical anomaly against a learned baseline. This allows us to spot a credential compromise or insider threat on the first malicious query, not after the data has already left the building.