Appearance
PostgreSQL 防止服务器欺骗学习笔记
概述
在企业数据库环境中,服务器欺骗是一个严重的安全威胁。当 PostgreSQL 服务器因维护、故障或其他原因关闭时,恶意用户可能会利用这个窗口期启动伪造的数据库服务器,试图窃取客户端的认证凭据和敏感查询。
服务器欺骗威胁分析
攻击原理
威胁场景
真实业务风险在生产环境中,以下场景容易遭受服务器欺骗攻击:
- 数据库维护窗口期:DBA 执行版本升级或硬件维护
- 服务器故障期间:主服务器宕机,客户端重连时
- 开发测试环境:多个开发者共享同一台服务器
- 云环境迁移:服务器迁移过程中的连接切换 :::
防护策略详解
1. Local 连接防护
1.1 Unix 域套接字目录权限控制
解决问题: 防止恶意用户在默认套接字目录创建伪造套接字文件
配置示例:
bash
# 1. 创建专用的安全套接字目录
sudo mkdir -p /var/run/postgresql
sudo chown postgres:postgres /var/run/postgresql
sudo chmod 755 /var/run/postgresql
# 2. 配置 postgresql.conf
echo "unix_socket_directories = '/var/run/postgresql'" >> /etc/postgresql/15/main/postgresql.conf
# 3. 重启PostgreSQL服务
sudo systemctl restart postgresql
分析过程:
unix_socket_directories
指定套接字文件位置- 通过目录权限控制,只允许
postgres
用户创建套接字文件 - 恶意用户无法在此目录创建伪造套接字
验证配置效果:
bash
# 检查套接字文件位置和权限
ls -la /var/run/postgresql/
# 输出示例:
# srwxrwxrwx 1 postgres postgres 0 Jan 15 10:30 .s.PGSQL.5432
# -rw------- 1 postgres postgres 5 Jan 15 10:30 .s.PGSQL.5432.lock
1.2 符号链接防护
解决问题: 防止应用程序仍然引用 /tmp
目录中的套接字文件
bash
# 创建指向安全位置的符号链接
sudo ln -sf /var/run/postgresql/.s.PGSQL.5432 /tmp/.s.PGSQL.5432
# 修改系统清理脚本,防止删除符号链接
echo '#!/bin/bash
# 清理临时文件,但保留PostgreSQL套接字符号链接
find /tmp -type f -atime +7 -not -name ".s.PGSQL.*" -delete
' > /etc/cron.daily/cleanup-tmp
1.3 requirepeer 连接验证
解决问题: 客户端验证服务器进程的实际所有者
客户端连接配置:
python
import psycopg2
# Python客户端使用 requirepeer 验证
conn_params = {
'host': '/var/run/postgresql', # Unix套接字路径
'database': 'myapp',
'user': 'app_user',
'password': 'secure_password',
'options': '-c search_path=public',
# 要求连接的服务器进程所有者必须是 postgres
'requirepeer': 'postgres'
}
try:
conn = psycopg2.connect(**conn_params)
print("✅ 连接验证成功,服务器进程所有者正确")
except psycopg2.OperationalError as e:
print(f"❌ 连接失败,可能的服务器欺骗: {e}")
2. TCP 连接防护
2.1 SSL 证书验证
解决问题: 通过加密和证书验证确保连接到合法服务器
服务器端 SSL 配置:
bash
# 1. 生成服务器证书(生产环境应使用CA签发)
openssl req -new -x509 -days 365 -nodes -text \
-out /etc/postgresql/15/main/server.crt \
-keyout /etc/postgresql/15/main/server.key \
-subj "/CN=postgresql.company.com"
# 2. 设置证书文件权限
sudo chown postgres:postgres /etc/postgresql/15/main/server.crt
sudo chown postgres:postgres /etc/postgresql/15/main/server.key
sudo chmod 600 /etc/postgresql/15/main/server.key
sudo chmod 644 /etc/postgresql/15/main/server.crt
postgresql.conf SSL 配置:
ini
# SSL 连接配置
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ca_file = 'ca.crt' # 如果使用客户端证书验证
# 强制SSL连接
ssl_min_protocol_version = 'TLSv1.2'
ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'
ssl_prefer_server_ciphers = on
pg_hba.conf 配置:
bash
# 只允许SSL连接,拒绝非加密连接
# TYPE DATABASE USER ADDRESS METHOD
hostssl all all 0.0.0.0/0 scram-sha-256
host all all 127.0.0.1/32 reject # 拒绝本地非SSL连接
2.2 客户端 SSL 验证配置
完整的 Java 客户端示例:
java
import java.sql.*;
import java.util.Properties;
public class SecurePostgreSQLConnection {
public static void main(String[] args) {
String url = "jdbc:postgresql://postgresql.company.com:5432/myapp";
Properties props = new Properties();
props.setProperty("user", "app_user");
props.setProperty("password", "secure_password");
// SSL 安全连接配置
props.setProperty("ssl", "true");
props.setProperty("sslmode", "verify-full"); // 验证服务器证书和主机名
props.setProperty("sslcert", "/path/to/client.crt"); // 客户端证书
props.setProperty("sslkey", "/path/to/client.key"); // 客户端私钥
props.setProperty("sslrootcert", "/path/to/ca.crt"); // CA根证书
try (Connection conn = DriverManager.getConnection(url, props)) {
System.out.println("✅ 安全连接建立成功");
// 验证连接安全性
DatabaseMetaData metaData = conn.getMetaData();
System.out.println("数据库版本: " + metaData.getDatabaseProductVersion());
} catch (SQLException e) {
System.err.println("❌ 连接失败: " + e.getMessage());
// 可能是服务器欺骗或证书验证失败
}
}
}
2.3 系统 CA 池使用
解决问题: 在生产环境中使用受信任的证书颁发机构
python
import psycopg2
import ssl
def create_secure_connection():
"""使用系统CA池创建安全连接"""
conn_params = {
'host': 'prod-postgresql.company.com',
'port': 5432,
'database': 'production_db',
'user': 'app_user',
'password': 'secure_password',
'sslmode': 'verify-full', # 强制完整验证
'sslrootcert': 'system', # 使用系统CA池
}
try:
# 建立连接
conn = psycopg2.connect(**conn_params)
# 验证SSL连接状态
with conn.cursor() as cur:
cur.execute("SELECT ssl_is_used(), ssl_version(), ssl_cipher();")
ssl_info = cur.fetchone()
print(f"SSL使用状态: {ssl_info[0]}")
print(f"SSL版本: {ssl_info[1]}")
print(f"加密套件: {ssl_info[2]}")
return conn
except psycopg2.OperationalError as e:
print(f"❌ SSL连接失败: {e}")
raise
# 使用示例
if __name__ == "__main__":
conn = create_secure_connection()
# 执行业务逻辑...
conn.close()
3. 特定认证方法的防护
3.1 SCRAM-SHA-256 与 Channel Binding
解决问题: 防止 SCRAM 认证过程中的中间人攻击
python
import psycopg2
def scram_secure_connection():
"""使用SCRAM-SHA-256和通道绑定的安全连接"""
conn_params = {
'host': 'postgresql.company.com',
'database': 'secure_app',
'user': 'secure_user',
'password': 'complex_password_123!',
# SSL 必需配置
'sslmode': 'verify-full',
'sslrootcert': '/etc/ssl/certs/company-ca.crt',
# SCRAM 和通道绑定配置
'channel_binding': 'require', # 要求通道绑定
}
try:
conn = psycopg2.connect(**conn_params)
# 验证认证方法
with conn.cursor() as cur:
cur.execute("""
SELECT
usename,
application_name,
client_addr,
backend_start,
ssl,
ssl_version
FROM pg_stat_ssl pss
JOIN pg_stat_activity psa ON pss.pid = psa.pid
WHERE psa.usename = current_user;
""")
connection_info = cur.fetchone()
print("认证连接信息:")
print(f" 用户: {connection_info[0]}")
print(f" SSL状态: {connection_info[4]}")
print(f" SSL版本: {connection_info[5]}")
return conn
except Exception as e:
print(f"❌ SCRAM认证失败: {e}")
raise
# pg_hba.conf 相应配置
"""
# TYPE DATABASE USER ADDRESS METHOD
hostssl secure_app secure_user 0.0.0.0/0 scram-sha-256
"""
3.2 GSSAPI 认证防护
解决问题: 企业环境中使用 Kerberos 认证防止欺骗
服务器端配置:
bash
# postgresql.conf GSSAPI 配置
krb_server_keyfile = '/etc/postgresql/15/main/krb5.keytab'
krb_caseins_users = on
# pg_hba.conf 配置
echo "hostgssenc all all 0.0.0.0/0 gss include_realm=0" >> /etc/postgresql/15/main/pg_hba.conf
客户端 GSSAPI 连接:
java
import java.sql.*;
import java.util.Properties;
public class KerberosPostgreSQLConnection {
public static Connection createGSSAPIConnection() throws SQLException {
// 设置Kerberos系统属性
System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");
System.setProperty("java.security.auth.login.config", "/path/to/jaas.conf");
String url = "jdbc:postgresql://postgresql.company.com:5432/enterprise_db";
Properties props = new Properties();
// GSSAPI 认证配置
props.setProperty("user", "[email protected]");
props.setProperty("gsslib", "gssapi"); // 使用GSSAPI
props.setProperty("gssencmode", "require"); // 强制GSSAPI加密
props.setProperty("jaasApplicationName", "PostgreSQLClient");
try {
Connection conn = DriverManager.getConnection(url, props);
System.out.println("✅ GSSAPI认证成功");
return conn;
} catch (SQLException e) {
System.err.println("❌ GSSAPI认证失败: " + e.getMessage());
throw e;
}
}
}
防护效果对比
防护方法 | 防护等级 | 适用场景 | 实施复杂度 | 性能影响 |
---|---|---|---|---|
Unix 套接字权限 | 高 | 本地连接 | 低 | 无 |
requirepeer | 中 | 本地连接 | 低 | 无 |
SSL 证书验证 | 高 | 远程连接 | 中 | 轻微 |
系统 CA 池 | 很高 | 生产环境 | 中 | 轻微 |
SCRAM+通道绑定 | 高 | 认证安全 | 中 | 轻微 |
GSSAPI 加密 | 很高 | 企业环境 | 高 | 中等 |
攻击检测和响应
检测伪造服务器的 SQL 查询
sql
-- 检查当前连接的SSL状态
SELECT
pid,
usename,
application_name,
client_addr,
backend_start,
ssl,
ssl_version,
ssl_cipher
FROM pg_stat_ssl pss
JOIN pg_stat_activity psa ON pss.pid = psa.pid
WHERE ssl = true;
-- 检查异常连接模式
SELECT
client_addr,
COUNT(*) as connection_count,
MIN(backend_start) as first_connection,
MAX(backend_start) as last_connection
FROM pg_stat_activity
WHERE backend_start > NOW() - INTERVAL '1 hour'
GROUP BY client_addr
HAVING COUNT(*) > 50 -- 检测异常高频连接
ORDER BY connection_count DESC;
-- 监控认证失败
SELECT
log_time,
user_name,
database_name,
remote_host,
message
FROM pg_log
WHERE message LIKE '%authentication failed%'
AND log_time > NOW() - INTERVAL '24 hours'
ORDER BY log_time DESC;
总结
防止 PostgreSQL 服务器欺骗需要多层次的安全策略:
- 网络层防护: 使用 SSL/TLS 加密和证书验证
- 认证层防护: 采用强认证方法如 SCRAM-SHA-256 和 GSSAPI
- 系统层防护: 合理配置文件权限和套接字目录
- 应用层防护: 客户端进行严格的服务器验证
- 监控层防护: 实施持续监控和异常检测
在生产环境中,建议同时使用多种防护方法,构建深度防御体系。单一防护方法可能存在被绕过的风险,只有多层防护才能确保数据库连接的绝对安全。
通过实施这些安全措施,企业可以有效防范服务器欺骗攻击,保护数据库连接的完整性和机密性。