Skip to main content

Database Security

User Roles and Privileges

User

  • An individual or application connecting to the database.
  • Identified by a username (and usually a password).

Privileges (Permissions)

  • Specific rights to perform actions on database objects.
  • Examples:
    • System Privileges → CREATE TABLE, CREATE USER, SHUTDOWN (affect the whole DB).
    • Object Privileges → SELECT, INSERT, UPDATE, DELETE (affect specific tables, views, procedures).

Roles

  • A role is a collection of privileges.
  • Makes privilege management easier (instead of assigning many privileges to each user).
  • Users are granted roles, and roles contain privileges.

Why Roles and Privileges Matter in Database Design

  • Security → Protect sensitive data from unauthorized access.
  • Integrity → Ensure only authorized users can change data (e.g., only HR can update salaries).
  • Accountability → Track who did what.
  • Least Privilege Principle → Users should only get the minimum permissions required to perform their tasks.

Example of User Role

Let’s imagine a University Database with three types of users:

  • Admin → Full control.
  • Professor → Can view and update grades.
  • Student → Can only view their own courses/grades.

Step 1: Create Users

-- Create three users
CREATE USER admin_user IDENTIFIED BY 'adminpass';
CREATE USER professor_user IDENTIFIED BY 'profpass';
CREATE USER student_user IDENTIFIED BY 'studentpass';

Step 2: Create Roles

-- Create roles
CREATE ROLE admin_role;
CREATE ROLE professor_role;
CREATE ROLE student_role;

Step 3: Assign Privileges to Roles

-- Admin can do everything
GRANT ALL PRIVILEGES TO admin_role;

-- Professors can SELECT and UPDATE grades table
GRANT SELECT, UPDATE ON Grades TO professor_role;

-- Students can only SELECT (read-only access)
GRANT SELECT ON Grades TO student_role;

Step 4: Assign Roles to Users

-- Assign roles to specific users
GRANT admin_role TO admin_user;
GRANT professor_role TO professor_user;
GRANT student_role TO student_user;

Step 5: Usage Example

  • If professor_user logs in and tries:
UPDATE Grades SET score = 90 WHERE student_id = 101 AND course_id = 'CS101';

Allowed (since professor has UPDATE privilege).

  • If student_user tries:
UPDATE Grades SET score = 100 WHERE student_id = 101;

Fails (student only has SELECT privilege).

  • If student_user runs:
SELECT * FROM Grades WHERE student_id = 101;

Allowed (read-only access).

Advanced Security Features

  1. Column-Level Privileges

    • Limit access to specific columns.
    GRANT SELECT (student_id, course_id) ON Grades TO student_role;
  2. Views for Security

    • Instead of giving direct access, create views.
    CREATE VIEW student_view AS
    SELECT student_id, course_id, score
    FROM Grades
    WHERE student_id = SYS_CONTEXT('USERENV','SESSION_USER');
    GRANT SELECT ON student_view TO student_role;

    Each student only sees their own grades.

  3. Revoking Privileges

    • If a user should no longer access data:
    REVOKE UPDATE ON Grades FROM professor_role;

Data Encryption

Encryption = the process of converting plain text (readable data) into cipher text (unreadable format) using an encryption algorithm and key.

Decryption = converting ciphertext back into plaintext with the correct key.

The purpose: even if unauthorized users access the database or backups, they cannot read sensitive data without the decryption key.

Types of Encryption in Databases

  1. Encryption at Rest
    • Protects stored data (disk files, backups, logs).
    • If someone steals the physical storage, data remains unreadable.
    • Example: Transparent Data Encryption (TDE) in Oracle, SQL Server, PostgreSQL.
  2. Encryption in Transit
    • Protects data moving between client ↔ database server over the network.
    • Uses protocols like TLS/SSL.
  3. Column-Level or Field-Level Encryption
    • Encrypts specific sensitive columns in tables (e.g., passwords, credit card numbers).
    • Provides fine-grained protection but may impact performance.
  4. Application-Level Encryption
    • Application encrypts data before inserting into DB and decrypts after fetching.
    • Database only stores ciphertext.

Example of Column-Level Encryption

Suppose we design a Banking Database where credit_card_number must be encrypted.

CREATE TABLE Customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100),
credit_card_number VARBINARY(256) -- store as encrypted
);

Insert with Encryption:

-- Create a symmetric key
CREATE SYMMETRIC KEY CreditCardKey
WITH ALGORITHM = AES_256
ENCRYPTION BY PASSWORD = 'StrongPassword123';

-- Open the key
OPEN SYMMETRIC KEY CreditCardKey
DECRYPTION BY PASSWORD = 'StrongPassword123';

-- Insert encrypted value
INSERT INTO Customers (customer_id, name, credit_card_number)
VALUES (1, 'Alice', EncryptByKey(Key_GUID('CreditCardKey'), '4111111111111111'));

The card number is stored in the database as ciphertext, not plain text.

Decrypt when Reading:

-- Decrypt the credit card number
SELECT name,
CONVERT(VARCHAR, DecryptByKey(credit_card_number)) AS credit_card_number
FROM Customers;

Only users with the right key/password can view the real number.

SQL Injection

SQL Injection (SQLi) happens when untrusted input is concatenated directly into an SQL query, allowing attackers to manipulate the query.

Example of a vulnerable query (string concatenation):

-- Suppose 'username' and 'password' come directly from user input
SELECT * FROM Users
WHERE username = ' " + user_input + " '
AND password = ' " + pass_input + " ';

If an attacker enters:

  • username = 'admin'
  • password = "' OR '1'='1"

The query becomes:

SELECT * FROM Users
WHERE username = 'admin'
AND password = '' OR '1'='1';
  • '1'='1' is always true, so the attacker logs in without a valid password.

SQL Injection Prevention at Database Design Level

When designing a secure database system, you don’t just rely on the application — you enforce controls at multiple layers:

Use Parameterized Queries / Prepared Statements

  • Instead of embedding input directly into SQL, use placeholders (? or :param).
  • DB engine treats inputs as data, not executable SQL.

Example in Python (safe with placeholders):

cursor.execute(
"SELECT * FROM Users WHERE username = %s AND password = %s",
(username, password)
)

Here, even if password = "' OR '1'='1", it is treated as a string, not SQL code.

Stored Procedures with Parameters

Encapsulate queries in stored procedures, and only allow execution via defined parameters.

CREATE PROCEDURE AuthenticateUser (@username NVARCHAR(50), @password NVARCHAR(50))
AS
BEGIN
SELECT * FROM Users
WHERE username = @username AND password = @password;
END;

Prevents direct manipulation of SQL strings.

Use the Principle of Least Privilege

  • Application accounts should have only the privileges they need.
  • Example:
    • The web app should only SELECT/INSERT into Users.
    • It should NOT have DROP TABLE, ALTER, or GRANT privileges.
GRANT SELECT, INSERT, UPDATE ON Users TO app_user;

Even if SQL injection happens, the attacker’s damage is limited.

Input Validation & Constraints at Schema Level

  • Use CHECK constraints, data types, NOT NULL, foreign keys to enforce valid inputs.
  • Example:
CREATE TABLE Users (
user_id INT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash CHAR(64) NOT NULL,
email VARCHAR(100) CHECK (email LIKE '%@%')
);

Even if injected input reaches DB, invalid formats get rejected.

Use Views for Restricted Access

  • Instead of exposing sensitive tables directly, create views with limited columns.
  • Example:
CREATE VIEW SafeUsers AS
SELECT user_id, username, email
FROM Users;
GRANT SELECT ON SafeUsers TO app_user;

Prevents SQL injection from exposing sensitive fields (like password hashes).