默認安裝面向兼容性,生產環境必須進行針對性加固。本文實戰記錄 **數據目錄權限演進 (3.21→3.23)、運行用戶隔離、授權表最小權限、本地日志洩露防範、編程層參數化查詢、語言特定防注入 API** 全鏈路加固清單。
| 版本 | 數據目錄權限 | 風險 | 現代標準 |
|---|---|---|---|
| 3.21.x | 775 (rwxrwxr-x) | 任意本地用戶可讀,拷貝 .MYD/.MYI/.frm 直讀數據 | 700 (rwx------) 擁有者 = mysql 用戶 組 = mysql 組 |
| 3.22.x | 770 (rwxrwx---) | 同組用戶可讀寫,仍不安全 | |
| 3.23+ | 700 (rwx------) | 僅 mysql 用戶可訪問,基準安全 |
生產環境檢查清單:
# 1. 確認運行用戶
ps -eo user,cmd | grep mysqld | grep -v grep
# 應為 mysql (或專用用戶),絕非 root
# 2. 修正目錄權限
chown -R mysql:mysql /var/lib/mysql
chmod 700 /var/lib/mysql
find /var/lib/mysql -type f -exec chmod 600 {} \;
# 3. 配置文件權限
chown root:mysql /etc/my.cnf
chmod 640 /etc/my.cnf
# 4. 日志文件權限
chown mysql:mysql /var/log/mysqld.log /var/log/mysql/*
chmod 640 /var/log/mysqld.log /var/log/mysql/*
-- 登錄 root 執行
DELETE FROM mysql.user WHERE User = ''; -- 匿名用戶
DELETE FROM mysql.user WHERE User = 'root' AND Host != 'localhost'; -- 非本地 root
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db = 'test' OR Db = 'test\_%';
FLUSH PRIVILEGES;
| 角色 | 權限授予語句 | Host 限制 |
|---|---|---|
| Web 應用讀寫 | GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_rw'@'10.0.1.%' IDENTIFIED BY 'StrongP@ssw0rd'; | 應用服務器段 |
| Web 應用只讀 | GRANT SELECT ON app_db.* TO 'app_ro'@'10.0.1.%' IDENTIFIED BY 'StrongP@ssw0rd'; | 應用服務器段 |
| 備份帳號 | GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER, RELOAD, PROCESS ON *.* TO 'backup'@'10.0.2.10' IDENTIFIED BY 'StrongP@ssw0rd'; | 備份服務器 IP |
| 監控帳號 | GRANT PROCESS, REPLICATION CLIENT, SELECT ON performance_schema.* TO 'monitor'@'10.0.3.%' IDENTIFIED BY 'StrongP@ssw0rd'; | 監控系統段 |
| DBA 管理 | GRANT ALL PRIVILEGES ON *.* TO 'dba'@'10.0.0.5' IDENTIFIED BY 'StrongP@ssw0rd' WITH GRANT OPTION; | 堡壘機/管理 IP |
原則:Host 盡量具體到 IP/段,禁用 '%';權限按「最小完成任務」授予;定期 SHOW GRANTS 審計。
| 權限 | 風險場景 | 策略 |
|---|---|---|
FILE | SELECT ... INTO OUTFILE '/var/www/html/shell.php' 寫 Webshell;LOAD DATA INFILE '/etc/passwd' 讀敏感文件 | 僅 DBA/備份按需授予,且設置 secure_file_priv=/var/lib/mysql-files/ |
PROCESS | SHOW FULL PROCESSLIST 看到含密碼的 SQL (如 SET PASSWORD、INSERT ... VALUES('pwd')) | 僅監控/管理帳號 |
SUPER | KILL 任意線程、修改全局變量、CHANGE MASTER、啟用一般日志寫 Webshell | 極度收緊,僅 DBA |
SHUTDOWN | 遠程關閉數據庫服務 | 禁用 |
CREATE USER / GRANT OPTION | 權限提升、橫向移動 | 僅 DBA |
風險:命令行帶密碼 mysqldump -uroot -pP@ssw0rd db > dump.sql 寫入 .bash_history;SQL 交互修改密碼 SET PASSWORD = PASSWORD('new') 寫入 .mysql_history。
# 當前用戶
rm -f ~/.bash_history ~/.mysql_history
ln -s /dev/null ~/.bash_history
ln -s /dev/null ~/.mysql_history
# 系統級 (所有用戶生效,需 root)
echo 'unset HISTFILE' >> /etc/profile
echo 'export MYSQL_HISTFILE=/dev/null' >> /etc/profile
# 或在用戶 .bashrc / .bash_profile 設置
# 錯誤示範
mysqldump -uroot -pP@ssw0rd db > dump.sql
mysql -uroot -pP@ssw0rd -e "UPDATE user SET password=PASSWORD('new')"
# 正確姿勢
mysqldump -uroot -p db > dump.sql # 交互輸入密碼
mysql -uroot -p -e "SET PASSWORD = PASSWORD('new')" # 交互輸入
# 或使用配置文件
[client]
user=root
password="P@ssw0rd" # 文件 600 權限
# mysqldump --defaults-file=/etc/mysql/backup.cnf db > dump.sql
| 語言/框架 | 標準防注入 API | 核心原理 | 禁用/危險用法 |
|---|---|---|---|
| PHP | PDO::prepare() / mysqli_prepare() | 服務端預處理 (emulate=false),參數與代碼分離 | 字符串拼接、mysqli_real_escape_string (僅作兼容) |
| Java | PreparedStatement / JPA @Query / MyBatis #{} | JDBC 預處理 / ORM 參數綁定 | Statement + 拼接、${} (MyBatis 原生 SQL) |
| Python | cursor.execute(sql, params) / SQLAlchemy text() | DB-API 參數化 / ORM 編譯 | cursor.execute(sql % params)、f-string 拼接 |
| Go | db.Prepare() / sqlx.NamedExec() | database/sql 預處理 | fmt.Sprintf 拼接 |
| C#/.NET | SqlCommand.Parameters.Add() / EF Core FromSqlInterpolated | ADO.NET 參數化 / EF 參數化 | String.Concat/Interpolation 直接拼接 |
| Node.js | mysql2/promise pool.execute() / Sequelize/Prisma/TypeORM | 預處理 / ORM 參數化 | connection.query(sql + val)、模板字符串拼接 |
| Perl | DBI prepare/execute / DBIx::Class | DBI 參數化 | do() 拼接、quote() 僅作備選 |
| C/C++ | mysql_stmt_prepare() / MySQL++ quote/escape | C API 預處理 / 封裝庫參數化 | sprintf 拼接、mysql_real_escape_string() 單獨使用 |
# 正則白名單示例 (PHP)
function validate($input, $type) {
$patterns = [
'int' => '^\d+$',
'uint' => '^[1-9]\d*$',
'email' => '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
'alpha' => '^[a-zA-Z]{1,50}$',
'alnum' => '^[a-zA-Z0-9]{1,50}$',
'safe' => '^[a-zA-Z0-9_\-\.@]{1,100}$', // 通用安全字符
'date' => '^\d{4}-\d{2}-\d{2}$',
'datetime'=> '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$',
];
return isset($patterns[$type]) && preg_match('/'.$patterns[$type].'/', $input);
}
# 使用
$id = $_GET['id'] ?? '';
if (!validate($id, 'uint')) { http_response_code(400); exit('Invalid ID'); }
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
McAfee Audit Plugin / pgaudit (PG) 記錄:登錄、DDL、DML、失敗嘗試、敏感表訪問。REQUIRE SSL 用戶、require_secure_transport=ON、skip_name_resolve=ON。MySQL 安全加固無銀彈,**目錄權限 700 + 專用運行用戶 + 最小權限帳號 + 禁用危險權限 + 參數化查詢 + 日志不洩露 + 網絡微隔離 + 審計可追溯**,九層防線缺一不可。將上述清單固化為 Ansible Playbook / Terraform Module / Dockerfile 最佳實踐,納入 CI/CD 門禁與定期巡檢,方能構建經得起實戰考驗的數據庫安全基線。