65.9K
CodeProject 正在变化。 阅读更多。
Home

使用加密和身份验证保护 MongoDB

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2018 年 1 月 25 日

CPOL

10分钟阅读

viewsIcon

23848

如何使用加密和身份验证来保护 MongoDB

引言

本指南展示了如何实现 MongoDB Community Edition 的两种主要认证方法:Scram-SHA-1 和 X509 证书认证。它还涵盖了如何使用 TLS/SSL 加密客户端和服务器之间的事务。

身份验证

认证是一种机制,客户端通过它向服务器证明其身份,服务器也通过它向客户端证明其身份。该系统旨在防止恶意实体冒充服务器或客户端。

Scram-SHA-1

Scram-SHA-1 现在是 MongoDB 使用的默认认证方法。这里有一篇很棒的文章详细展示了该机制的应用方式。简而言之,它是一个质询-响应系统。当客户端首次在服务器注册时,服务器可以访问客户端的密码。服务器不直接存储密码,而是存储从密码派生出的两个哈希值以及一些用于实现哈希算法的元数据。服务器将元数据发送给客户端,客户端通过以与服务器相同的方式创建相同的哈希值来证明它拥有正确的密码。服务器通过首先发送其第二个存储的哈希值来向客户端证明自己。然后客户端使用其密码来验证哈希值。由于会话特定的随机字节被添加到数据交换中,因此事务不会受到重放攻击。

服务器的认证数据库

通过将客户端的详细信息存储在服务器的认证数据库中,将其注册到服务器。在激活认证机制之前,至少需要有一个客户端注册到服务器。推荐的做法是添加一个管理员作为第一个客户端。管理员的角色是创建其他用户。一旦创建了该用户,就可以激活认证机制。

创建管理员

在 Windows 命令提示符下,导航到 mongoDB/bin 文件夹,然后粘贴以下内容:

mongo
use admin
db.createUser(
  {
    user: "AdminUser",
    pwd: "AdminUserPwd",
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  }
)
exit

在这里,创建了一个名为 AdminUser、密码为 AdminUserPwd 的客户端。它被授予 userAdminAnyDatabase 角色,允许它创建其他用户。角色是另一个重要的安全措施,因为它们控制特定客户端可以向数据库发出的有效命令的数量。对于后面的一些示例,管理员需要对 MongoDB 的内部数据库拥有不受限制的访问权限。为此,它需要配置为 Root 用户。

mongo
use admin
db.createUser(
  {
    user: "AdminUser",
    pwd: "AdminUserPwd",
    roles: [ "root" ]
  }
)
exit

要检查服务器为名为 AdminUser 的客户端存储的数据,请在命令提示符下粘贴以下内容。

mongo
use admin
 db.system.users.findOne({user: "AdminUser"})
exit

iterationCount 是哈希算法应用于密码的次数。盐值是与密码一起使用的随机字节数组,用于派生密钥。它防止具有相同密码的客户端生成相同的密钥。storedKey 用于向服务器标识客户端,serverKey 用于向客户端标识服务器。

激活认证

在接下来的示例中,假设 MongoDB 已安装到 C:\mongodb\bin 目录中。最好避免使用包含空格的文件路径,因为它们在某些情况下可能导致异常。通过发出以下命令启动服务器:

cd  C:\mongodb\bin
mongod.exe --config C:\mongodb\bin\mongoDB0.cfg --serviceName MongoDB0 
           --serviceDisplayName MongoDB0 --install
net start MongoDB0

在此示例安装中,MongoDB 使用以下 mongoDB0.cfg 文件进行配置。

storage:  
    dbPath: "C:/MongoDB/MongoDB0"
systemLog:  
    destination: file
    path: "C:/MongoDB/log/MongoDB0.log"
    logAppend: true
    timeStampFormat: iso8601-utc
#replication:  
    #replSetName: "myReplSet"
net:  
    port: 27017
    #ssl:
        #mode: requireSSL
        #PEMKeyFile: "C:/MongoDB/SSL/mongo0.pem"
        #CAFile: "C:/MongoDB/SSL/mongoCA.crt"
        #clusterFile: "C:/MongoDB/SSL/mongo0.pem"
#security:  
    #authorization: enabled
    #clusterAuthMode: x509

行首的 # 符号表示该行的其余部分是注释。要启用授权,请从以“#security:”和“#authorization: enabled”开头的行中删除 #,然后保存文件。然后通过在命令窗口中发出以下命令来停止并重新启动服务。

net stop  MongoDB0
net start MongoDB0

现在可以使用 mongo 客户端 shell 以 AdminUser 身份登录到服务,然后创建另一个用户。

mongo  
use admin
db.auth("AdminUser", "AdminUserPwd" )
db.createUser(
  {
    user: "ScramUser",
    pwd: "ScramUserPwd",
    roles: [ { role: 'readWrite', db: 'TestMongoDB'} ]
  }
)
exit

ScramUser 现在可以读取和写入名为 TestMongoDB 的数据库。此示例展示了如何使用 C# 驱动程序连接到数据库。

const string connectionString ="mongodb://ScramUser:ScramUserPwd@localhost";
var client = new MongoClient(connectionString);
IMongoDatabase database = client.GetDatabase("TestMongoDB");
IMongoCollection<clubmember> collection = database.GetCollection<clubmember>("CarClub");
 long count = collection.Count(new BsonDocument());

基于证书的认证

使用这种认证方法,客户端和服务器都通过使用 X.509 身份证书相互验证其身份。证书由受信任的 证书颁发机构 (CA) 使用其私钥加密(签名)。证书包含其所有者的公钥以及一些数据,例如有效期,以便确定证书的有效性。服务器使用 CA 的公钥解密客户端的证书,并检查证书是否可接受。客户端对服务器的证书执行相同的操作。服务器还通过将客户端的证书与存储在服务器数据库中的条目进行比较,来检查客户端是否已注册。注册过程存储客户端证书主题行中给出的详细信息。主题行,也称为专有名称 (DN),可以具有多个属性。最常见的属性如下:

  • C = 国家
  • ST = 州或省
  • L = 位置
  • O = 组织名称
  • OU = 组织单位名称
  • CN = 通用名称,可以是网站

要注册证书中主题行为以下内容的客户端:

C=GB,ST=NPT,L=London,O=HPUser,OU=IT,CN=George-HP,emailAddress=myemail@myemail.com

执行类似的操作:

mongo  
use admin
db.auth("AdminUser", "AdminUserPwd" )
use $external
db.createUser(
  {
    createUser: "C=GB,ST=NPT,L=London,O=HPUser,OU=IT,CN=George-HP,
                 emailAddress=myemail@myemail.com",
    roles: [
             { role: 'readWrite', db: 'TestMongoDB' } 
           ],
    writeConcern: { w: "majority" , wtimeout: 5000 }
  }
)

这里使用的 AdminUser 需要具有 root 权限才能写入外部数据库。认证方法仅在认证过程中加密数据。认证后发生的数据交换不加密。为了加密这些交换,必须添加另一层安全性,即 传输层安全 (TLS)。Scram-SHA-1 认证可以在不使用 TLS 的情况下使用,但 X.509 认证需要启用 TLS。

传输层安全

TLS 采用 X.509 证书验证。使用 TLS,客户端和服务器的公钥都用于在初始(握手)交换期间加密数据。握手过程用于确定要遵循的协议并制定对称会话密钥。然后双方使用相同的会话密钥来加密后续数据交换。使用会话密钥是因为它比公钥/私钥对具有更好的性能。Mongo 的配置文件可能引用 SSL,但当前版本 MongoDb 中使用的协议实际上是 TLS。SSL 是一个已被 TLS 取代的旧协议。

如何使用自签名证书启用认证和加密

以下测试示例仅适用于与万维网隔离的本地内网。所使用的证书是自签名的,不使用独立的商业证书颁发机构。证书是使用 OpenSSL 生成的。OpenSSL 是一个用于生成自签名证书的开源控制台应用程序。可以从这里下载。安装很简单,但最好以管理员身份运行它,以避免在尝试写入某些目录时出现问题。我个人不喜欢直接在命令提示符下输入命令,因为它们很难编辑,而是将一系列命令复制到剪贴板,然后通过同时按下“ctrl”和“v”键将它们粘贴到命令提示符。这里有最常用命令的解释:https://www.sslshopper.com/article-most-common-openssl-commands.html

生成证书颁发机构证书

首先是生成 CA 的私钥。此密钥应安全存储。

genrsa -out mongoCA.key -aes256 8192

系统将提示您输入密钥的密码。密码不会回显到控制台,并且在输入密码时命令提示符不会移动。该密钥用于生成 CA 证书。

req -x509 -new -extensions v3_ca -key mongoCA.key -days 365 -out mongoCA.crt

这将生成一个名为 mongoCA.crt 的证书,有效期为 365 天。系统将提示您输入专有名称的属性。

生成服务器证书

该技术是创建一个证书请求,然后使用 CA 的私钥和证书请求创建证书。使用带有 OpenSSL 的命令字符串比单独输入所需参数要容易得多。以下生成一个名为 mongo0 的服务器的证书请求,其挑战密码为 mongo0Pwd

req -new -newkey rsa:4096 -nodes -out mongo0.csr -keyout mongo0.key 
-subj “/C=GB/ST/=NPT/L=London/O=mongo0/OU=IT/CN=George-HP/emailAddress=myemail@myemail.com " 
-passin pass:mongo0Pwd

要生成证书,请输入以下内容:

x509 -CA mongoCA.crt -CAkey mongoCA.key -CAcreateserial 
     -req -days 365 -in mongo0.csr -out mongo0.crt

现在需要将密钥和证书捆绑成一个 PEM 文件,供服务器使用。以管理员身份打开 Windows 命令提示符,导航到 OpenSSL 的 bin 目录并输入:

type mongo0.key mongo0.crt > mongo0.pem  

其中 type 是一个命令,它不是要求敲击键盘。

生成客户端证书

生成客户端证书的过程与生成服务器证书的过程类似。在本地网络中,通用名称通常设置为客户端机器的域名,OrganizationName 是客户端的名称。

为名为 x509User 的客户端创建证书请求:

req -new -newkey rsa:4096 -nodes -out x509User.csr -keyout x509User.key 
-subj “/C=GB/ST/=NPT/L=London/O=x509User/OU=IT/CN=George-HP/emailAddress=myemail@myemail.com " 
-passin pass:x509UserPwd

为名为 x509User 的客户端创建证书:

x509 -CA mongoCA.crt -CAkey mongoCA.key -CAcreateserial -req -days 365 
     -in x509User.csr -out x509User.crt

客户端要求将密钥和证书捆绑到一个 PFX 文件中,因为 PFX 文件在客户端的连接字符串中被引用。

 pkcs12 -export -in x509User.crt -inkey x509User.key -out x509User.pfx 

当系统提示输入导出密码时,输入挑战密码 (x509Pwd)。

配置服务器以使用 TLS 和 X.509 认证

步骤 1. 内务管理任务

MongoDB 文件夹中创建三个子目录,分别命名为 SSLlogMongoDB0。将已在 openssl /bin 文件夹中生成的以下文件复制到 SSL 文件夹。

  • x509User.pfx
  • mongo0.pem
  • mongoCA.crt

步骤 2. 编辑服务器的配置文件

文件 mongoDB0.cfg 应存储在 mongodb/bin 文件夹中。

storage:  
    dbPath: "C:/MongoDB/MongoDB0"
systemLog:  
    destination: file
    path: "C:/MongoDB/log/MongoDB0.log"
    logAppend: true
    timeStampFormat: iso8601-utc
#replication:  
    #replSetName: "myReplSet"
net:  
    port: 27017
    ssl:
        mode: requireSSL
        PEMKeyFile: "C:/MongoDB/SSL/mongo0.pem"
        CAFile: "C:/MongoDB/SSL/mongoCA.crt"
security:  
    authorization: enabled

步骤 3. 重启服务器

要使 mongodb 服务器使用 config 文件,只需停止它并通过在命令提示符下输入以下内容来重新启动它:

net stop MongoDB0
net start MongoDB0

步骤 4. 为 Root 用户生成证书

现在 TLS 已启用,需要一个 pem 文件才能使 AdminUser 登录到 mongo 服务,以便它可以将 x509User 注册为客户端。

req -new -newkey rsa:4096 -nodes -out AdminUser.csr -keyout AdminUser.key 
-subj “/C=GB/ST/=NPT/L=London/O=AdminUser/OU=IT/CN=George-HP/emailAddress=myemail@myemail.com "
-passin pass:AdminUserPwd

使用证书请求生成证书:

x509 -CA mongoCA.crt -CAkey mongoCA.key -CAcreateserial -req -days 365 
     -in AdminUser.csr -out AdminUser.crt

要生成 pem 文件,以管理员身份打开 Windows 命令提示符,导航到 OpenSSL/bin 目录并输入:

type AdminUser.key AdminUser.crt > AdminUser.pem  

将文件复制到 mongoDB/SSL 文件夹。现在 AdminUser 可以通过首先使用 TSL 连接到数据库,然后使用 SCRAM 进行身份验证来注册新用户。

mongo  --ssl --sslCAFile C:/MongoDB/SSL/mongoCA.crt 
       --sslPEMKeyFile C:/MongoDB/SSL/AdminUser.pem --host George-HP
use admin
db.auth("AdminUser", "AdminUserPwd" )
use $external
db.createUser(
  {
    createUser: "C=GB,ST=NPT,L=London,O=x509User,OU=IT,
                 CN=George-HP,emailAddress=myemail@myemail.com"
    roles: [
             { role: 'readWrite', db: 'TestMongoDB' } 
           ],
    writeConcern: { w: "majority" , wtimeout: 5000 }
  }
)

使用 C# 驱动程序连接到服务器

这是连接到在 localhost 上运行的实例的代码。

  const string connectionString = "mongodb://";
  //For a server running on a domain named GEORGE-DELL and port 27025 use
  //const string connectionString = "mongodb://GEORGE-DELL:27025";
     var clientSettings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
         clientSettings.SslSettings = new SslSettings();
         clientSettings.UseSsl = true;
         clientSettings.SslSettings.ClientCertificates = new List<x509certificate>()
          {
       		 new X509Certificate2(@"C:\MongoDB\SSL\x509User.pfx", "x509UserPwd")
           };
            clientSettings.Credentials = new[]
            {
                MongoCredential.CreateMongoX509Credential(
              "C=GB,ST=NPT,L=London,O=x509User,OU=IT,CN=George-HP,
               emailAddress=myemail@myemail.com")
            };
            clientSettings.SslSettings.EnabledSslProtocols = SslProtocols.Tls12;
            clientSettings.SslSettings.CheckCertificateRevocation = false;
            clientSettings.VerifySslCertificate = false;

        var client = new MongoClient(clientSettings);   
</x509certificate>

非常重要的是,用于在 mongod 中注册用户或包含在 C# 驱动程序连接字符串中的主题子字符串必须与客户端的 X.509 证书中包含的主题行完全相同,逐字逐句。要以所需的顺序和格式 (RFC2253) 查看证书的主题,请在 SSL 提示符下输入以下内容:

req -text -noout -verify -nameopt RFC2253 -in x509User.csr

使用模板

生成证书有点繁琐。但通过使用列出常用命令的模板,可以减少一些繁琐。大多数属性对于给定的实现是常量,如上所述,保持它们常量很重要。可以通过在文本编辑器中使用“查找和替换”来简单地编辑模板,从而更改证书之间变化的属性。

配置 Windows 防火墙

您可能需要更改 Windows 防火墙中的设置,以启用网络上不同域之间的连接。例如,您可以在命令提示符下使用 netsh 来向防火墙添加规则,以允许 MongoDB 服务器侦听端口 27025 的专用网络 TCP 连接,方法是使用以下命令:

netsh advfirewall firewall add rule name="mongod config svr inbound" 
profile=private dir=in action=allow protocol=TCP remoteip=any localport=27025

显示所有现有规则的命令是:

netsh advfirewall firewall show rule name=all

这里有其他示例:https://docs.mongodb.com/manual/tutorial/configure-windows-netsh-firewall/

结论

如果能花时间实施 MongoDB,它将提供高度安全性。我希望本文能帮助减少实施时间。

历史

  • 2018 年 1 月 25 日:初始版本
© . All rights reserved.