How IP Address Parsing Inconsistencies Cause SSRF and Trust-Boundary Bypass in Node.js Applications
Understanding the Vulnerability
In many Node.js applications, the ip-address library is used to parse, validate, and work with IPv4 addresses. The library's Address4 class is commonly relied upon to validate IP addresses before making network requests or enforcing access controls. However, version 10.2.0 and earlier contained a subtle but critical flaw: the library decoded leading-zero octets as decimal numbers, while DNS resolvers and most network systems interpreted them as octal.
This inconsistency created a security gap where an attacker could craft an IP address that the ip-address library would classify as safe (matching an allowlist), but which would be interpreted by the actual resolver as a completely different IP address—potentially one that should have been blocked.
The Vulnerability Explained
The Parsing Inconsistency
Consider this example:
- Input IP:
192.168.001.1 - ip-address 10.2.0 parses it as:
192.168.1.1(treating001as decimal) - DNS resolver interprets it as:
192.168.1.1in standard mode, but192.168.1.1in some systems, or octal192.168.001.1=192.168.1.1
But here's the dangerous case:
- Input IP:
127.000.000.001 - ip-address 10.2.0 parses it as:
127.0.0.1(decimal interpretation of leading zeros) - DNS resolver interprets it as:
127.0.0.1(octal interpretation:000= 0 in octal,001= 1 in octal)
While this example resolves to the same address, the vulnerability becomes critical when:
- Input IP:
192.168.0177.1(where0177is octal for 127) - ip-address 10.2.0 parses it as:
192.168.177.1(treating0177as decimal) - DNS resolver interprets it as:
192.168.127.1(treating0177as octal)
An attacker could submit 192.168.0177.1 to an application that uses ip-address for validation. The application checks: "Is this in my blocklist?" The library says no, it's 192.168.177.1. But when the resolver actually processes the request, it interprets it as 192.168.127.1—potentially an internal address that should have been blocked.
How This Enables SSRF Attacks
Server-Side Request Forgery (SSRF) attacks occur when an attacker can trick a server into making requests to unintended destinations. IP-based allowlists and blocklists are a common defense:
// Vulnerable code pattern in applications using ip-address 10.2.0
const Address4 = require('ip-address').Address4;
const INTERNAL_IPS = ['127.0.0.1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
function isAllowedIP(userProvidedIP) {
try {
const addr = new Address4(userProvidedIP);
const normalizedIP = addr.toString(); // Uses decimal parsing
// Check if normalized IP is in allowlist
return INTERNAL_IPS.some(internal => normalizedIP.startsWith(internal));
} catch (e) {
return false;
}
}
// Attacker submits: 127.000.000.001
// Library parses as: 127.0.0.1
// Allowlist check: PASSES (it's localhost)
// BUT if resolver behavior differs, actual request goes elsewhere
The attack flow:
1. Attacker provides a crafted IP like 192.168.0177.1
2. Application's ip-address library parses it as 192.168.177.1
3. Allowlist check passes because 192.168.177.1 isn't blocked
4. Application makes HTTP request to 192.168.0177.1
5. Resolver interprets 0177 as octal (127), resolving to 192.168.127.1
6. Request reaches an internal service that was supposed to be blocked
Real-World Impact
This vulnerability affects any Node.js application that:
- Uses ip-address library version 10.2.0 or earlier
- Implements IP-based access controls or SSRF prevention
- Makes HTTP requests based on user-provided IP addresses or URLs
- Runs in environments where DNS resolvers interpret octal notation
Affected applications could be vulnerable to:
- Internal service access: Bypassing firewall rules to access internal databases, caches, or APIs
- Metadata service attacks: On cloud platforms (AWS, GCP, Azure), accessing instance metadata services
- Local file access: Depending on the application's architecture
- Trust boundary bypass: Accessing services that should only be reachable internally
The Fix
The fix was implemented in ip-address version 10.3.1. The package upgrade corrected the octal parsing logic in the Address4 class to properly handle leading-zero octets according to standard resolver behavior.
What Changed
From the PR diff:
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
- "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.3.1.tgz",
+ "integrity": "sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==",
The upgrade from 10.2.0 to 10.3.1 in both package.json and package-lock.json applies the security patch.
How the Fix Works
In version 10.3.1, the Address4 parser was corrected to:
- Properly detect octal notation: When an octet begins with
0followed by digits, it's now correctly interpreted as octal (base 8) rather than decimal (base 10) - Align with resolver behavior: The parsing now matches how DNS resolvers and network utilities interpret IP addresses
- Maintain backward compatibility: Valid, standard IP addresses (without leading zeros) continue to work exactly as before
Before (10.2.0):
Input: "192.168.0177.1"
Parsing: 192 . 168 . 0177(decimal=177) . 1
Result: "192.168.177.1"
After (10.3.1):
Input: "192.168.0177.1"
Parsing: 192 . 168 . 0177(octal=127) . 1
Result: "192.168.127.1"
Why This Matters
Now, when an attacker submits 192.168.0177.1, the ip-address library will correctly identify it as 192.168.127.1, and your allowlist/blocklist checks will work as intended. The parsing behavior is no longer a security liability—it aligns with what actually happens on the network.
Prevention & Best Practices
1. Keep Dependencies Updated
Regularly audit and update dependencies, especially security-critical libraries like ip-address. Use tools like npm audit to identify vulnerable versions:
npm audit
npm update ip-address
2. Don't Rely Solely on IP Validation
While IP-based access controls are useful, they should be one layer of defense:
// Good: Multiple validation layers
function isRequestAllowed(req) {
// Layer 1: IP validation
if (!isAllowedIP(req.ip)) return false;
// Layer 2: Authentication
if (!req.user) return false;
// Layer 3: Authorization
if (!req.user.canAccessResource(req.resource)) return false;
return true;
}
3. Validate Against Actual Resolver Behavior
When implementing custom IP parsing, test against actual DNS resolvers:
const dns = require('dns').promises;
const Address4 = require('ip-address').Address4;
async function validateIP(userIP) {
try {
// Parse with ip-address
const addr = new Address4(userIP);
// Verify against actual DNS resolution
const resolved = await dns.resolve4(userIP);
// Compare parsed vs resolved
if (addr.toString() !== resolved[0]) {
console.warn(`Mismatch: ${userIP} parsed as ${addr.toString()}, resolved as ${resolved[0]}`);
return false; // Reject ambiguous IPs
}
return true;
} catch (e) {
return false;
}
}
4. Use Static Analysis Tools
Enable security scanning in your CI/CD pipeline:
# Trivy scans for known vulnerable dependencies
trivy fs .
# npm audit checks for CVEs
npm audit
5. Follow OWASP Guidelines
- OWASP A01:2021 – Broken Access Control: Implement proper access control, not just IP validation
- OWASP A10:2021 – Server-Side Request Forgery (SSRF): Use allowlists, not blocklists; validate all URLs and IPs
6. Test Edge Cases
Include tests for IP addresses with leading zeros:
const Address4 = require('ip-address').Address4;
describe('IP Address Parsing', () => {
it('should correctly parse octal notation', () => {
const addr = new Address4('192.168.0177.1');
expect(addr.toString()).toBe('192.168.127.1'); // Not 192.168.177.1
});
it('should reject ambiguous IPs', () => {
// Your application logic should reject IPs with leading zeros
// or verify them against actual resolver behavior
});
});
Key Takeaways
-
Leading-zero octets in IP addresses are octal, not decimal: The
ip-addresslibrary version 10.2.0 incorrectly treated them as decimal, creating a parsing mismatch with DNS resolvers and network systems. -
This mismatch enables SSRF attacks: Attackers could craft IPs that pass your allowlist but resolve to blocked internal addresses, bypassing trust boundaries.
-
The fix aligns parsing with resolver behavior: Upgrading to
ip-address10.3.1 ensures the library interprets IPs the same way DNS resolvers do, eliminating the vulnerability. -
IP validation alone is insufficient: Always use defense-in-depth with authentication, authorization, and multiple validation layers.
-
Regular dependency updates are critical: Vulnerabilities like this can be fixed with a simple version bump—but only if you keep your dependencies current.
How Orbis AppSec Detected This
Source: User-controlled URL or IP address input in network request functions
Sink: The Address4 class constructor and toString() method in the ip-address library (versions ≤10.2.0), which was used to validate IPs before making HTTP requests or enforcing access controls
Missing control: The ip-address library's parsing logic did not align with standard DNS resolver behavior for octal notation, creating an inconsistency that could be exploited
CWE: CWE-918 (Server-Side Request Forgery), CWE-1025 (Comparison Using Wrong Factors)
Fix: Upgrade ip-address from 10.2.0 to 10.3.1, which corrects the octal parsing logic to match standard resolver behavior
Orbis AppSec automatically detected this vulnerability through dependency scanning and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
CVE-2026-69192 demonstrates how subtle inconsistencies in parsing logic can create serious security vulnerabilities. By treating leading-zero octets as decimal instead of octal, the ip-address library created a trust boundary bypass that could allow SSRF attacks. The fix in version 10.3.1 aligns the library's behavior with standard resolver implementations, eliminating the vulnerability.
As developers, we should:
1. Keep dependencies updated and monitor security advisories
2. Understand the libraries we use, especially those handling security-critical functions like IP validation
3. Implement defense-in-depth rather than relying on a single security control
4. Test edge cases and validate assumptions about how systems interpret data
5. Use automated scanning to catch known vulnerabilities before they reach production
By staying vigilant and maintaining current dependencies, you can prevent vulnerabilities like this from affecting your applications.
References
- CWE-918: Server-Side Request Forgery (SSRF) – https://cwe.mitre.org/data/definitions/918.html
- CWE-1025: Comparison Using Wrong Factors – https://cwe.mitre.org/data/definitions/1025.html
- OWASP SSRF Prevention Cheat Sheet – https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
- OWASP Access Control Cheat Sheet – https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html
- Node.js DNS Module Documentation – https://nodejs.org/api/dns.html
- Semgrep Rule: IP Address Validation – https://semgrep.dev/r?q=ip-address
- GitHub PR: fix: upgrade ip-address to 10.3.1 (CVE-2026-69192)