
Gepflegter Fork von node-ip, bei dem der ungepatchte SSRF-Sicherheitshinweis (CVE-2024-29415) behoben wurde.
Gepflegter Fork von ip — IP-Adress-Hilfsprogramme für Node.js — mit behobener ungepatchter SSRF-Sicherheitswarnung.
Das Originalpaket hat etwa 38 Mio. Downloads/Monat, keine Veröffentlichung seit Februar 2024 und eine Sicherheitswarnung mit hohem Schweregrad (CVE-2024-29415 / GHSA-2p57-rm9w-gvfp), die jede veröffentlichte Version betrifft und bei jeder Installation npm audit fehlschlagen lässt. Dieser Fork behebt die Grundursache, anstatt nur Regex zu patchen.
npm install @bybrave/ip2
// CommonJS — drop-in
const ip = require('@bybrave/ip2');
// ESM — named imports
import { isPrivate, isPublic, cidrSubnet } from '@bybrave/ip2';
// TypeScript types built in
Die ursprünglichen isPrivate() / isPublic() / isLoopback() führten Regexes über den rohen Eingabestring aus. Jede Schreibweise einer Adresse, die die Regexes nicht vorhersahen — oktale oder hexadezimale Oktette, abgekürzte IPv4, unkomprimiertes IPv6 — wurde stillschweigend als public klassifiziert:
// original [email protected] — all of these are loopback/private, all reported PUBLIC:
ip.isPublic('127.1') // true 💥
ip.isPublic('012.1.2.3') // true 💥 (octal 10.1.2.3)
ip.isPublic('01200034567') // true 💥 (integer 10.0.14.119)
ip.isPublic('000:0:0000::01') // true 💥 (that's ::1)
Wenn diese Antworten ausgehende Anfragen schützen, handelt es sich um ein SSRF-Loch (CVE-2024-29415, unvollständiger Fix von CVE-2023-42282).
Dieser Fork:
net.isIP, plus IPv6-Zonen-IDs). Die Klassifizierung erfolgt auf Bytes, niemals auf Zeichenketten.127.1, 0x7f.1, 0177.0.0.1, 2130706433) werfen einen Fehler anstatt geraten zu werden. Fail closed, nicht open. Falls Sie veraltete Notationen akzeptieren müssen, normalisieren Sie zuerst explizit: ip.fromLong(ip.normalizeToLong(addr)).isPrivate() bedeutet jetzt "global nicht erreichbar" — die gleiche Konvention wie bei Pythons ipaddress. Das fügt CGNAT (100.64/10), TEST-NETs, Benchmarking, Multicast, Reservierte, ULA, Dokumentationsbereiche und mehr hinzu — siehe Tabelle unten.// @bybrave/ip2
ip.isPublic('127.1') // throws Error: Invalid ip address: 127.1
ip.isPrivate('000:0:0000::01') // true (parsed as ::1)
ip.isLoopback('::fFFf:127.0.0.1')// true
ip.isPublic('64:ff9b::8.8.8.8') // true (NAT64 of a public address)
ip.isPrivate('100.64.0.1') // true (CGNAT — original said public)
[email protected]- const ip = require('ip');
+ const ip = require('@bybrave/ip2');
Die API-Oberfläche ist gleich; die Verhaltensunterschiede sind die Sicherheits- und Korrektheitskorrekturen — siehe die bahnbrechenden Änderungen unten vor dem Upgrade.
Version 3.0.0, weil Strenge die Lösung ist — wenn Sie dieser Bibliothek nicht-kanonische Zeichenfolgen gegeben haben, hat v2 Ihnen falsche Antworten geliefert, keine kompatiblen:
isPrivate / isPublic / isLoopback / toBuffer / toLong werfen bei fehlerhafter oder nicht-kanonischer Eingabe (oktale/hexadezimale Oktette, 127.1, ganzzahlige Zeichenfolgen). Verwenden Sie zuerst normalizeToLong(), falls Sie veraltete Notationen akzeptieren müssen.isLoopback('fe80::1') ist jetzt false (link-local ≠ loopback; bleibt isPrivate). isLoopback('::') ist jetzt false (unspezifiziert ≠ loopback; bleibt isPrivate).loopback('ipv6') gibt statt zurück.Alles, was das Original exportiert, plus isValid():
ip.address('public', 'ipv6') // address of a network interface
ip.isPrivate('10.0.0.1') // true — not globally reachable
ip.isPublic('8.8.8.8') // true
ip.isLoopback('127.8.8.8') // true
ip.isValid('300.1.2.3') // false (never throws)
ip.isV4Format('192.168.0.1') // true
ip.isV6Format('2001:db8::1') // true
ip.isEqual('::ffff:7f00:1', '127.0.0.1') // true
ip.toBuffer('127.0.0.1') // Buffer([127, 0, 0, 1])
ip.toString(buf, offset, length) // '127.0.0.1'
ip.toLong('127.0.0.1') // 2130706433
ip.fromLong(2130706433) // '127.0.0.1'
ip.normalizeToLong('127.1') // 2130706433 (lenient, -1 on error)
ip.fromPrefixLen(24) // '255.255.255.0'
ip.mask('192.168.1.134', '255.255.255.0') // '192.168.1.0'
ip.cidr('192.168.1.134/26') // '192.168.1.128'
ip.not('255.255.255.0') // '0.0.0.255'
ip.or('0.0.0.255', '192.168.1.10') // '192.168.1.255'
const s = ip.cidrSubnet('192.168.1.134/26')
s.networkAddress // '192.168.1.128'
s.firstAddress // '192.168.1.129'
s.lastAddress // '192.168.1.190'
s.broadcastAddress // '192.168.1.191'
s.subnetMaskLength // 26
s.numHosts // 62
s.contains('::ffff:192.168.1.180') // true (v4-mapped handled)
IPv4: 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10 (CGNAT), 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.88.99.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4 (inkl. Broadcast).
IPv6: ::/96 (unspezifiziert, Loopback, v4-kompatibel), 64:ff9b:1::/48, 100::/64, 2001::/23, 2001:db8::/32, 3fff::/20, 5f00::/16, fc00::/7, fe80::/10, fec0::/10, ff00::/8. Übergangsbereiche ::ffff:0:0/96, 64:ff9b::/96 und 2002::/16 folgen ihrer eingebetteten IPv4-Adresse.
Wenn dieses Paket Ihnen Zeit spart, können Sie die Wartung unterstützen:
Bitcoin (BTC): bc1q37557q5jpeaxqydzwvf3jgj7zhnfpn2td3q40q
MIT. Basierend auf node-ip von Fedor Indutny.
::ffff:a.b.c.d64:ff9b::a.b.c.d2002:xxxx:xxxx::| Behoben | Ursprüngliches Problem |
|---|
| CVE-2024-29415 / GHSA-2p57-rm9w-gvfp — SSRF über nicht-kanonische Adressen | #136, #150, #153, #158 |
isPrivate() übersieht CGNAT, TEST-NETs, Benchmarking, Multicast, reservierte, Dokumentationsbereiche | — |
cidrSubnet(...).contains() gibt false für IPv4-gemappte IPv6-Adressen zurück | #104 |
isV6Format() gibt true für IPv4-Adressen zurück | #67 |
isV4Format() akzeptiert Oktette ≥ 256 | #105 |
address('public') gab private Adressen zurück und umgekehrt (Filter war invertiert) | — |
loopback('ipv6') gab fe80::1 zurück, das link-local ist, nicht loopback → jetzt ::1 | — |
cidr()/cidrSubnet() erzeugten stillschweigend Müll für ungültige Präfixlängen (/33, /x) → werfen jetzt einen Fehler | — |
address(name) stürzte mit TypeError für unbekannte Schnittstellennamen ab → gibt jetzt undefined zurück | — |
TypeScript-Typen integriert (kein @types/ip nötig), ESM benannte Exporte | — |
'::1''fe80::1'isPrivate() gibt für mehr Bereiche true zurück (CGNAT, TEST-NETs, Multicast, …) — es bedeutet jetzt "global nicht erreichbar".address('public') gibt jetzt tatsächlich öffentliche Adressen zurück. Falls Sie auf das invertierte Verhalten angewiesen waren, um eine LAN-Adresse zu erhalten, verwenden Sie address('private') — das hat es Ihnen geliefert.