IP 追溯主要用於獲取請求的真實 IP ,因爲現有服務是基於 Nginx 實現負載均衡的,所以獲取請求真實 IP 存在必定難度。在 Matrix 現有實現中,IP 追溯由 getIp
函數完成,其具體代碼以下。javascript
/** * 得到請求發送方的 ip * @param {Context} ctx * @return {string} */
export function getIp(ctx) {
const xRealIp = ctx.get('X-Real-Ip');
const { ip } = ctx;
const { remoteAddress } = ctx.req.connection;
return xRealIp || ip || remoteAddress;
}
複製代碼
在對代碼進行更爲詳細的討論以前,咱們須要分析幾種經常使用獲取請求 IP 來源的方式:html
req.socket.remoteAddress
X-Forwarded-For
X-Real-IP
在 Node.js 官方文檔 net_socket_remoteaddress 中,咱們得知可經過 req.socket.remtoeAddress
獲取 Socket 鏈接的源 IP 地址信息。java
socket.remoteAddress
Added in: v0.5.10node
- String
The string representation of the remote IP address. For example,
'74.125.127.100'
or'2001:4860:a005::68'
. Value may beundefined
if the socket is destroyed (for example, if the client disconnected).nginx
根據官方文檔 http_request_socket 的描述,req.connection
和 req.socket
是等價的,咱們也能夠經過 req.connection.remoteAddress
獲取 Socket 鏈接的源 IP 信息。這種獲取請求 IP 來源的方式,適用於客戶端直連服務端的場景。git
但因爲現有 Matrix 服務使用了 Nginx 做爲服務集羣的負載均衡,故經過 req.socket.connection
獲取獲得的是 Nginx 的 IP 地址,並非請求的真實 IP 地址。github
根據 RFC 7239 規範,HTTP 代理(如 Nginx、Apache 等)會改寫 HTTP 請求頭部,添加 X-Forwarded-For
字段,用於追蹤請求的來源,該字段的格式以下:api
X-Forwarded-For: client, proxy1, proxy2
複製代碼
下面對 X-Forwarded-For
字段的處理過程,進行詳細闡述:瀏覽器
X-Forwarded-For
字段。X-Forwared-For
字段,而且將值設置爲客戶端的 IP 地址。X-Forwarded-For
字段值中追加上一代理的 IP 地址。X-Forwarded-For
值的最左邊的 IP 地址獲取客戶端的 IP 地址。但遺憾的是,X-Forwarded-For
是可僞造的:若是客戶端在發起 HTTP 請求時,設置了僞造的 X-Forwarded-For
字段,因爲後續的每層代理只會追加此字段而不會覆蓋,故業務服務器最終獲取的客戶端 IP 地址多是客戶端僞造的地址。安全
一般,咱們須要進行額外的配置,才能使得 Nginx 支持 X-Forwarded-For
。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
複製代碼
HTTP 代理服務器可在請求頭部中設置 X-Real-IP
請求源信息,但這並非 RFC 規範中的內容。
X-Real-IP
是客戶端不可僞造的,但僅能描述最近一個代理的真實 IP ,若是有多層代理,其仍舊不可做爲客戶端請求的真實 IP 。在現實生活中,多層代理實際上是比較少見的,常見的是單層代理,所以在一般狀況下,使用 X-Real-IP
是足以完成任務的,且相對於 X-Forwarded-For
有更好的安全性。
咱們可經過如下設置,使得 Nginx 支持 X-Real-IP
字段的自動設置。
proxy_set_header X-Real-IP $remote_addr;
複製代碼
如下是我對幾種獲取客戶端真實 IP 的方式的總結,分析了各方式的侷限和應用場景。
req.socket.remoteAddress | X-Forwarded-For | X-Real-IP | |
---|---|---|---|
是否可僞造 | 否 | 是 | 否 |
有效性 | 僅在客戶端直連服務端時 | 僅在未僞造時 | 僅在客戶端未通過多級代理時 |