azu/request-filtering-agent
GitHub: azu/request-filtering-agent
一个通过拦截私有和保留 IP 地址请求来防御 SSRF 攻击的 Node.js http(s).Agent 实现库。
Stars: 24 | Forks: 9
# request-filtering-agent [](https://github.com/azu/request-filtering-agent/actions)
一个用于拦截对 [私有 IP 地址](https://en.wikipedia.org/wiki/Private_network) 和 [保留 IP 地址](https://en.wikipedia.org/wiki/Reserved_IP_addresses) 请求的 [http(s).Agent](https://nodejs.org/api/http.html#http_class_http_agent) 类。
它有助于防止 [服务器端请求伪造 (SSRF)](https://en.wikipedia.org/wiki/Server-side_request_forgery) 攻击。
- [什么是 SSRF (服务器端请求伪造)?教程与示例](https://portswigger.net/web-security/ssrf)
此库依赖于 [ipaddr.js](https://github.com/whitequark/ipaddr.js) 的定义。
此库默认拦截发往以下 IP 地址的请求。
- [私有 IPv4 地址](https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses)
- [私有 IPv6 地址](https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses)
- [链路本地地址](https://en.wikipedia.org/wiki/Private_network#Link-local_addresses)
- [保留 IP 地址](https://en.wikipedia.org/wiki/Reserved_IP_addresses)
因此,此库会拦截发往非 `unicast` IP 地址的请求。
:warning: Node.js 内置的 `fetch` 不支持 `http.Agent`。
- [支持 nodejs/undici · Issue #23 · azu/request-filtering-agent](https://github.com/azu/request-filtering-agent/issues/23)
## 支持 `http.Agent` 的库
此库提供了 Node.js 的 [http.Agent](https://nodejs.org/api/http.html#http_class_http_agent) 实现。
[http.Agent](https://nodejs.org/api/http.html#http_class_http_agent) 受到流行库的支持。
- Node.js 内置的 `http` 和 `https`
- [node-fetch](https://github.com/bitinn/node-fetch)
- [node-http-proxy](https://github.com/http-party/node-http-proxy)
- [axios](https://github.com/axios/axios)
- [got](https://github.com/sindresorhus/got)
- [@cypress/request](https://github.com/cypress-io/request)
- :memo: [Request](https://github.com/request/request) 已被弃用,且存在 SSRF 问题
- [CVE-2023-28155 Request 允许通过攻击者控制的服务器进行跨协议重定向来绕过 SSRF 缓解措施 · Issue #3442 · request/request](https://github.com/request/request/issues/3442)
- [Request 中的服务器端请求伪造 · CVE-2023-28155 · GitHub Advisory Database](https://github.com/advisories/GHSA-p8p7-x288-28g6)
`request-filtering-agent` 可与这些库配合使用!
## 安装
使用 [npm](https://www.npmjs.com/) 安装:
```
npm install request-filtering-agent
```
### 支持的 Node.js 版本
| 版本 | Node.js 14 | Node.js 16 | Node.js 18 | Node.js 20 | Node.js 22 |
| :------ | :--------- | :--------- | :--------- | :--------- | :--------- |
| v1.x.x | 支持 | 支持 | 支持 | 不支持 | 不支持 |
| v2.x.x | 不支持 | 不支持 | 支持 | 支持 | 支持 |
| v3.x.x | 不支持 | 不支持 | 不支持 | 支持 | 支持 |
## 用法
`useAgent(url, options)` 为该 url 返回一个 agent。
该 agent 默认拦截对 [私有网络](https://en.wikipedia.org/wiki/Private_network) 和 [保留 IP 地址](https://en.wikipedia.org/wiki/Reserved_IP_addresses) 的请求。
```
const fetch = require("node-fetch");
const { useAgent } = require("request-filtering-agent");
const url = 'http://127.0.0.1:8080/';
fetch(url, {
// use http or https agent for url
agent: useAgent(url)
}).catch(err => {
console.err(err); // DNS lookup 127.0.0.1(family:4, host:127.0.0.1.nip.io) is not allowed. Because, It is private IP address.
});
```
`request-filtering-agent` 支持像 [nip.io](http://nip.io) 这样的环回域名。
此库会检测经过 DNS 查找的 IP 地址。
```
$ dig 127.0.0.1.nip.io
;127.0.0.1.nip.io. IN A
;; ANSWER SECTION:
127.0.0.1.nip.io. 300 IN A 127.0.0.1
```
示例代码:
```
const fetch = require("node-fetch");
const { useAgent } = require("request-filtering-agent");
const url = 'http://127.0.0.1.nip.io:8080/';
fetch(url, {
agent: useAgent(url) // use http or https agent for url
}).catch(err => {
console.err(err); // DNS lookup 127.0.0.1(family:4, host:127.0.0.1.nip.io) is not allowed. Because, It is private IP address.
});
```
它将防止 [DNS 重绑定](https://en.wikipedia.org/wiki/DNS_rebinding)
## API
```
export interface RequestFilteringAgentOptions {
// Allow to connect private IP address
// This includes Private IP addresses and Reserved IP addresses.
// https://en.wikipedia.org/wiki/Private_network
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
// Example, http://127.0.0.1/, http://localhost/, https://169.254.169.254/
// Default: false
allowPrivateIPAddress?: boolean;
// Allow to connect meta address 0.0.0.0
// 0.0.0.0 (IPv4) and :: (IPv6) a meta address that routing another address
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
// https://tools.ietf.org/html/rfc6890
// Default: false
allowMetaIPAddress?: boolean;
// Allow address list
// It supports CIDR notation.
// This values are preferred than denyAddressList
// Default: []
allowIPAddressList?: string[];
// Deny address list
// It supports CIDR notation.
// Default: []
denyIPAddressList?: string[];
}
/**
* A subclass of http.Agent with request filtering
*/
export declare class RequestFilteringHttpAgent extends http.Agent {
constructor(options?: http.AgentOptions & RequestFilteringAgentOptions);
}
/**
* A subclass of https.Agent with request filtering
*/
export declare class RequestFilteringHttpsAgent extends https.Agent {
constructor(options?: https.AgentOptions & RequestFilteringAgentOptions);
}
export declare const globalHttpAgent: RequestFilteringHttpAgent;
export declare const globalHttpsAgent: RequestFilteringHttpsAgent;
/**
* Get an agent for the url
* return http or https agent
* @param url
*/
export declare const useAgent: (url: string, options?: https.AgentOptions & RequestFilteringAgentOptions) => RequestFilteringHttpAgent | RequestFilteringHttpsAgent;
```
### 示例:创建带有选项的 Agent
一个允许请求 `127.0.0.1`,但禁止其他私有 IP 的 agent。
```
const fetch = require("node-fetch");
const { RequestFilteringHttpAgent } = require("request-filtering-agent");
// Create http agent that allow 127.0.0.1, but it disallow other private ip
const agent = new RequestFilteringHttpAgent({
allowIPAddressList: ["127.0.0.1"], // it is preferred than allowPrivateIPAddress option
allowPrivateIPAddress: false, // Default: false
});
// 127.0.0.1 is private ip address, but it is allowed
const url = 'http://127.0.0.1:8080/';
fetch(url, {
agent: agent
}).then(res => {
console.log(res); // OK
});
// Allow requests to a specific CIDR range
const agentWithCIDR = new RequestFilteringHttpAgent({
allowIPAddressList: ["192.168.1.0/24"],
});
const urlInCIDR = 'http://192.168.1.1:8080/';
fetch(urlInCIDR, {
agent: agentWithCIDR
}).then(res => {
console.log(res); // OK
});
// Deny requests to a specific CIDR range
const agentWithDenyCIDR = new RequestFilteringHttpAgent({
allowPrivateIPAddress: true,
denyIPAddressList: ["192.168.1.0/24"],
});
const urlInDenyCIDR = 'http://192.168.1.1:8080/';
fetch(urlInDenyCIDR, {
agent: agentWithDenyCIDR
}).catch(err => {
console.err(err); // DNS lookup 192.168.1.1(family:4, host:192.168.1.1) is not allowed. Because It is defined in denyIPAddressList.
});
```
## 相关
- [welefen/ssrf-agent: make http(s) request to prevent SSRF](https://github.com/welefen/ssrf-agent)
- 它仅提供高级封装
- 它仅处理 [node-ip](https://github.com/indutny/node-ip/blob/43e442366bf5a93493c8c4c36736f87d675b0c3d/lib/ip.js#L302-L314) 中定义的私有 IP 地址
- 缺少像 `0.0.0.0` 这样的元 IP 地址
## 更新日志
查看 [Releases 页面](https://github.com/azu/request-filtering-agent/releases)。
## 运行测试
安装 devDependencies 并运行 `yarn test`:
```
yarn test
```
:memo: 此测试需要 IPv6 支持:
- Travis CI: NG
- GitHub Actions: OK
## 作者
- [github/azu](https://github.com/azu)
- [twitter/azu_re](https://twitter.com/azu_re)
## 许可证
MIT © azu
标签:CISA项目, GNU通用公共许可证, HTTP代理, IPv4, IPv6, IP地址过滤, MITM代理, Node.js, PowerShell, SSRF防护, TLS, Web安全, 安全中间件, 服务端请求伪造, 白名单, 私有网络检测, 网络安全, 网络安全库, 自动化攻击, 自动化攻击, 蓝队分析, 请求过滤, 防御工具, 隐私保护, 黑名单