强制 Node.js 使用系统 OpenSSL 和 LD_PRELOAD

问题描述 投票:0回答:1

我正在研究一个用例,我需要通过 OpenSSL 中的 SSL_CTX_load_verify_locations 函数注入自定义证书来覆盖 TLS 连接期间使用的证书。我已在 Python 应用程序 中成功实现了此解决方案,使用 LD_PRELOAD 在运行时覆盖 OpenSSL 函数。

但是,同样的方法不适用于 Node.js。经过一番研究,我发现 Node.js

在内部嵌入了 OpenSSL ,而不是依赖于动态链接的 OpenSSL 库。结果看来我的LD_PRELOAD技巧没有效果。我正在寻找一种方法来强制 Node.js 使用系统安装的 OpenSSL(共享库)而不是嵌入式的,而无需从源代码重建 Node.js。

以下是我的设置的详细信息以及我迄今为止所尝试的内容。

覆盖 SSL_CTX_load_verify_locations 的 C 代码

#define _GNU_SOURCE #include <dlfcn.h> #include <openssl/ssl.h> #include <stdio.h> // Function pointer to hold the original SSL_CTX_load_verify_locations function int (*original_SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath) = NULL; // Custom SSL_CTX_load_verify_locations function int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath) { // Load the original function if it hasn't been loaded yet if (!original_SSL_CTX_load_verify_locations) { original_SSL_CTX_load_verify_locations = (int (*)(SSL_CTX *, const char *, const char *)) dlsym(RTLD_NEXT, "SSL_CTX_load_verify_locations"); const char *error = dlerror(); if (error != NULL) { fprintf(stderr, "Error loading original SSL_CTX_load_verify_locations: %s\n", error); return -1; } } const char *my_CAfile = "/Users/gouravkumar/Desktop/Keploy/Lima-workspace/gk_workspace/tls-ebpf/sample-apps/keploy.crt"; printf("\n\nOverriding CAfile with %s\n\n", my_CAfile); // Call the original function with the overridden CAfile return original_SSL_CTX_load_verify_locations(ctx, my_CAfile, CApath); }
我如何编译代码

clang-14 -I/usr/include/openssl -L/usr/lib/aarch64-linux-gnu -fPIC -shared -o ssl_override.so ssl_override.c -lssl -lcrypto
Node.js 代码

const express = require('express'); const axios = require('axios'); const fs = require('fs'); const https = require('https'); const app = express(); const CERTIFICATE_PATH = '/Users/gouravkumar/Desktop/Keploy/Lima-workspace/gk_workspace/tls-ebpf/sample-apps/ninja.crt'; const httpsAgent = new https.Agent({ ca: fs.readFileSync(CERTIFICATE_PATH), keepAlive: false, }); app.get('/catFact', async (req, res) => { try { const response = await axios.get('https://catfact.ninja/fact', { httpsAgent }); res.status(200).json(response.data); } catch (error) { res.status(500).json({ error: 'SSL certificate validation failed', details: error.message }); } }); const PORT = 8080; app.listen(PORT, () => { console.log(`Server is running on http://0.0.0.0:${PORT}`); });
如何运行 Node.js 应用程序

LD_PRELOAD=/Users/gouravkumar/Desktop/Keploy/Lima-workspace/gk_workspace/tls-ebpf/tls-change-cert/ssl_override.so node index.js


我尝试过的

    LD_PRELOAD 与多个库: 我尝试在 LD_PRELOAD 中使用 OpenSSL 共享库和自定义覆盖库。
export LD_LIBRARY_PATH=/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH export LD_PRELOAD="/usr/lib/aarch64-linux-gnu/libssl.so.3 \ /usr/lib/aarch64-linux-gnu/libcrypto.so.3 \ /Users/gouravkumar/Desktop/Keploy/Lima-workspace/gk_workspace/tls-ebpf/tls-change-cert/ssl_override.so" node index.js

    使用 lsof 验证加载的库: 我确认我的覆盖库和系统的 OpenSSL 库都已加载到 Node.js 进程中。
➜ lsof -p $(pidof node) | grep ssl node 507762 gouravkumar mem REG 0,40 70040 /path/to/ssl_override.so node 507762 gouravkumar mem REG 254,1 737192 /usr/lib/aarch64-linux-gnu/libssl.so.3

    结果: 尽管确认库已加载,但重写的 SSL_CTX_load_verify_locations 函数中的 printf 语句并未被调用。这表明 Node.js 没有在运行时调用该函数,可能是因为它依赖于其嵌入式 OpenSSL 实现而不是动态链接版本。
我需要什么帮助

如何强制 Node.js 使用系统安装的 OpenSSL 共享库(如 libssl.so.3 和 libcrypto.so.3)而不是嵌入式版本?

  • 有没有办法确保 Node.js 动态加载系统的 OpenSSL 而无需从源代码重建它?

  • 是否有任何已知的解决方法或配置(例如设置特定的环境变量)来实现此目的?

在 Node.js 中处理版本:

➜ tls-support git:(main) ✗ node -p "process.versions" { node: '18.20.4', acorn: '8.11.3', ada: '2.7.8', ares: '1.28.1', base64: '0.5.2', brotli: '1.0.9', cjs_module_lexer: '1.2.2', cldr: '44.1', icu: '74.2', llhttp: '6.1.1', modules: '108', napi: '9', nghttp2: '1.61.0', nghttp3: '0.7.0', ngtcp2: '1.3.0', openssl: '3.0.13+quic', simdutf: '5.2.4', tz: '2024a', undici: '5.28.4', unicode: '15.1', uv: '1.44.2', uvwasi: '0.0.19', v8: '10.2.154.26-node.37', zlib: '1.3.0.1-motley' }
    
node.js ssl openssl ssl-certificate ld-preload
1个回答
0
投票
好吧,让我们把你的问题分成两个:

    使用系统安装的OpenSSL
  1. 覆盖证书
使用系统安装的OpenSSL

事实上,OpenSSL 是静态链接的,Node.js 在运行时不会使用系统安装的 OpenSSL 库。这种设计选择意味着您无法轻松覆盖或替换 Node.js 使用的 OpenSSL 版本或其配置,除非从源代码重建它,至少据我了解。

但是,如果您只需要覆盖证书,有一些方法可以做到:

覆盖证书

1 最简单的方法是利用杠杆

NODE_EXTRA_CA_CERTS

环境变量:

`NODE_EXTRA_CA_CERTS=/path/to/custom-ca.crt node your-app.js` This will just extend the list of trusted certificates you can use in your app.
2 对 tls 方法进行猴子修补:

const tls = require('tls'); const fs = require('fs'); // Read your custom CA certificate const customCa = fs.readFileSync('/path/to/custom-ca.crt'); // Store the original method const originalCreateSecureContext = tls.createSecureContext; // Override the method tls.createSecureContext = function (options = {}) { // Ensure 'ca' is an array and include the custom certificate options.ca = options.ca || []; if (!Array.isArray(options.ca)) { options.ca = [options.ca]; } options.ca.push(customCa); // Call the original method with modified options return originalCreateSecureContext.call(tls, options); };
3 以编程方式将其注入 https lib 

const https = require('https'); const fs = require('fs'); // Read your custom CA certificate const customCa = fs.readFileSync('/path/to/custom-ca.crt'); // Create an HTTPS agent that includes your custom CA const httpsAgent = new https.Agent({ ca: customCa, }); // Use the custom agent in your HTTPS request https.get('https://example.com', { agent: httpsAgent }, (res) => { // Handle the response res.on('data', (d) => { process.stdout.write(d); }); }).on('error', (e) => { console.error(e); });
    
© www.soinside.com 2019 - 2024. All rights reserved.