/request-filtering-agent

An http(s).Agent implementation that block request Private/Reserved IP addresses. Prevent SSRF.

Primary LanguageTypeScriptMIT LicenseMIT

request-filtering-agent Actions Status

An http(s).Agent class block the request to Private IP addresses and Reserved IP addresses.

It helps to prevent server-side request forgery (SSRF) attack.

This library depended on ipaddr.js definitions. This library block the request to these IP addresses by default.

So, This library block the request to non-unicast IP addresses.

Support http.Agent libraries

This library provides Node.js's http.Agent implementation. http.Agent is supported by popular library.

request-filtering-agent works with these libraries!

Install

Install with npm:

npm install request-filtering-agent

Usage

useAgent(url) return an agent for the url.

The agent blocks the request to Private network and Reserved IP addresses by default.

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 support loopback domain like nip.io. This library detects the IP address that is dns lookup-ed.

$ 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

Example code:

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

It will prevent 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
    // This values are preferred than denyAddressList
    // Default: []
    allowIPAddressList?: string[];
    // Deny address list
    // Default: []
    denyIPAddressList?: string[]
}
/**
 * Apply request filter to http(s).Agent instance
 */
export declare function applyRequestFilter<T extends http.Agent | https.Agent>(agent: T, options?: RequestFilteringAgentOptions): T;
/**
 * 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) => RequestFilteringHttpAgent | RequestFilteringHttpsAgent;

Example: Create an Agent with options

An agent that allow requesting 127.0.0.1, but it disallows other Private IP.

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

Example: Apply request filtering to excising http.Agent

You can apply request filtering to http.Agent or https.Agent using applyRequestFilter method.

const http = require("http")
const fetch = require("node-fetch");
const { applyRequestFilter } = require("request-filtering-agent");

// Create http agent with keepAlive option
const agent = new http.Agent({
    keepAlive: true,
});
// Apply request filtering to http.Agent
const agentWithFiltering = applyRequestFilter(agent, {
    allowPrivateIPAddress: false // Default: false
});
// 169.254.169.254 is private ip address aka. link-local addresses
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
// https://serverfault.com/questions/427018/what-is-this-ip-address-169-254-169-254
const url = 'http://169.254.169.254/';
fetch(url, {
    agent: agentWithFiltering
}).catch(error => {
    console.error(error); // Dis-allowed
});

Related

Changelog

See Releases page.

Running tests

Install devDependencies and Run yarn test:

yarn test

📝 This testing require IPv6 supports:

  • Travis CI: NG
  • GitHub Actions: OK

Contributing

Pull requests and stars are always welcome.

For bugs and feature requests, please create an issue.

For security issue, please see SECURITY.md

  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request :D

Author

License

MIT © azu