一、MongoDB默认安全设置的那些坑

刚接触MongoDB时,很多人会觉得它开箱即用特别方便——不用像传统数据库那样折腾用户权限,甚至默认连密码都不设。但这种"方便"背后藏着大问题:2017年就有公司因为MongoDB默认配置漏洞,导致数TB用户数据被黑客清空勒索。下面这段连接代码展示了典型的风险操作:

// 危险示例:使用默认配置连接MongoDB(技术栈:Node.js + MongoDB驱动)
const { MongoClient } = require('mongodb');
const url = 'mongodb://localhost:27017'; // 无认证连接

async function main() {
  const client = new MongoClient(url);
  try {
    await client.connect();
    console.log('成功连接到数据库!');
    // 任何人都可以在这里执行任意操作...
  } finally {
    await client.close();
  }
}
main().catch(console.error);

这种配置下,攻击者只需要知道服务器IP,就能像操作自己电脑一样随意增删改查数据。更可怕的是,MongoDB默认会监听所有网络接口(0.0.0.0),相当于把大门敞开给整个互联网。

二、加固必须做的四件事

1. 启用访问控制

启动MongoDB服务时一定要加上--auth参数,或者在配置文件中设置security.authorization: enabled。但要注意顺序:

# 正确流程(技术栈:Linux系统管理)
# 1. 先以无认证模式启动
mongod --port 27017 --dbpath /data/db

# 2. 创建管理员(在mongo shell中执行)
use admin
db.createUser({
  user: "superadmin",
  pwd: "ComplexP@ssw0rd!2023", // 实际使用中要用更强密码
  roles: ["root"]
})

# 3. 关闭服务后重新启用认证
mongod --auth --port 27017 --dbpath /data/db

2. 网络层隔离

生产环境务必修改默认端口,并通过防火墙限制访问源:

// 安全连接示例(技术栈:Node.js)
const secureUrl = 'mongodb://superadmin:ComplexP@ssw0rd!2023@localhost:27018/admin?authSource=admin';
// 注意三个关键点:
// 1. 使用非默认端口27018
// 2. 指定认证数据库为admin
// 3. 连接字符串包含用户名密码

3. 加密传输配置

TLS加密是防止中间人攻击的关键,配置示例:

# mongod.conf 关键配置(技术栈:MongoDB配置文件)
net:
  ssl:
    mode: requireSSL
    PEMKeyFile: /etc/ssl/mongodb.pem
    CAFile: /etc/ssl/ca.pem
  bindIp: 127.0.0.1 # 只允许本地连接

4. 精细化权限控制

不要所有用户都用admin权限,应该按需分配:

// 创建应用专用用户(在mongo shell中执行)
use appdb
db.createUser({
  user: "appuser",
  pwd: "App@Secure123",
  roles: [
    { role: "readWrite", db: "appdb" },
    { role: "read", db: "reportdb" }
  ]
})

三、进阶防护策略

1. 审计日志配置

记录所有敏感操作,配置示例:

# mongod.conf 审计配置
auditLog:
  destination: file
  format: JSON
  path: /var/log/mongodb/audit.json
  filter: '{ "users": { $exists: true } }'

2. 数据字段级加密

使用MongoDB的客户端字段级加密(CSFLE):

// 字段加密示例(技术栈:Node.js)
const { ClientEncryption } = require('mongodb-client-encryption');
const encryption = new ClientEncryption(client, {
  keyVaultNamespace: 'encryption.__keyVault',
  kmsProviders: {
    local: { key: Buffer.from('32字节长的密钥...', 'utf-8') }
  }
});

// 加密身份证号字段
const encryptedId = await encryption.encrypt(
  '1234567890',
  {
    algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic',
    keyId: keyId
  }
);

3. 定期安全扫描

使用mongodb-security-checklist工具:

# 安全扫描示例(技术栈:Linux命令行)
docker run --rm -it ghcr.io/stamparm/mongodb-security-checklist \
  -h your.mongo.host -p 27017 -u admin -p 'yourpassword'

四、常见问题解决方案

1. 连接数暴增问题

突然出现大量连接可能是扫描攻击,解决方案:

# mongod.conf 连接限制
net:
  maxIncomingConnections: 500
  serviceExecutor: 'adaptive'

2. 认证失败排查

当遇到Authentication failed错误时,检查:

// 诊断脚本(技术栈:Node.js)
const { MongoClient } = require('mongodb');
const uri = 'mongodb://wrong:password@localhost:27017/admin?authMechanism=SCRAM-SHA-1';

MongoClient.connect(uri, {
  serverSelectionTimeoutMS: 5000,
  authMechanism: 'SCRAM-SHA-256' // 明确指定认证机制
}).catch(err => {
  console.error('认证失败原因:', err.message);
  // 常见错误:
  // - MONGODB-CR已废弃
  // - 用户名包含特殊字符
  // - 密码过期
});

3. 备份加密策略

即使是备份文件也要加密:

# 加密备份示例(技术栈:Linux + OpenSSL)
mongodump --uri="mongodb://user:pwd@localhost:27017/dbname" \
  --archive | openssl enc -aes-256-cbc -salt -out backup.enc -k "密码"

五、不同场景下的最佳实践

1. 开发环境配置

虽然开发环境可以放宽限制,但仍需基础防护:

# Docker开发环境示例(技术栈:Docker)
version: '3'
services:
  mongo:
    image: mongo:5.0
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: devadmin
      MONGO_INITDB_ROOT_PASSWORD: Dev@Password123
    command: ["--bind_ip_all", "--auth"]

2. 云服务特殊配置

AWS DocumentDB等云服务有额外要求:

// 云服务连接示例(技术栈:Node.js)
const { MongoClient } = require('mongodb');
const uri = 'mongodb://user:password@docdb-2023.cluster.us-east-1.docdb.amazonaws.com:27017/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0';

// 特别注意:
// 1. 必须使用TLS
// 2. 需要下载特定的CA证书
// 3. 要指定replicaSet名称

3. 容器化部署要点

Kubernetes环境需要特别注意:

# Kubernetes ConfigMap示例(技术栈:K8s)
apiVersion: v1
kind: ConfigMap
metadata:
  name: mongodb-config
data:
  mongod.conf: |
    security:
      authorization: enabled
      keyFile: /etc/mongodb-keyfile
    net:
      bindIp: 0.0.0.0
      port: 27017
      tls:
        mode: requireTLS
        certificateKeyFile: /etc/ssl/tls.crt

六、安全加固效果验证

完成所有配置后,建议用以下方法验证:

// 安全测试脚本(技术栈:Node.js)
const insecureChecks = [
  { desc: '测试匿名访问', cmd: { listDatabases: 1 } },
  { desc: '测试未加密连接', uri: 'mongodb://localhost:27017' }
];

insecureChecks.forEach(async test => {
  try {
    const client = new MongoClient(test.uri || 'mongodb://localhost:27017');
    await client.connect();
    const result = await client.db().admin().command(test.cmd);
    console.error(`[危险] ${test.desc} 成功!`);
  } catch (e) {
    console.log(`[安全] ${test.desc} 已拦截: ${e.message}`);
  }
});

七、总结与持续防护

安全加固不是一次性的工作,需要:

  1. 每季度审查用户权限
  2. 及时安装安全补丁
  3. 监控异常查询模式
  4. 定期演练灾难恢复

最后记住:默认安全≠安全,数据库安全需要主动配置和持续维护。从今天开始,检查你的MongoDB配置是否还"裸奔"在互联网上吧!