如何在Firebase云函数中获取客户端IP地址?

38

在使用Firebase云函数将数据保存到Firebase数据库时,我希望同时写入发出请求的IP地址。

然而,req.connection.remoteAddress总是返回::ffff:0.0.0.0。是否有一种方法可以获取实际发起请求的客户端的IP地址?


1
看起来你不能再依赖于x-forwarded-for了。尝试读取fastly-client-ip。其他IP地址似乎是Google内部IP。 - jqualls
1
四年过去了,Google仍然没有发布任何官方文档。这太疯狂了。 - Broda Noel
10个回答

28

客户端的IP地址是request.ip

示例:

export const pay = functions.https.onRequest((request, response) => {
  console.log(`My IP is ${request.ip}`);
});

23
在使用可调用函数(functions.https.onCall((data, context) => {...}))时,context.rawRequest.ip表示请求的IP地址。 - sceee
1
考虑到它是未记录的并且可能会被更改,使用它是否安全? - eozzy
1
我看到context.rawRequest.ip出现了undefined - 这个已经被弃用了吗? - Stefan
1
@Stefan,因为我可能是唯一被通知的人,所以我想测试一下我的示例。这个示例仍然有效。我还没有查看onCall方法,但由于request.ip仍然有效,所以我认为仍然有一种方法可以访问IP。即使在Firebase中弃用了IP地址,我仍然认为ip会返回一个值。 - Luke
3
谢谢Luke,好的我不对(但是也许是吗?) - 我正在使用模拟器,它应该模拟一些东西。结果发现它仅包含了一些预期属性中的几个。对于未来的读者,请不要在本地测试。如果您有一个 https.onCall,那么请使用 functions.logger.log(context.rawRequest.headers); 查看商品。 - Stefan
显示剩余2条评论

20

我将IP地址视为双精度浮点数。获取###.105.156.223,###.105.156.223。 - jwilleke
在这个Gist https://gist.github.com/katowulf/6fffffb45ee5cbfbca6c3511e5d19528 中,他们使用了类似的方法。然而,有一个回复指出这种方法不可靠 - 你发现这种方法可靠吗? - user3836415
这种方法似乎不再起作用了,因为它在每个请求中都会给出一个新的IP。 - imrok
1
这是有效的,提取第一个IP地址。 - Ujjwal Singh
@UjjwalSingh 提取第一个IP 可以 工作,但是它是不可信的。例如,请求者本身可能(恶意地?)在发送的请求中添加 X-Forwarded-For,并且使用这种方法,他们可以告诉你任何他们想要的IP,而不是真实的IP。 - Brett

19

如果您想通过Firebase Hosting查找客户端IP,则应使用标题fastly-client-ip,其中将包含真实的客户端IP。


3
我本以为这是垃圾邮件,但他们把x-forwarded-for改成了fastly-client-ip。感谢您发布此更新。 - Robin

3
这对我有用:

这适用于我:

const express = require('express');

const logIP = async (req : any, res : any, next : any) => {
    const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.headers['fastly-client-ip'];
}

const logIPApp = express();
logIPApp.use(logIP);

exports.reportIP = functions.https.onRequest(logIPApp);

1
为什么logIP常量只是在其作用域内创建一个新的常量,而没有对其进行任何操作?抱歉,我有什么遗漏吗? - Sylvester Das

1

总结

  • 通常情况下,你可以在X-Forwarded-For头部的第一个条目中找到真实的客户端IP,但你不能信任它。

  • 当使用Cloud Run(或Cloud Functions gen 2)时,你只能信任最后一个条目(由Cloud Run orchestrator自动添加)。

  • 此外,如果你正在代理Firebase Hosting,我无法找到任何可信任的IP地址的方法。

如果你需要信任IP地址

正如这个回答https://dev59.com/SFYN5IYBdhLWcg3wEEcm#48032910所解释的那样,你可以通过从X-Forwarded-For头部中取出第一个地址来获取客户端的IP地址。这是一个逗号分隔的地址列表,每个代理都会追加它看到的客户端地址。

然而,请求者可以(恶意地)自行添加X-Forwarded-For头部,并且每个代理都会追加它。通过这种方式,请求者可以告诉你任何他们想要的IP地址。

如果您正在使用Cloud Run(或Cloud Functions gen 2),那么据我所知,Cloud Run orchestrator始终作为容器的代理。这意味着您可以信任X-Forwarded-For中的最后一个条目,因为这是Cloud Run本身看到的客户端IP。最后一个地址是您唯一可以信任的地址。列表中的任何前面的地址都可能由请求者或沿途的任何不受信任的代理提供。

如果您正在使用Firebase Hosting

如果您将Cloud Run容器(或Cloud Function)代理到Firebase托管后面,则列表中的最后一个地址是Fastly CDN代理,而不是原始客户端地址。您可能会尝试使用倒数第二个地址(或Fastly-Client-IP标头),但您不能信任此信息。

如果请求者了解您的Cloud Run容器的直接地址(例如<your-service>.a.run.app),则他们可以直接发出请求,并伪造其中任何一个标头。

您可能会尝试通过检查列表中的最后一个IP地址是信任服务器(似乎Fastly CDN对Cloud Run的请求使用了Google的IP范围之一)来验证请求是否确实来自Fastly/Firebase Hosting CDN,但这并不能排除请求可能来自托管在Fastly或Google Cloud服务上的恶意客户端

这意味着您不能这样做。您只能信任最后一个地址,因此无法通过Firebase Hosting进行代理。除非GCP/Firebase Hosting团队未来提供一些“可信标头”。


0

以下是我在使用 Firebase Cloud Functions 时成功的方法:

const clientIP = req.headers['x-appengine-user-ip'] || req.header['x-forwarded-for']

请注意,这在本地无法工作!


0

之前的答案很有帮助,特别是Brett的长篇解释我非常感激。

如果你在寻找如何在2代onCall云函数中获取客户端IP地址时遇到了这个线程,之前的答案从概念上解释了,但没有给出一个真实的例子。确切的方法是:

exports.exampleFunction = onCall({cors: true}, (request) => {
  var index = request.rawRequest.rawHeaders.indexOf('x-forwarded-for');
  console.log(request.rawRequest.rawHeaders[index+1]);
});

在第二代 onCall 函数中,request.rawRequest.rawHeaders 是一个数组。你需要查找数组中的 x-forwarded-for 值,然后 IP 地址就是该值之后的下一个元素。
分享这个信息是为了帮助其他人。
这种方法似乎有些笨拙,如果有更好的方法来处理第二代函数,我很愿意知道。

0
为了在Firebase云函数中获取客户端IP地址,我创建了以下代码:
function getIPFromRequest(request: any) {
    return (
          request.ip ||
          request.headers['fastly-client-ip'] ||
          request.headers['x-forwarded-for']?.split(',')[0].trim() ||
          request.connection.remoteAddress ||
          request.socket.remoteAddress ||
          ''
    );
}

在某些Node.js框架或中间件(如Express.js)中,您可以使用request.ip来访问客户端的IP地址。原则上,request.ip实际上是通过获取头部信息来设置的。但是假设request.ip未设置,我们将自己获取头部信息。简单来说,这就是getIPFromRequest的作用。

0
Firebase / GCloud Functions所记录和支持的唯一方法是使用'X-Forwarded-For'头部:
参考: 截至2023年8月,我现在使用的代码是...
const header = ctx.rawRequest.header.bind(ctx.rawRequest)
const ip = (header('x-forwarded-for') || '').split(',')[0]
const ipCountry = header('x-appengine-country') || ''

请注意,x-appengine-user-ip不再起作用,因为它返回的IP地址不是客户端的IP地址(看起来像是服务器或代理的IP地址)。

-1
如果您正在云可调用函数中查找IP地址或标头,则可以在上下文对象内获取信息。
例如:
    exports.testUser = async (data, context) => {
      console.log('------------------------::context::------------------');
      if(context.rawRequest && context.rawRequest.headers){
         console.log(context.rawRequest.headers);
      }
    }

在头部中,您可以获取IP地址:header {'x-appengine-user-ip':'xxip'}等。

我已经在我的auth函数中尝试过了,但是头部信息中没有IP信息。exports.auth = functions.https.onCall(async ({ roomid, userid }, context => {... - Stefan
你尝试过使用 context.rawRequest.headers['x-appengine-user-ip'] 这种方式吗? - Bhargav Suthar
谢谢,这给了我一个未定义的值,但我刚刚意识到我正在使用本地模拟器,所以这很可能在那里不起作用。然而,如果存在IP,则先前的方法仍应该给出一个IP...我将尝试在真正的Firebase分阶段应用程序中尝试这个方法。 - Stefan
是的,如果不在本地运行,它就可以工作...那个模拟器,现在就把我射了吧 :-) - Stefan
@Stefan - 如果使用 Firebase Authentication with Identity Platform (https://firebase.google.com/docs/auth#identity-platform),则可以设置 Auth 函数以包含 IP 信息在 'EventContext' 对象中。有关如何设置和访问 IP 信息的具体示例,请参见 https://firebase.google.com/docs/auth/extend-with-blocking-functions 中的“获取用户和上下文信息”。 - lemonshark12

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接