Why Read-Only Database Permissions Aren't Enough
Most teams protect production databases by granting users read-only permissions at the database level. While this is a good starting point, it has significant gaps that can lead to data incidents.
The Problem with Database-Level Permissions
Database permissions control what a user can do, but they have blind spots:
- Functions with side effects: In Oracle, a function called within a SELECT can contain
PRAGMA AUTONOMOUS_TRANSACTION— executing DML (DELETE, INSERT) inside a seemingly read-only query. - Stored procedures: If a user has EXECUTE privileges, they can run procedures that modify data, even with a "read-only" role.
- No audit at the application layer: Database audit logs often lack context — who was the actual end-user? What client were they using? Why did they run that query?
- Connection string exposure: Every team member with a SQL client has the full connection string, including passwords. One person leaves, credentials must be rotated.
Application-Layer Enforcement
ReadSentinel takes a different approach: every query is parsed using ANTLR-based SQL grammars before it reaches the database. This means:
- INSERT, UPDATE, DELETE are blocked regardless of database permissions
- User-defined function calls are blocked (only whitelisted built-in functions allowed)
- Stored procedure EXEC/CALL statements are rejected
- PL/SQL anonymous blocks are rejected
- MongoDB write operators ($set, $unset, $inc) are detected and blocked in filter JSON
This creates a defense-in-depth approach: even if database permissions are misconfigured, the application layer prevents damage.
The Credential Problem
With traditional SQL clients (DBeaver, DataGrip, pgAdmin), every user needs the connection string including the password. When someone leaves the team, you need to rotate credentials on every database they had access to.
With ReadSentinel, connection strings are encrypted with AES-256-GCM and never exposed to end users. Team members select a connection from a list and query — without ever seeing the host, port, or password. Offboarding is instant: deactivate the user account.
How to Prevent Accidental DELETE in Production
Accidental DELETE FROM table without a WHERE clause is one of the most common production incidents. Here's how teams typically handle it — and why most approaches are insufficient.
Common Approaches
- Read-only database user: Works until someone needs to run a stored procedure or the DBA grants extra privileges for "just this one task"
- "Be careful" culture: Scales terribly. One tired engineer at 2am will miss the WHERE clause
- Approval workflows: Slow, creates bottlenecks, people start sharing credentials to bypass
- VPN-only access: Doesn't prevent writes, just limits who can try
The ReadSentinel Approach
Block writes at the application layer, period. The query DELETE FROM customers never reaches the database because ReadSentinel's ANTLR parser identifies it as a DELETE statement and rejects it before execution. The user gets a clear error: "Only SELECT queries are allowed."
This works for:
- Support teams looking up customer data
- Developers debugging issues in UAT
- Auditors running compliance reports
- Business analysts pulling metrics
None of these users need write access. With ReadSentinel, they physically cannot write — regardless of the underlying database permissions.
Database Access Audit Trail: What SOX and GDPR Require
If your organization handles financial data (SOX) or personal data (GDPR), you need to answer: who accessed what data, when, and why?
What Auditors Look For
- User identification: Which individual ran the query? (Not a shared account)
- Timestamp: When exactly was the data accessed?
- Query content: What tables/columns were queried?
- Data volume: How many rows were returned?
- Access justification: Is the user's role appropriate for this data?
How ReadSentinel Helps
ReadSentinel's audit log captures every query execution with:
- Authenticated user email (no shared accounts — each person has their own credentials)
- Millisecond-precision timestamp
- Full SQL text
- Connection used (which database)
- Row count returned
- Execution duration
- Status: success, rejected (write attempted), timeout, error
The audit log is stored in a separate append-only database and accessible via the admin panel with filters. Export for auditor review on demand.
Combined with role-based access control, you have a complete picture: "User X, with role Y, ran query Z against database W at time T and got N rows back."