【问题标题】:Kerberos authentication in Node.js https.get or https.requestNode.js https.get 或 https.request 中的 Kerberos 身份验证
【发布时间】:2013-12-04 14:54:19
【问题描述】:

我正在尝试编写一个简单的脚本,该脚本从内部网络上的工具请求一些数据。代码如下:

#!/usr/bin/node

var https = require('https');
var fs = require('fs');

var options = {
  host: '<link>',
  port: 443,
  path: '<path>',
  auth: 'username:password',
  ca: [fs.readFileSync('../.cert/newca.crt')]
};

https.get(options, function(res) {
  console.log("Got response: " + res.statusCode);
  res.on('data', function (d) {
    console.log('BODY: ' + d);
  });
}).on('error', function(e) {
  console.log("Got error: " + e.message);
});

现在的问题是,如何使用 Kerberos 票证进行身份验证,而不是在 auth: 中以纯文本形式提供我的凭据?

【问题讨论】:

    标签: javascript node.js https kerberos


    【解决方案1】:

    来自http://docs.oracle.com/cd/E24191_01/common/tutorials/authn_kerberos_service.html

    消息级标准的客户端令牌位置:Kerberos 服务票证可以在 Authorization HTTP 标头中发送,也可以在 在消息本身内部,例如,在一个 元素。或者,它可能包含在消息中 属性。选择以下选项之一:

    所以您提供的是票证而不是您的用户名:密码

    或者,您可以如下所述将信息放在消息正文中或作为消息属性

    var request = https.request(options, function(resource) {
      var chunks = [];
       resource.on('data', function (chunk) {
         chunks.push(chunk);
       });
       resource.on('end', function () {
         var data = chunks.join('');
         console.log(data);
       });
    }
    
    request.on('error',...)
    request.send('<body-with-ticket>');
    request.end();
    

    编辑:

    “”部分是我在哪里使用票证的示例,将其放在多类型正文中并发送,或者使用 WWW-Authenticate 标头发送它

    例如。将其添加到 https.request 的选项中

    options = {
        host: 'hostname',
        port: 443, 
        'WWW-Authenticate': 'Negotiate ' + ticketdata
    };
    

    google 有一些关于其工作原理的漂亮图表: https://developers.google.com/search-appliance/kb/secure/kerberos-diagram

    【讨论】:

    • 感谢您的解决方案,即使我知道我们可以设置 HTTP 请求的自定义标头并发送 kerberos 凭据(准确地说,我们需要将 'WWW-Authenticate' 设置为 kerberos 票证。但是 问题是从KRB5CCNAME文件(kerberos票据)中获取内容,这个文件内容是加密的。
    • 基本上我需要实用程序来读取 KRB5CCNAME 文件并设置身份验证头。我会自己编写这个实用程序,但对 Kerberos 知之甚少,如果您能给我一些启发,我将不胜感激!
    • “您提供票证”是什么意思。我是否只是指向像auth: 'ticket', 这样的票证文件。可以发一下代码吗?
    • 我今天一定很慢,抱歉; ticketdata 是什么?我从 Google 链接中了解到,它是 Kerberos 票证在哈希中的表示(正确吗?),但我从哪里得到它?
    【解决方案2】:

    在 Paul Scheltema 的回答中,您需要从操作系统的深度获取票证数据。您(或代表您的模块)必须使用 GSS-API 让 Active Directory 为您生成票证数据。

    这种机制在 Chrome 中是有的,但似乎 Node.js 中没有(只有 Chrome 的 javascript 引擎),所以你可能需要添加一个模块,例如:

    要安装/编译此类模块,您可能需要 Visual Studio。


    要设置环境, - 在所有计算机上,您必须在端口 88 (Kerberos) 和 53 (dns) 上启用 tcp 和 udp。 - 必须在 Windows Server 上运行 Active Directory(ldap、dns、kdc) - 在页面 https://www.npmjs.org/package/passport-kerberos 他们使用术语 REALM。这是一个域名,大写

    【讨论】:

    • 我知道这可以用一个像 passport-kerberos 这样的模块来完成,但是那个模块是怎么做到的呢?我不能绕过模块并简单地实现相同的逻辑,保持简单的脚本没有任何依赖关系吗?
    • 逻辑:使用GSS-API/SSPI函数形成对域控制器的请求。此请求必须包含用户身份验证信息(从 Kerberos 缓存中获取,受操作系统高度保护),以及转换为 userPrincipal 表单(使用 dns)的 URL。如果 Active Directory 在 LDAP 中有这样的 userPrincipal 并且身份验证数据正确,则会生成 Kerberos 票证。这张票应该被包装到 SPNEGO 中,Base64 编码,这是 Paul Scheltema 回答中的“票证数据”。通常这是由网络浏览器为我们完成的。当它不存在时,在 node.js 中,需要一个模块。
    • 好的,我想真的没有办法快速简单地做到这一点,并且必须有一些模块依赖项。感谢您的回答!
    • 几个月前研究过 Kerberos,肯定会有一些模块依赖,除非你想深入研究 passport-kerberos 的源代码(甚至是 Chromium 源代码)并重新实现它.我可能会在某个时候自己做这件事(假设这个特定的项目上升到最顶端),因为我在 npm 中看到的唯一 Kerberos 模块只允许您根据活动目录验证凭据(基本上,允许您使用 Kerberos在服务器上下文中),它们并没有让您实际与远程服务器作为客户端进行协商
    【解决方案3】:

    我已经使用“kerberos”模块,版本 0.0.12 使它工作。我用工作示例创建了一个要点:

    https://gist.github.com/dmansfield/c75817dcacc2393da0a7

    基本上,您使用三种方法来获取“Authorization”标头数据:

    • authGSSClientInit,需要服务名称,例如HTTP@somehost.com
    • authGSSClientStep,它需要存在凭据缓存(在 Linux 上,您可以通过执行“kinit”来获得此缓存,并且可以使用“klist”进行验证),并且实际上会返回您需要的 base64 内容(没有前导的“Negotiate” " 字符串)
    • authGSSClientClean,释放所有分配的内存结构

    然后您创建一个“授权”标头(不是上面显示的 WWW-Authenticate,这是服务器发回的内容),它应该可以工作。

    另请注意:通常,Web 浏览器请求资源,在响应中返回带有 WWW-Authenticate: Negotiate 标头的 401,然后使用“授权”标头中提供的票证数据重新请求资源。每个资源都会发生这种两步舞。我不确定这是否意味着什么。

    【讨论】:

      【解决方案4】:

      如果您使用的是 Windows,则可以使用 SSPI 接口。它以项目node-expose-sspi 暴露在Node 上。

      SSPI 接口允许您使用 SSO(NTLM 和 Kerberos)编写任何客户端或服务器。

      https://github.com/jlguenego/node-expose-sspi

      注意:我是 node-expose-sspi 的作者。

      【讨论】:

        【解决方案5】:

        2020 UPD:

        较新版本的kerberos npm 包的方法要少得多。但我让它工作:

        const kerberos = require('kerberos').Kerberos;
        const fetch = require('node-fetch');
        
        (async () => {
            const client = await kerberos.initializeClient("HTTP@site.internal.net", {
                mechOID: kerberos.GSS_MECH_OID_SPNEGO,
            })
        
            const ticket = await client.step("")
        
            const resp = await fetch("https://site.internal.net/api/v1/hello", {
                headers: {
                    'Authorization': 'Negotiate ' + ticket
                }
            })
        
            console.log(await resp.json())
        })();
        

        在 Windows 和 Linux 上完美运行,应该也可以在 macOS 上运行。

        【讨论】:

        • idk 为什么你被否决了,当我将 mechOID 更改为 GSS_MECH_OID_KRB5 时,这对我来说非常有效。我使用的是 macOS Catalina 10.15.7 (19H15)
        • 聚会迟到了,但这里有一个问题。在传递参数时我们如何传递密码?我正在尝试在休息环境中进行这项工作。对于令牌调度的工作方式非常困惑。一些建议或任何注释?
        • 这是 SSO。操作系统认为您已获得授权,因此它仅在系统调用后提供 SPNEGO 令牌(此库执行此操作)。
        【解决方案6】:

        在 Windows 上。这个answer 似乎是正确的,只是可能需要一些调整。

        const client = await kerberos.initializeClient("HTTP/site.internal.net@DOMAIN.HERE", {
            mechOID: kerberos.GSS_MECH_OID_SPNEGO,
        })
        

        适用于 GSS_MECH_OID_KRB5 和 GSS_MECH_OID_SPNEGO。 从命令行使用 klist,您将获得现有 kerberos 票证、其站点(例如 site.internal.net)和域(例如 DOMAIN.HERE)的概览。

        kerberos 流程是您首先获得带有 WWW-Authenticate: Negotiate 标头的 401。然后,您应该使用 'Authorization': 'Negotiate ' + ticket 向以 401 响应的 url(可以是相同的 url,可以是重定向 url)发出第二个请求,如示例所示。不要忘记重用第一次请求中的任何 Cookies 来维护会话。

        完整的请求可能如下所示:

        const kerberos = require('kerberos').Kerberos;
        const fetch = require('node-fetch');
        
        (async () => {
            const client = await kerberos.initializeClient("HTTP/site.internal.net@DOMAIN.HERE", {
                mechOID: kerberos.GSS_MECH_OID_SPNEGO, // or GSS_MECH_OID_KRB5
            })
        
            const ticket = await client.step("")
        
            // 401 here, get cookies
            const resp1 = await fetch("https://site.internal.net/api/v1/hello")
        
            // ... code to handle cookies ...
        
            const resp2 = await fetch("https://site.internal.net/api/v1/hello", {
                headers: {
                    'Authorization': 'Negotiate ' + ticket,
                    'Cookie': // ... add cookies from first request ..
                }
            })
        
            console.log(await resp2.json())
        })();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-06-22
          • 1970-01-01
          • 1970-01-01
          • 2010-09-28
          • 2017-03-17
          • 2022-06-13
          • 1970-01-01
          相关资源
          最近更新 更多