Express.js: cách lấy địa chỉ máy khách từ xa


256

Tôi hoàn toàn không hiểu làm thế nào tôi nên có một địa chỉ IP người dùng từ xa.

Giả sử tôi có một lộ trình yêu cầu đơn giản như:

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

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

Cách tiếp cận trên có đúng để lấy địa chỉ IP người dùng thực hay có cách nào tốt hơn không? Còn proxy thì sao?


1
Làm thế nào về việc sử dụng nút-ipware theo giải thích ở đây .
un33k

nếu bạn không thể nhận req.hostname như 'example.com': stackoverflow.com/a/37824880/5333284
zhi.yang

Câu trả lời:


469

Nếu bạn đang chạy phía sau một proxy như NGiNX hoặc những gì có bạn, chỉ sau đó bạn nên kiểm tra 'x-Forwarded-for':

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

Nếu proxy không phải là 'của bạn', tôi sẽ không tin vào tiêu đề 'x-Forward-for', bởi vì nó có thể bị giả mạo.


2
Điều này là chính xác, tuy nhiên trong tình huống của tôi, tôi đã phải sử dụng dấu ngoặc vuông (xem chỉnh sửa ở trên) Cũng đảm bảo rằng bạn đã bật x-chuyển tiếp-cho kích hoạt trong cấu hình nginx của bạn. Hoạt động như một lá bùa!
Chín giờ

43
Bạn cần lưu ý rằng bạn phải đưa lệnh này proxy_set_header X-Forwarded-For $remote_addr;vào cấu hình nginx của mình trong trường hợp bạn đang sử dụng proxy ngược của chính mình.
Coxer

Nếu bạn dự định sử dụng IP trong trình duyệt thì http (s): // [ipv6]: port Lưu ý [] là cần thiết, hy vọng điều này sẽ giúp
psuhas

43
người dùng copy-pasta xin lưu ý: Điều này có thể trả về một danh sách địa chỉ IP được phân tách bằng dấu phẩy. Chúng tôi đã có một lỗi từ một nhà phát triển sao chép điều này và so sánh kết quả với một IP. Có lẽ làm một cái gì đó như var ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress || '').split(',')[0].trim();để có được IP khách hàng.
Davy Jones

3
@Rishav :: 1 là địa chỉ IPv6 cho localhost
Daniel O

238

Trong khi câu trả lời từ @alessioalex hoạt động, có một cách khác như được nêu trong phần Express đằng sau phần ủy nhiệm của Express - guide .

  1. Thêm vào app.set('trust proxy', true)mã khởi tạo nhanh của bạn.
  2. Khi bạn muốn lấy ip của máy khách từ xa, hãy sử dụng req.iphoặc req.ipstheo cách thông thường (như thể không có proxy ngược)

Đọc tùy chọn:

  • Sử dụng req.iphoặc req.ips. req.connection.remoteAddresskhông hoạt động với giải pháp này.
  • Có nhiều tùy chọn hơn 'trust proxy'nếu bạn cần thứ gì đó tinh vi hơn là tin tưởng mọi thứ được chuyển qua trong x-forwarded-fortiêu đề (ví dụ: khi proxy của bạn không xóa tiêu đề x-chuyển tiếp có sẵn từ các nguồn không đáng tin cậy). Xem hướng dẫn liên kết để biết thêm chi tiết.
  • Nếu máy chủ proxy của bạn không có x-forwarded-fortiêu đề dân cư , có hai khả năng.
    1. Máy chủ proxy không chuyển tiếp thông tin về nơi yêu cầu ban đầu. Trong trường hợp này, sẽ không có cách nào để tìm ra yêu cầu ban đầu từ đâu. Bạn cần sửa đổi cấu hình của máy chủ proxy trước.
      • Ví dụ: nếu bạn sử dụng nginx làm proxy ngược, bạn có thể cần thêm proxy_set_header X-Forwarded-For $remote_addr;vào cấu hình của mình.
    2. Máy chủ proxy chuyển tiếp thông tin về nơi yêu cầu ban đầu được gửi theo kiểu độc quyền (ví dụ: tiêu đề http tùy chỉnh). Trong trường hợp như vậy, câu trả lời này sẽ không hoạt động. Có thể có một cách tùy chỉnh để lấy thông tin đó ra, nhưng trước tiên bạn cần hiểu cơ chế này.

4
Câu trả lời của tôi chung chung hơn (không gắn với Express), nhưng nếu bạn đang sử dụng Express thì đó thực sự là cách tốt hơn.
alessioalex

4
Máy chủ proxy của bạn phải được đặt tiêu đề 'x-Forwarded-for' thành địa chỉ từ xa. Ví dụ, trong trường hợp nginx, bạn nên có proxy_set_header X-Forwarded-For $ remote_addr trong tệp cấu hình của bạn
Kamagatos

1
Đằng sau IIS với IISnode là proxy, app.enable('trust proxy')cũng hoạt động để sử dụng req.ip. Ngoại trừ tôi có được cổng với nó 1.2.3.4:56789. Để loại bỏ điều đó, tôi làmvar ip = req.ip.split(':')[0]
Christiaan Westerbeek

Giải pháp này có an toàn không?
Daniel Kmak

3
Tôi cảm thấy rằng câu trả lời này thiếu bảo mật và cần cập nhật. Bạn nên LUÔN xác định proxy nào ứng dụng của bạn tin tưởng. Câu trả lời được chấp nhận ít nhất có một thông báo nhỏ về việc giả mạo. Điều đó nói rằng, đó là một giải pháp tốt hơn để sử dụng một thư viện như thế này nếu bạn đang sử dụng express, nhưng mã được trích dẫn không chính xác và không được tìm thấy trên tài nguyên được liên kết.
Phil

54

Trong nginx.conftập tin:
proxy_set_header X-Real-IP $remote_addr;

Trong node.jstệp máy chủ:
var ip = req.headers['x-real-ip'] || req.connection.remoteAddress;

lưu ý rằng tiêu đề viết thường


3
Chào mừng bạn đến với Stack Overflow! Thay vì chỉ đăng một khối mã, vui lòng giải thích lý do tại sao mã này giải quyết vấn đề được đặt ra. Không có lời giải thích, đây không phải là một câu trả lời.
Artemix

1
Câu trả lời của @ququzone là ok. Giải thích được đặt tiêu đề tùy chỉnh theo yêu cầu có tên "x-real-ip", lấy địa chỉ IP gốc từ khách truy cập. Nó hoạt động với tôi với nút và socket.io.
coffekid

8
Đối với tôi, địa chỉ IP có sẵn req.headers['x-real-ip']ngay cả trong nginx.conftiêu đề được đặt bằng chữ in hoa.
Nik Sumeiko

Đây là những gì đã giải quyết nó cho tôi. Ngay cả khi proxy ủy quyền được đặt thành đúng, nó vẫn sử dụng địa chỉ 127.0.0.1 cục bộ
David

30

Riêng với nút, tài liệu cho thành phần máy chủ http, trong kết nối sự kiện cho biết:

[Kích hoạt] khi một luồng TCP mới được thiết lập. Ổ cắm [là] một đối tượng của loại net.Socket. Thông thường người dùng sẽ không muốn truy cập sự kiện này. Cụ thể, ổ cắm sẽ không phát ra các sự kiện có thể đọc được do cách trình phân tích cú pháp giao thức gắn vào ổ cắm. Các ổ cắm cũng có thể được truy cập tại request.connection.

Vì vậy, điều đó có nghĩa request.connectionlà một ổ cắm và theo tài liệu thực sự có một thuộc tính socket.remoteAddress mà theo tài liệu này là:

Chuỗi đại diện của địa chỉ IP từ xa. Ví dụ: '74 .125.127.100 'hoặc' 2001: 4860: a005 :: 68 '.

Theo express, đối tượng yêu cầu cũng là một thể hiện của đối tượng yêu cầu Node http, vì vậy phương pháp này vẫn hoạt động.

Tuy nhiên, trong Express.js, yêu cầu đã có hai thuộc tính: req.ipreq.ips

req.ip

Trả lại địa chỉ từ xa hoặc khi "proxy ủy thác" được bật - địa chỉ ngược dòng.

req.ips

Khi "proxy ủy thác"true, phân tích danh sách địa chỉ IP "X-Forwarded-For" và trả về một mảng, nếu không thì một mảng trống được trả về. Ví dụ: nếu giá trị là "client, proxy1, proxy2", bạn sẽ nhận được mảng ["client", "proxy1", "proxy2"] trong đó "proxy2" là luồng xuống xa nhất.

Theo tôi hiểu, có thể đáng nói rằng, Express req.iplà cách tiếp cận tốt hơn so với req.connection.remoteAddress, vì req.ipchứa ip máy khách thực tế (với điều kiện proxy đáng tin cậy được bật nhanh), trong khi cái kia có thể chứa địa chỉ IP của proxy (nếu có một).

Đó là lý do tại sao câu trả lời hiện được chấp nhận cho thấy:

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

Các req.headers['x-forwarded-for']sẽ tương đương với tốc req.ip.



7

Đây chỉ là thông tin bổ sung cho câu trả lời này .

Nếu bạn đang sử dụng nginx, bạn sẽ thêm proxy_set_header X-Real-IP $remote_addr;vào khối vị trí cho trang web. /etc/nginx/sites-available/www.example.comví dụ. Dưới đây là một khối máy chủ ví dụ.

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;
    }
}

Sau khi khởi động lại nginx, bạn sẽ có thể truy cập ip trong node/ expresstuyến ứng dụng của bạn vớireq.headers['x-real-ip'] || req.connection.remoteAddress;


3

Theo Express đằng sau proxy , req.ipđã đưa vào tài khoản proxy ngược nếu bạn đã cấu hình trust proxyđúng. Do đó, nó tốt hơn so với req.connection.remoteAddressthu được từ lớp mạng và không biết về proxy.


3

Tôi đã viết một gói cho mục đích đó. Bạn có thể sử dụng nó như là phần mềm trung gian thể hiện. Gói của tôi được xuất bản ở đây: https://www.npmjs.com/package/express-ip

Bạn có thể cài đặt mô-đun bằng cách sử dụng

npm i express-ip

Sử dụng

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
Bạn phải tiết lộ liên kết trong bài viết khi liên kết đến một cái gì đó bạn liên kết với. Nếu bạn không tiết lộ liên kết, nó được coi là thư rác. Tiết lộ phải rõ ràng, nhưng không cần phải chính thức (ví dụ: Đối với nội dung cá nhân của riêng bạn : "trên trang web của tôi", "trên blog của tôi", v.v.). Xem: Điều gì biểu thị sự tự quảng cáo "Tốt"? , một số mẹo và lời khuyên về tự quảng cáo , định nghĩa chính xác của "thư rác" cho Stack Overflow là gì? những gì làm cho một cái gì đó thư rác .
Makyen

2

Tôi biết câu hỏi này đã được trả lời, nhưng đây là cách tôi khiến tôi làm việc.

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

2

Nếu bạn ổn sử dụng thư viện của bên thứ 3. Bạn có thể kiểm tra ip yêu cầu .

Bạn có thể sử dụng nó là bởi

import requestIp from 'request-ip';

app.use(requestIp.mw())

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

Mã nguồn khá dài, vì vậy tôi sẽ không sao chép ở đây, bạn có thể kiểm tra tại https://github.com/pbojinov/request-ip/blob/master/src/index.js

Về cơ bản,

Nó tìm kiếm các tiêu đề cụ thể trong yêu cầu và rơi trở lại một số mặc định nếu chúng không tồn tại.

IP người dùng được xác định theo thứ tự sau:

  1. X-Client-IP
  2. X-Forwarded-For (Tiêu đề có thể trả về nhiều địa chỉ IP theo định dạng: "IP khách, proxy 1 IP, proxy 2 IP", vì vậy chúng tôi lấy địa chỉ đầu tiên.)
  3. CF-Connecting-IP (Đám mây)
  4. Fastly-Client-Ip (Tiêu đề lưu trữ CDN và Firebase nhanh khi được chuyển sang chức năng đám mây)
  5. True-Client-Ip (Akamai và Cloudflare)
  6. X-Real-IP (Nginx proxy / FastCGI)
  7. X-Cluster-Client-IP (Rackspace LB, Cá đuối gai độc)
  8. X-Forwarded, Forwarded-ForForwarded(biến thể của # 2)
  9. req.connection.remoteAddress
  10. req.socket.remoteAddress
  11. req.connection.socket.remoteAddress
  12. req.info.remoteAddress

Nếu một địa chỉ IP không thể được tìm thấy, nó sẽ trở lại null.

Tiết lộ: Tôi không liên kết với thư viện.


1

Điều này làm việc cho tôi tốt hơn so với phần còn lại. Các trang web của tôi đứng sau CloudFlare và dường như cần phải có cf-connecting-ip.

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

Không kiểm tra Express đằng sau proxy vì nó không nói gì về cf-connecting-iptiêu đề này .


1

Với hỗ trợ can-flare, nginx và 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

Trong trường hợp của tôi, tương tự như giải pháp này , tôi đã kết thúc bằng cách sử dụng phương pháp x-Forwarded-for sau đây:

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

x-forwarded-fortiêu đề sẽ tiếp tục thêm tuyến của IP từ gốc đến máy chủ đích cuối cùng, do đó nếu bạn cần truy xuất IP của máy khách gốc, đây sẽ là mục đầu tiên của mảng.


0

var ip = req.connection.remoteAddress;

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


ouput giống như: - :: ffff: XXX.XX.XX.XX từ đây, chúng tôi sẽ nhận được ip
Bhulawat Ajay

3
Tôi nghĩ ip = ip.split(':').pop();sẽ vùi dập trong trường hợp này nếu ip bình thường tức là 127.0.0.1 sẽ đến Nó vẫn có thể cung cấp cho bạn ip.
9 giờ

0

Đối tượng tiêu đề có mọi thứ bạn cần, chỉ cần làm điều này:

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

0

Kết hợp tất cả các giải pháp witk @kakopappa cùng với morganghi nhật ký địa chỉ IP của khách hàng:

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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.