Chapter 33: Database Security — Auth, Encryption, Injection

SQL Injection — The #1 Database Attack

// VULNERABLE (string concatenation):
query = "SELECT * FROM users WHERE name = '" + user_input + "'";
// If user_input = "'; DROP TABLE users; --"
// → SELECT * FROM users WHERE name = ''; DROP TABLE users; --'
// YOUR TABLE IS GONE.

// SAFE (parameterized query):
query = "SELECT * FROM users WHERE name = $1";
params = [user_input];  // treated as DATA, never as SQL
⚠️ Rule #1 of Database Security

NEVER concatenate user input into SQL strings. ALWAYS use parameterized queries (prepared statements). This applies to every language, every framework, every database. No exceptions.

Authentication & Authorization

-- Create roles with minimal privileges
CREATE ROLE app_readonly LOGIN PASSWORD '...';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;

CREATE ROLE app_readwrite LOGIN PASSWORD '...';
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_readwrite;

-- Never let applications connect as superuser!

Encryption

Row-Level Security (RLS)

-- Each user can only see their own data
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
CREATE POLICY user_docs ON documents
  USING (owner_id = current_user_id());
-- Now SELECT * FROM documents only returns rows owned by current user

Security Checklist

Key Takeaways