The document appears to be a transcript from a talk given by Josh Berkus of the PostgreSQL Core Team at OSCON 2008. The talk discusses why database security is important for application developers and common misconceptions about database security.
3. “I don't need to
know”
“Our network security will take
care of it.”
“I applied all the web server and
PHP patches.”
“Security belongs in the
application layer.”
“Database security slows
development.”
“Nobody will hack my website. We
run Linux.”
15. The cost of
unsafe data
Contacting 19 000 customers:
$380 000
Paying for credit reports for 19
000 customers:
$931 000
Shipping stolen merchandise:
$4 600 000
Lost customer goodwill and
reputation as an insecure &
careless company:
Priceless!
32. access control
Goal: Use database access control
lists to prevent connections from
anywhere but specified networks.
database webserver
server
33. pg_hba.conf
TYPE DATABASE USER CIDR-ADDRESS METHOD
local all postgres ident
host all postgres 127.0.0.1/32 ident
local all all md5
host all all 127.0.0.1/32 md5
hostssl webapp +webusers 192.168.2.0/24 md5
host all +admins 10.2.0.0/16 krb5
host all all 0.0.0.0/0 reject
34. mysql users table
User host ssl_type
-- superuser
root 127.0.0.1
-- anonymous user, matches everyone
localhost
127.0.0.1
-- SSL webapp
webapp 129.168.2.* ANY
-- mysql doesn't support kerberos
admins 10.2.*
35. authentication
Goal: prevent privilege escalation
on connections to the database.
psql -U postgres -h
masterserver -c 'update users
set password = 'haxx0r'
where login = 'administrator'
36. authentication
methods
ident: host OS responsible for
security
good for: administrative tasks
bad for: external users
md5: hashed passwords
good for: most things
bad for: embed password in the app.
krb5 / gss / ldap: identity checked
against authentication servers
good for: everything
bad for: lots of troubleshooting
37. pg_hba.conf
TYPE DATABASE USER CIDR-ADDRESS METHOD
local all postgres ident
host all postgres 127.0.0.1/32 ident
local all all md5
host all all 127.0.0.1/32 md5
hostssl webapp +webusers 192.168.2.0/24 md5
host all +admins 10.2.0.0/16 krb5
host all all 0.0.0.0/0 reject
38. ROLEs & privileges
Goal: prevent authenticated low-
level users from modifying or
accessing restricted data.
SELECT FROM users;
UPDATE users;
39. ROLEs
ROLEs ~~ users and groups.
some roles can log in (“users”)
roles can be members of multiple other
roles
use SET ROLE to change ROLE context
users
admins
dataentry readonly
claudio felipe
leo wei-chen guest
40. privileges
All database objects have
privileges, specific to their type:
tables: SELECT, INSERT, UPDATE, DELETE
schema: USAGE, CREATE
function: EXECUTE
database: CONNECT, TEMP, CREATE
Privileges can be used to “lock
down” data for low-level users.
41. using ROLEs &
privileges example
basic web application
admins webusers
claudio felipe member guest
schema admin schema schema cms
members
users pages
rights profiles templates
settings messages
42. using ROLEs &
privileges example
admin: modify anything
admins webusers
claudio felipe member guest
schema admin schema schema cms
members
users pages
rights profiles templates
settings messages comments
43. using ROLEs &
privileges example
webusers: connect, read cms
admins webusers
claudio felipe member guest
schema admin schema schema cms
members
users pages
rights profiles templates
settings messages comments
44. using ROLEs &
privileges example
members: read admin, write members
admins webusers
claudio felipe member guest
schema admin schema schema cms
members
users pages
rights profiles templates
settings messages comments
46. database
abstraction
views
a VIEW is a “stored query” with its
own permissions
limit access to specific rows or
columns
stored procedures
SECURITY DEFINER procedures allow
controlled privilege escalation
make sure to lock them down, though!
47. don't allow access
to base tables
schema admin schema member
members
rights
settings profiles
messages
view
user_names
users functions
login()
change_pw()
48. using abstraction:
password checking
CREATE FUNCTION login (
mailaddr TEXT, pwd TEXT, vip INET
) RETURNS login_type
LANGUAGE plpgsql VOLATILE STRICT SECURITY DEFINER
SET SEARCH_PATH = admin, members;
as $func$
declare rtype login_type;
vuser INT;
vmail TEXT;
vkey INT;
vadmin BOOLEAN;
begin
--this is the login procedure which is the only way to authenticate a new user.
--it checks the users password, generates a passkey, deletes any old sessions
--and creates the new session
select id, (admin_info.user > 0) into vuser, vadmin
from users JOIN user_passwords ON users.id = user_passwords.user
LEFT OUTER JOIN admin_info ON users.id = admin_info.user
where lower(email) = lower(vmail)
and permissions is not null
and syshash_compare(pwd, "password");
IF vuser > 0 THEN ...
49. What do you do if
they get in anyway?
sometimes your other measures fail
exploits
loopholes
misconfiguration
sometimes the bad guys have
legitimate access
users
staff
sysadmins
50. database auditing
Goal: know what happened after it
happened, and be able to restore
your data without searching backup
tapes.
51. auditing: logs
dozens of log options
users
connections
queries run
errors
the log can help you analyze a
break-in
maybe even tell you what was stolen
52. secure your logs
best way to find “DBA corruption”
make sure that not even the admins can
erase/alter all copies
make sure few people can change
postgresql.conf
use a secured log server
“syslog” is good for this
make a plan for secure log
archiving
56. data auditing
member
schema schema
members audit_members
UPDATE
or profiles profiles
DELETE
57. data auditing
member
schema schema
members audit_members
UPDATE
or profiles profiles
DELETE
INSERT
old data
58. data auditing
table members.profiles
member | interests
josh | pottery, cooking
table audit_members.profiles
member | interests | changed | change_by
josh | gaming | 5/23/01 | claudio
josh | pottery | 3/24/08 | felipe
59. data auditing
CREATE FUNCTION audit.trail_companies ()
RETURNS TRIGGER
LANGUAGE plpgsql SECURITY DEFINER SET SEARCH_PATH = audit, main
as $func$
BEGIN
INSERT INTO audit.companies
SELECT *, now(), CURRENT_USER FROM companies
WHERE id = OLD.id;
RETURN OLD;
IF TG_OP = 'DELETE' THEN
RETURN OLD;
ELSIF TG_OP = 'UPDATE' THEN
NEW.mod_date = now();
RETURN NEW;
END;
END; $func$;
CREATE TRIGGER tg_companies
BEFORE UPDATE OR DELETE companies
FOR EACH ROW EXECUTE PROCEDURE audit.trail_companies();
63. contact
Josh Berkus
josh@postgresql.org
blogs.ittoolbox.com/database/soup
www.powerpostgresql.com
PostgreSQL
www.postgresql.org
SEPostgres:
http://code.google.com/p/sepgsql
Thanks to KaiGai Kohei for SEPostgres diagrams, and to Harrison Fisk for MySQL examples.
Copyright 2008 Josh Berkus, distributable under the creative commons attribution license