MySQL

MySQL Security & Permissions Interview Questions

8 Questions

User accounts defined by username and host. Privileges granted at different levels: global, database, table, column. GRANT/REVOKE manage permissions.

-- Create user
CREATE USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'strong_password';
CREATE USER 'webapp'@'app-server.domain.com' IDENTIFIED BY 'password';

-- Grant privileges (global)
GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost';

-- Grant privileges (database level)
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'appuser'@'192.168.1.%';

-- Grant privileges (table level)
GRANT SELECT, UPDATE ON mydb.orders TO 'analyst'@'%';

-- Grant privileges (column level)
GRANT UPDATE(salary, commission) ON hr.employees TO 'payroll'@'localhost';

-- Grant with GRANT OPTION (delegate)
GRANT SELECT ON mydb.* TO 'user'@'localhost' WITH GRANT OPTION;

-- View user privileges
SHOW GRANTS FOR 'appuser'@'192.168.1.%';

-- Remove privileges
REVOKE INSERT, UPDATE ON mydb.* FROM 'appuser'@'192.168.1.%';

-- Delete user
DROP USER 'appuser'@'192.168.1.%';

Why it matters: Enforce principle of least privilege.

Real applications: Application users, DBA users, read-only access.

Common mistakes: Overly permissive grants, weak passwords, root access for apps.

Password storage: Hashed in mysql.user table. MySQL 8.0 uses caching_sha2_password (SHA-256). Older versions use mysql_native_password. Support for role-based login.

-- Check password plugin
SELECT user, host, plugin FROM mysql.user;
-- Result: plugin = 'caching_sha2_password' (MySQL 8.0+)

-- Set password
SET PASSWORD FOR 'user'@'localhost' = 'new_password';
ALTER USER 'user'@'localhost' IDENTIFIED BY 'new_password';

-- Set password with specific hash
ALTER USER 'user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
ALTER USER 'user'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'password';

-- Password validation policy
SHOW VARIABLES LIKE 'validate_password%';
-- validate_password_policy: MEDIUM (default)
-- validate_password_length: 8 (minimum length)

-- Setup password policy
SET GLOBAL validate_password.policy = 'STRONG';
SET GLOBAL validate_password.length = 12;

-- Password expiration
ALTER USER 'user'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
SELECT * FROM mysql.user WHERE password_expired = 'Y';

-- Account locking
ALTER USER 'user'@'localhost' ACCOUNT LOCK;
ALTER USER 'user'@'localhost' ACCOUNT UNLOCK;

Why it matters: Authentication security, password best practices.

Real applications: User management, compliance requirements.

Common mistakes: Weak passwords, old password plugins, no expiration policy.

Roles (MySQL 8.0+) group permissions for users. Simplify permission management for similar users. Can be nested.

-- Create role
CREATE ROLE 'app_read_only', 'app_write', 'app_admin';

-- Grant privileges to role
GRANT SELECT ON mydb.* TO 'app_read_only';
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'app_write';
GRANT ALL PRIVILEGES ON mydb.* TO 'app_admin';

-- Assign role to user
CREATE USER 'user1'@'localhost' IDENTIFIED BY 'password';
GRANT 'app_read_only' TO 'user1'@'localhost';

-- Set default role
SET DEFAULT ROLE 'app_read_only' FOR 'user1'@'localhost';

-- User must activate role on connect
-- Add to connection or my.cnf
SET ROLE ALL;  -- Activate all assigned roles

-- View role assignments
SELECT * FROM mysql.role_edges;
SELECT CURRENT_ROLE;

-- Role hierarchy
CREATE ROLE 'developer';
GRANT 'app_read_only' TO 'developer';  -- Nested role
GRANT 'developer' TO 'user2'@'localhost';  -- User gets all permissions

Why it matters: Simplified permission management at scale.

Real applications: Multi-app environments, standardized permissions.

Common mistakes: Roles not set as default, MySQL < 8.0 not supported.

SSL/TLS encrypts connections. Prevents credential interception. Configure with server certificates and require REQUIRE SSL for users.

-- Generate SSL certificates (one time)
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem
openssl genrsa -out server-key.pem 4096
openssl req -new -key server-key.pem -out server.csr
openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem
openssl genrsa -out client-key.pem 4096
openssl req -new -key client-key.pem -out client.csr
openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem

-- Configure MySQL server (my.cnf)
[mysqld]
ssl-ca=/path/to/ca.pem
ssl-cert=/path/to/server-cert.pem
ssl-key=/path/to/server-key.pem

-- Restart MySQL
systemctl restart mysql

-- Verify SSL enabled
SHOW VARIABLES LIKE 'have_ssl';  -- Result: YES

-- Require SSL for user
CREATE USER 'secuser'@'192.168.1.%' IDENTIFIED BY 'password' REQUIRE SSL;
ALTER USER 'secuser'@'192.168.1.%' REQUIRE SSL;

-- Connect with SSL
mysql --ssl-ca=ca.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem -h servername
mysql --ssl-mode=REQUIRED -h servername

-- Verify SSL connection
SHOW STATUS LIKE 'Ssl_version';  -- Result: TLSv1.2

Why it matters: Credential and data encryption in transit.

Real applications: Remote connections, compliance (GDPR, HIPAA).

Common mistakes: Not enforcing SSL, self-signed certificates in production.

GRANT assigns permissions. REVOKE removes permissions. Privilege levels: global, database, table, column, procedure. WITH GRANT OPTION allows delegation.

-- Global privileges (*.*)
GRANT SELECT, SHOW DATABASES ON *.* TO 'user'@'host';

-- Database privileges (database.*)
GRANT CREATE, ALTER, DROP ON testdb.* TO 'user'@'host';

-- Table privileges (database.table)
GRANT SELECT, UPDATE, DELETE ON testdb.orders TO 'user'@'host';

-- Column privileges (specific columns)
GRANT UPDATE(price, discount), SELECT ON sales.products TO 'analyst'@'host';

-- Procedure/Function privileges
GRANT EXECUTE ON PROCEDURE testdb.calculate_revenue TO 'user'@'host';

-- With grant option (can delegate)
GRANT SELECT ON *.* TO 'user'@'host' WITH GRANT OPTION;

-- Revoke specific privilege
REVOKE INSERT ON testdb.* FROM 'user'@'host';

-- Revoke all privileges
REVOKE ALL PRIVILEGES ON *.* FROM 'user'@'host';

-- Revoke grant option
REVOKE GRANT OPTION ON *.* FROM 'user'@'host';

-- Common privilege sets
-- SELECT: Read data
-- INSERT: Insert new data
-- UPDATE: Modify data
-- DELETE: Remove data
-- ALL: All privileges (except WITH GRANT OPTION)
-- USAGE: No access

-- Application user (principle of least privilege)
GRANT SELECT, INSERT, UPDATE ON appdb.* TO 'app'@'app-server' IDENTIFIED BY 'password';

Why it matters: Enforce least privilege principle.

Real applications: Application users, analyst access, contractor accounts.

Common mistakes: ALL PRIVILEGES to app users, not revoking stale privileges.

Auditing tracks user actions via general log, audit plugin, or application logging. Important for compliance and security investigations.

-- Enable general query log
SET GLOBAL general_log = ON;
SET GLOBAL log_output = 'TABLE';  -- Log to mysql.general_log table

-- Query log (includes connection, disconnection, queries)
SELECT * FROM mysql.general_log WHERE user = 'appuser' ORDER BY event_time DESC LIMIT 10;

-- Disable query log (verbose)
SET GLOBAL general_log = OFF;

-- MySQL Audit Plugin
INSTALL PLUGIN audit_log SONAME 'audit_log.so';
SET GLOBAL audit_log_events = 'Connect,Query_ddl,Query_dml';

-- Slow query log (for query analysis)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;  -- Queries > 2 seconds
SELECT * FROM mysql.slow_log;

-- Application-level audit table
CREATE TABLE audit_log (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user VARCHAR(100),
    action VARCHAR(50),
    table_name VARCHAR(100),
    record_id INT,
    old_value TEXT,
    new_value TEXT,
    timestamp DATETIME DEFAULT NOW()
);

-- Trigger for audit
CREATE TRIGGER orders_update_audit AFTER UPDATE ON orders
FOR EACH ROW
INSERT INTO audit_log (user, action, table_name, record_id, old_value, new_value)
VALUES (USER(), 'UPDATE', 'orders', NEW.id, JSON_OBJECT('status', OLD.status), JSON_OBJECT('status', NEW.status));

Why it matters: Compliance, threat detection, investigation.

Real applications: Security audits, PCI-DSS compliance, forensics.

Common mistakes: Not logging to persistent storage, performance impact ignored.

SQL injection: Unsanitized input. Default credentials: Weak passwords. Privilege escalation: Overpermissive grants. Unencrypted transport: No SSL/TLS. Exposed backups: World-readable dump files.

-- SQL Injection vulnerability
// Vulnerable code
SELECT * FROM users WHERE username = '$username' AND password = '$password';
// Injection: $username = "admin' --"

-- Prevention: Use prepared statements
PREPARE stmt FROM 'SELECT * FROM users WHERE username = ? AND password = ?';
SET @u = 'user', @p = 'pass';
EXECUTE stmt USING @u, @p;

-- Default credentials vulnerability
-- Change root password
ALTER USER 'root'@'localhost' IDENTIFIED BY 'strong_password';

-- Delete anonymous users
DROP USER ''@'localhost';
DROP USER ''@'%.%.%.%';

-- Privilege escalation
-- Don't grant too much
GRANT ALL ON *.* TO 'app'@'app-server';  -- BAD
GRANT SELECT ON db.* TO 'app'@'app-server';  -- GOOD

-- Unencrypted transport
-- Require SSL
ALTER USER 'user'@'host' REQUIRE SSL;

-- Exposed backups
-- Restrict permissions on dump files
chmod 600 backup.sql
# Encrypt backups
gpg --encrypt backup.sql

-- Data exposure
-- Don't select * in logs
SELECT user, host FROM mysql.user;  -- DON'T SELECT password!
SELECT id, amount FROM orders;  -- DON'T SELECT credit_card_number!

Why it matters: Prevent data breaches and unauthorized access.

Real applications: Secure application design, regulatory compliance.

Common mistakes: No input validation, weak passwords, overpermissive access.

Best practices: Least privilege principle, strong passwords, SSL/TLS, regular audits, principle of separation, backup security, update regularly.

-- ✅ Good: Least privilege for applications
CREATE USER 'webdb'@'web-server' IDENTIFIED BY 'long_random_password';
GRANT SELECT, INSERT, UPDATE ON webdb.* TO 'webdb'@'web-server';
-- NO DELETE, NO ADMIN PRIVILEGES

-- ✅ Good: Separate users by role
CREATE USER 'app'@'app-host';  -- Application
CREATE USER 'backup'@'backup-host';  -- Backup jobs
CREATE USER 'analytics'@'analytics-host';  -- Read-only analytics
-- Each with minimal required permissions

-- ✅ Good: Strong password policy
SET GLOBAL validate_password.policy = 'STRONG';
SET GLOBAL validate_password.length = 12;
ALTER USER 'user'@'host' PASSWORD EXPIRE INTERVAL 90 DAY;

-- ✅ Good: Secure connections
ALTER USER 'remote-user'@'%.domain.com' REQUIRE SSL;
CREATE USER 'local-admin'@'localhost' IDENTIFIED BY 'pass';  -- Local is OK

-- ✅ Good: Regular audits
SELECT user, host, plugin FROM mysql.user;  -- Review users
SHOW GRANTS FOR 'appuser'@'%';  -- Verify permissions

-- ❌ Bad: Root access for applications
GRANT ALL ON *.* TO 'app'@'app-host';  -- Dangerous!

-- ❌ Bad: Weak passwords
CREATE USER 'user'@'host' IDENTIFIED BY '123';  -- Weak!

-- ❌ Bad: Exposed backups
mysqldump > backup.sql  # Readable by all!
chmod 644 backup.sql  # World readable!

-- ✅ Good: Encrypted backups
mysqldump | gpg -e > backup.sql.gpg
chmod 600 backup.sql.gpg

Why it matters: Prevent data breaches and compliance violations.

Real applications: Production systems, regulated industries.

Common mistakes: Overpermissive access, weak passwords, unencrypted backups.