【问题标题】:Is there any way to programmatically generate a CouchDB cookie?有没有办法以编程方式生成 CouchDB cookie?
【发布时间】:2015-11-24 08:42:21
【问题描述】:

我正在开发一个应用程序,它将使用 CouchDB 为用户存储一些数据。但我不希望用户直接登录 CouchDB。

我将拥有一个应用客户端(移动/网络)、一个应用服务器和 CouchDB 服务器。客户端应用程序将向应用程序服务器进行身份验证,那么我的理想方案是让我的应用程序服务器以编程方式对用户进行身份验证,然后仅将 10 分钟的 cookie 发送到客户端应用程序。

也就是说,我希望应用服务器代表应用客户端的用户从 CouchDB 服务器请求一个 Cookie,然后只将 cookie 发送给应用客户端。

应用服务器可以代表经过身份验证的用户发布到 _session,但这需要:

  1. 在应用服务器中维护用户密码列表
  2. 为所有用户使用一个已知的单一密码
  3. 为每个身份验证请求将密码重置为随机值

出于安全原因,#3 似乎是最好的,但这似乎是额外的工作,并且是到数据库的额外往返行程(虽然不是很贵)。所以我的问题是:作为管理员,有没有什么方法可以代表用户生成 cookie,而不使用用户的密码?

这也可能允许我完全拒绝对 _session 的请求,除了来自我的应用服务器的请求,作为一项附加的安全措施。


为了完整起见,我还要提一下,我已经查看了这些其他选项,发现它们需要:

  1. Proxy Auth

    x_auth_token 永不过期的事实让我感到担忧。这意味着受损的令牌将永远授予对用户数据的访问权限。而且 AFAICT,如果不更改用户名或服务器密码(这实际上也会使其他所有人的身份验证令牌无效),甚至无法使令牌失效。但也许我在这里遗漏了什么?

  2. OAuth auth

    这似乎只是解决了问题。现在,我必须存储 OAuth 机密,而不是在我的服务器应用程序中存储用户的密码。另外,现在我的服务器和客户端代码一定更复杂。

【问题讨论】:

    标签: authentication couchdb


    【解决方案1】:

    扩展至natevw's brilliant answer。我遇到了类似的问题,如果没有偶然发现那个答案,我永远不会意识到选项 3 是可能的。

    这是我用于生成 cookie 的 python3 实现(使用 pycouchdb 与沙发交互):

    def generate_couchdb_cookie(couchAddress, couchSecret, username):
    
        timestamp = format(int(time.time()), 'X')
        data      = username + ":" + timestamp
    
        server    = pycouchdb.Server(couchAddress)
        db        = server.database("_users")
        doc       = db.get("org.couchdb.user:" + username)
        salt      = doc["salt"]
        secret    = couchSecret + salt
    
        hashed    = hmac.new(secret.encode(), data.encode(), hashlib.sha1).digest()
        inbytes   = data.encode() + ":".encode() + hashed
        result    = base64.urlsafe_b64encode(inbytes)
    
        return "AuthSession=" + (result.decode("utf-8")).rstrip('=')
    

    【讨论】:

      【解决方案2】:

      我没有遵循您的确切目标。您似乎暗示用户可能有密码(“应用程序服务器以编程方式对用户进行身份验证”),但您不希望用户“永远需要知道他们的 CouchDB 密码”。你想要什么样的身份验证?

      我采用了两种(半)通用方法来使用 CouchDB 进行身份验证:

      1. “Man-in-the-middle[ware]”方法,我在 CouchDB 前面有一个瘦中间件。该中间件将用户名/密码转发到“/_session”,该“/_session”会根据 CouchDB _users 数据库生成 cookie 或错误代码。中间件将这个 cookie 从 CouchDB 复制到它自己的 HTTP 响应中返回给客户端(或在出错的情况下显示一条消息)。然后在需要访问数据库的后续请求中,它会将 cookie(现在来自客户端请求)再次转发回数据库。

      2. 传统方法,您只需使用 CouchDB 作为数据存储并维护您自己的“用户”条目/索引。确保您使用当前的密码存储/处理最佳实践或使用为您处理这些详细信息的库。中间件作为“自身”连接到数据库,并根据自己的会话处理以自己的逻辑处理读/写权限。

      3. 或者——一种混合方法——您可以仅使用“/_session”API 来查看 CouchDB 是否接受用户名+密码为有效。如果是,请为该用户创建一个单独的中间件处理的会话。 (基本上,您只使用 CouchDB 的 _user 数据库作为“密码处理库”,其余的是传统方法,其中访问控制全部在中间件而不是数据库中实现。)

      对于现实世界的生产资料,我倾向于只使用后两种(或者考虑到前面的编号……)——第一种方法很有趣,但是 CouchDB 缺乏文档——级别读取权限通常意味着让用户几乎直接访问数据库服务器在实践中是站不住脚的。


      更新:您的问题现在清楚表明您希望客户端应用程序直接与两个服务器对话:应用程序(以前称为“中间件”)服务器和 CouchDB(数据库)服务器。我将保留上面的内容,因为我认为它仍然有些用处,并为本次更新提供了一些背景/上下文。

      您怀疑Proxy Authentication 是错误的解决方案是正确的:它不是旨在供最终用户使用,而是真正替换#1 的cookie 转发“技巧”部分更多。也就是说,代理身份验证是当您完全信任一个方(即您的中间软件)在它代表工作时提供用户信息一个用户。但是您希望用户直接与数据库对话,而您不能通过X-Auth-CouchDB-Token 信任他们。

      我会听从您对 OAuth 选项的判断。我确实认为它更接近您想要的,但很明显,您以某种方式针对不同的服务对用户进行身份验证,并且不需要存储 per-user keys in CouchDB 本身。 OAuth 1.0 要求的请求签名确实意味着您的客户端应用程序的 HTTP 库也需要支持。

      我看到了一些选项,无需构建自定义 CouchDB 插件,即可让您的 app 服务器将令牌分发给您的 database 服务器将接受的经过身份验证的用户:

      1. 毕竟是代理!也就是说,将您的数据库服务器隐藏在您的应用服务器或另一个轻量级自定义反向代理后面。所有这些中间件需要做的是检查您现有的客户端应用程序会话(cookie 或其他身份验证标头),如果它有效,则设置 CouchDB 将接受的内部proxy auth headers - 然后它会逐字转发其余的请求/响应。

      2. 确定性密码,每个用户,如果它让你感觉更好的话。使用只有它知道的秘密配置您的应用服务器,然后将每个用户密码设置为类似HMAC(username, app_server_secret)。现在,当您想为用户生成令牌时,您的应用服务器可以为每个用户生成密码。请注意,这实际上并不比仅使用app_server_secret 作为每个用户的密码更安全——CouchDB 已经独立地对每个用户密码进行加盐和哈希处理,因此如果有人获得了数据库但没有获得您的应用程序的配置值,那么攻击者分不清两人。在这两种情况下,防止未经授权的数据库使用完全取决于对 app_server_secret 保密。

      3. 重新实现 CouchDB 当前的 cookie 生成算法。 CouchDB 的 cookie 算法(view source)基本上是data = username + ':' + timestamp; base64(data + ':' + sha_mac(data, secret))。其中secret 是couch_httpd_auth.secret 值加上用户的salt 值。您可以告诉您的应用服务器 couchdb_httpd_auth/secret 值,它可以按照相同的步骤生成您提供给客户端应用程序的有效 cookie,CouchDB 将接受它作为自己的。此 cookie 将在时间戳 + 配置的 couch_httpd_auth/timeout 之前有效。尽管看起来很“hacky”,但这可能是最接近您要求的内容,尽管您仍然需要以某种方式设置/禁用用户的实际密码。

      【讨论】:

      • 感谢您的回答。我很抱歉问了一个不清楚的问题。我已经编辑它希望更清楚。我基本上希望按照您在#1 中的建议进行操作,但希望有一种方法可以完全避免使用用户的密码。也就是说,让经过身份验证的管理员用户在不知道密码的情况下生成用户的 cookie,这样就根本不需要知道密码。
      • #3 确实看起来最接近我正在寻找的东西。非常感谢您的深入回答,并涵盖了解决问题的所有各个角度。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-12
      • 2010-09-18
      • 2014-10-12
      • 2013-04-02
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多