Express.js:リモートクライアントアドレスを取得する方法


256

リモートユーザーのIPアドレスを取得する方法が完全にわかりません。

次のような単純なリクエストルートがあるとします。

app.get(/, function (req, res){
   var forwardedIpsStr = req.header('x-forwarded-for');
   var IP = '';

   if (forwardedIpsStr) {
      IP = forwardedIps = forwardedIpsStr.split(',')[0];  
   }
});

上記のアプローチは、実際のユーザーIPアドレスを取得するために正しいですか、それとももっと良い方法がありますか?そして、プロキシはどうですか?


1
どのように使用方法についてのノード-ipwareを説明どおりにここに
un33k 2014年

「example.com」のようなreq.hostnameを取得できない場合:stackoverflow.com/a/37824880/5333284
zhi.yang

回答:


469

NGiNXなどのプロキシの背後で実行している場合は、「x-forwarded-for」を確認する必要があります。

var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

プロキシが「あなたのもの」でない場合、「x-forwarded-for」ヘッダーは偽装される可能性があるため、信頼できません。


2
これは正しいですが、私の状況では角括弧を使用する必要がありました(上記の編集を参照)。また、nginx構成でx-forwarded-forが有効になっていることを確認してください。魅力的な作品!
2003

43
proxy_set_header X-Forwarded-For $remote_addr;独自のリバースプロキシを使用している場合は、このディレクティブをnginx構成に含める必要があることに注意してください。
Coxer、

ブラウザでIPを使用する場合は、http(s):// [ipv6]:port []が必要であることに注意してください。これが役に立てば幸いです
2016年

43
copy-pastaユーザーは注意してください:これはIPアドレスのコンマ区切りリストを返す可能性があります。これをコピーして結果をIPと比較する開発者からのバグがありました。おそらくvar ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress || '').split(',')[0].trim();、クライアントIPを取得するようなことを行います。
Davy Jones

3
@Rishav :: 1はlocalhostのIPv6アドレスです
Daniel O

238

@alessioalexからの回答は機能しますがExpress-guideの「プロキシの背後にあるExpress」セクションで述べられている別の方法があります

  1. app.set('trust proxy', true)エクスプレス初期化コードに追加します。
  2. リモートクライアントのIPを取得するreq.ip場合req.ipsは、またはを使用して(リバースプロキシがないかのように)

オプションの読み取り:

  • req.ipまたはを使用しreq.ipsます。req.connection.remoteAddressこのソリューションでは機能しません。
  • ヘッダーで'trust proxy'渡されたすべてを信頼するよりも高度なものが必要な場合x-forwarded-for(たとえば、プロキシが既存のx-forwarded-forヘッダーを信頼できないソースから削除しない場合)には、より多くのオプションを使用できます。詳細については、リンクされたガイドを参照してください。
  • プロキシサーバーにx-forwarded-forヘッダーが設定されていない場合、2つの可能性があります。
    1. プロキシサーバーは、要求が元々あった場所に関する情報を中継しません。この場合、リクエストの元の場所を見つける方法はありません。最初にプロキシサーバーの構成を変更する必要があります。
      • たとえば、nginxをリバースプロキシとして使用する場合proxy_set_header X-Forwarded-For $remote_addr;、構成に追加する必要がある場合があります。
    2. プロキシサーバーは、要求が元々どこから来たかに関する情報を独自の方法で中継します(たとえば、カスタムhttpヘッダー)。そのような場合、この回答は機能しません。その情報を取得するカスタムの方法があるかもしれませんが、最初にメカニズムを理解する必要があります。

4
私の答えはより一般的でした(Expressに関連付けられていません)が、Expressを使用している場合は、それが実際により良い方法です。
alessioalex 2013年

4
プロキシサーバーのヘッダー 'x-forwarded-for'をリモートアドレスに設定する必要があります。たとえばnginxの場合、設定ファイルにproxy_set_header X-Forwarded-For $ remote_addrが含まれている必要があります
Kamagatos

1
IISnodeをプロキシとしてapp.enable('trust proxy')使用するIISの背後では、を使用することもできますreq.ip。私はそれでポートを取得することを除いて1.2.3.4:56789。それを取り除くために、私はそうしますvar ip = req.ip.split(':')[0]
Christiaan Westerbeek 14

このソリューションは安全ですか?
Daniel Kmak

3
この回答にはセキュリティが不足しており、更新が必要だと思います。アプリケーションが信頼するプロキシは常に定義する必要があります。受け入れられた回答には、少なくともなりすましに関する注意が少しあります。とは言っても、Expressを使用している場合は、このようなライブラリを使用する方が適切なソリューションですが、引用符で囲まれたコードは正しくなく、リンクされたリソースで見つかりません。
Phil

54

nginx.confファイル:
proxy_set_header X-Real-IP $remote_addr;

ではnode.js、サーバーのファイル:
var ip = req.headers['x-real-ip'] || req.connection.remoteAddress;

小文字のヘッダーを表すことに注意してください


3
Stack Overflowへようこそ!コードのブロックだけを投稿するのではなく、このコードがもたらす問題を解決する理由を説明してください。説明がなければ、これは答えではありません。
Artemix 2013

1
@ququzoneの答えは問題ありません。説明には、訪問者から元のIPアドレスを取得する「x-real-ip」という名前のリクエストにカスタムヘッダーが設定されています。nodeとsocket.ioで動作します。
coffekid 2013年

8
私にとって、IPアドレスはreq.headers['x-real-ip']nginx.confヘッダーが大文字で設定されている場合でも使用できます。
Nik Sumeiko 2014

これは私のためにそれを解決したものです。trust-proxyをtrueに設定しても、ローカルの127.0.0.1アドレスを使用していました
David

30

特にノードの場合、httpサーバーコンポーネントのドキュメントでは、イベント接続の次のように述べています。

[トリガー]新しいTCPストリームが確立されたとき。[the]ソケットは、net.Socketタイプのオブジェクトです。通常、ユーザーはこのイベントにアクセスしたくないでしょう。特に、プロトコルパーサーがソケットに接続する方法が原因で、ソケットは読み取り可能なイベントを発行しません。ソケットはからもアクセスできますrequest.connection

つまり、これrequest.connectionはソケットであり、ドキュメントによれば、実際にはsocket.remoteAddress属性があり、ドキュメントによれば次のとおりです。

リモートIPアドレスの文字列表現。たとえば、「74.125.127.100」または「2001:4860:a005 :: 68」です。

Expressでは、リクエストオブジェクトはNode httpリクエストオブジェクトのインスタンスでもあるため、このアプローチは引き続き機能します。

ただし、Express.jsでは、リクエストにはすでに2つの属性(req.ipreq.ips)があります。

req.ip

リモートアドレスを返すか、「信頼プロキシ」が有効になっている場合-アップストリームアドレス。

req.ips

ときに「信頼プロキシは」であるtrue、そうでない場合は空の配列が返され、「X-転送さ-については、」IPアドレスリストを解析し、配列を返します。たとえば、値が "client、proxy1、proxy2"の場合、配列["client"、 "proxy1"、 "proxy2"]が返されます。ここで、「proxy2」は最も下流にあります。

私の理解によれば、Express req.ipは実際のクライアントIPが含まれているreq.connection.remoteAddressためreq.ip(信頼できるプロキシがExpressで有効になっている場合)、の方がプロキシのIPアドレスを含む可能性があるため(ある場合) 1)。

これが、現在受け入れられている回答が示唆している理由です。

var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

これreq.headers['x-forwarded-for']は、expressに相当しreq.ipます。



7

これは、この回答の単なる追加情報です。

を使用している場合は、サイトのロケーションブロックにnginx追加proxy_set_header X-Real-IP $remote_addr;します。/etc/nginx/sites-available/www.example.com例えば。サーバーブロックの例を次に示します。

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    location / {
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_pass http://127.0.1.1:3080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

を再起動nginxすると、node/ expressアプリケーションルートのIPにアクセスできるようになりますreq.headers['x-real-ip'] || req.connection.remoteAddress;


3

プロキシの背後にあるExpressによると、適切にreq.ip構成trust proxyされている場合、リバースプロキシが考慮されています。したがってreq.connection.remoteAddress、ネットワーク層から取得され、プロキシを認識しない場合よりも優れています。


3

そのためのパッケージを書きました。エクスプレスミドルウェアとして使用できます。私のパッケージはここに公開されています:https : //www.npmjs.com/package/express-ip

を使用してモジュールをインストールできます

npm i express-ip

使用法

const express = require('express');
const app = express();
const expressip = require('express-ip');
app.use(expressip().getIpInfoMiddleware);

app.get('/', function (req, res) {
    console.log(req.ipInfo);
});

1
自分が提携しているものにリンクする場合は、その所属を投稿で開示する必要あります。アフィリエーションを開示しない場合、スパムと見なされます。開示は明示的である必要がありますが、正式である必要はありません(たとえば、自分の個人的なコンテンツの場合:「自分のサイトで...」、「自分のブログで...」など)。参照:「良い」自己宣伝を意味するものは何ですか?自己宣伝に関するいくつかのヒントやアドバイススタックオーバーフローのための「スパム」の正確な定義は何ですか?、そして何かがスパムになるもの
マキエン

2

私はこの質問に答えたことを知っていますが、これが私の仕事の仕方です。

let ip = req.connection.remoteAddress.split(`:`).pop();

2

サードパーティのライブラリを使用している場合。あなたはrequest-ipをチェックすることができますます。

使用できます

import requestIp from 'request-ip';

app.use(requestIp.mw())

app.use((req, res) => {
  const ip = req.clientIp;
});

ソースコードはかなり長いので、ここではコピーしません。 。https://github.com/pbojinov/request-ip/blob/master/src/index.jsでます。

基本的に、

リクエストで特定のヘッダーを探し、存在しない場合はいくつかのデフォルトにフォールバックします。

ユーザーipは、次の順序で決定されます。

  1. X-Client-IP
  2. X-Forwarded-For (ヘッダーは、「クライアントIP、プロキシ1 IP、プロキシ2 IP」の形式で複数のIPアドレスを返す可能性があるため、最初のものを使用します。)
  3. CF-Connecting-IP (クラウドフレア)
  4. Fastly-Client-Ip (クラウド機能に適応した場合のCDNとFirebaseホスティングヘッダー)
  5. True-Client-Ip (アカマイとCloudflare)
  6. X-Real-IP (Nginxプロキシ/ FastCGI)
  7. X-Cluster-Client-IP (ラックスペースLB、リバーベッドスティングレイ)
  8. X-ForwardedForwarded-ForおよびForwarded(#2のバリエーション)
  9. req.connection.remoteAddress
  10. req.socket.remoteAddress
  11. req.connection.socket.remoteAddress
  12. req.info.remoteAddress

IPアドレスが見つからない場合は、が返されnullます。

開示:私は図書館に関係していません。


1

これは私にとって他のものよりもうまくいきました。私のサイトはCloudFlareの背後にあり、それが必要なようですcf-connecting-ip

req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress

このヘッダーについて何も言わなかったため、プロキシの背後でExpressをテストしませんでしたcf-connecting-ip


1

could-flare、nginx、x-real-ipのサポート

var user_ip;

    if(req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
      let first = req.headers['cf-connecting-ip'].split(', ');
      user_ip = first[0];
    } else {
      let user_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
    }

1

私の場合、このソリューションと同様に、次のx-forwarded-forアプローチを使用しました。

let ip = (req.headers['x-forwarded-for'] || '').split(',')[0];

x-forwarded-forヘッダーは、起点から最終宛先サーバーまでIPのルートを追加し続けるため、起点クライアントのIPを取得する必要がある場合、これは配列の最初のアイテムになります。


0

var ip = req.connection.remoteAddress;

ip = ip.split( ':')[3];


出力は次のようになります:-:: ffff:XXX.XX.XX.XXこれからIPを取得します
Bhulawat Ajay

3
ip = ip.split(':').pop();この場合、通常のIP、つまり127.0.0.1が来れば打者になると思います。それでも、IPを提供することができます。
2018年


0

witk @kakopappaソリューションとmorganクライアントIPアドレスのロギングをすべてまとめます。

morgan.token('client_ip', function getId(req) {
    return req.client_ip
});
const LOG_OUT = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" :client_ip'
self.app.use(morgan(LOG_OUT, {
    skip: function(req, res) { // custom logging: filter status codes
        return res.statusCode < self._options.logging.statusCode;
    }
}));

// could-flare, nginx and x-real-ip support
var getIpInfoMiddleware = function(req, res, next) {
    var client_ip;
    if (req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
        var first = req.headers['cf-connecting-ip'].split(', ');
        client_ip = first[0];
    } else {
        client_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
    }
    req.client_ip = client_ip;
    next();
};
self.app.use(getIpInfoMiddleware);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.