Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
ip2 — node-ip의 유지 관리 포크로, 패치되지 않은 SSRF 권고(CVE-2024-29415)가 수정되었습니다. | Kitploit
도구/GitHubGitHub/bybravehq/ip2
General Purpose UtilitiesStatic AnalysisVulnerability AnalysisConfiguration AuditingWeb SecurityNetwork Security
GitHubbybravehq/ip2

ip2

node-ip의 유지 관리 포크로, 패치되지 않은 SSRF 권고(CVE-2024-29415)가 수정되었습니다.

저장소 보기
3310일 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
웹사이트
공유

@bybrave/ip2

CI npm

ip — Node.js용 IP 주소 유틸리티 — 의 유지 관리되는 포크로, 패치되지 않은 SSRF 취약점이 수정되었습니다.

원본 패키지는 월 약 3,800만 다운로드를 기록하지만 2024년 2월 이후 릴리스가 없으며, 모든 게시된 버전에 영향을 미치고 모든 설치 시 npm audit을 실패하게 만드는 HIGH 심각도 권고(CVE-2024-29415 / GHSA-2p57-rm9w-gvfp)가 있습니다. 이 포크는 정규식을 패치하는 대신 근본 원인을 수정합니다.

npm install @bybrave/ip2
// CommonJS — 드롭인
const ip = require('@bybrave/ip2');

// ESM — 명명된 가져오기
import { isPrivate, isPublic, cidrSubnet } from '@bybrave/ip2';

// TypeScript 타입 내장

취약점과 수정 방법

원본 isPrivate() / isPublic() / isLoopback()은 원시 입력 문자열에 대해 정규식을 실행했습니다. 정규식이 예상하지 못한 주소 표기법 — 8진수 또는 16진수 옥텟, 축약형 IPv4, 비압축 IPv6 — 은 조용히 공용으로 분류되었습니다:

// 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)

이러한 답변이 아웃바운드 요청을 보호한다면, 이는 SSRF 취약점입니다(CVE-2024-29415, CVE-2023-42282의 불완전한 수정).

이 포크:

  1. 먼저 파싱하고, 그 다음 분류합니다. 모든 분류 함수는 엄격한 파서(net.isIP와 동일한 문법, IPv6 존 ID 추가)를 사용하여 주소를 바이트로 파싱합니다. 분류는 문자열이 아닌 바이트에 대해 실행됩니다.
  2. 모호한 입력을 거부합니다. 비정규 표기법(127.1, 0x7f.1, 0177.0.0.1, 2130706433)은 추측하는 대신 throw합니다. 열린 상태가 아닌 닫힌 상태로 실패합니다. 레거시 표기법을 허용해야 하는 경우 먼저 명시적으로 정규화하세요: ip.fromLong(ip.normalizeToLong(addr)).
  3. 전체 IANA 특수 목적 레지스트리를 포함합니다 (IPv4, IPv6), 4개의 정규식이 아닙니다. isPrivate()는 이제 "전역적으로 도달 가능하지 않음"을 의미합니다 — Python의 ipaddress와 동일한 규칙입니다. 이는 CGNAT(100.64/10), TEST-NET, 벤치마킹, 멀티캐스트, 예약, ULA, 문서 범위 등을 추가합니다 — 아래 표를 참조하세요.
  4. 전환 주소를 내장된 IPv4로 분류합니다. ::ffff:a.b.c.d(v4-mapped), 64:ff9b::a.b.c.d(NAT64) 및 2002:xxxx:xxxx::(6to4)는 내부의 IPv4 주소에 따라 private/public입니다.
// @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]에 대한 모든 수정 사항

수정됨원본 문제
CVE-2024-29415 / GHSA-2p57-rm9w-gvfp — 비정규 주소를 통한 SSRF#136, #150, #153, #158
isPrivate()가 CGNAT, TEST-NET, 벤치마킹, 멀티캐스트, 예약, 문서 범위를 놓침—
cidrSubnet(...).contains()가 IPv4-mapped IPv6 주소에 대해 false 반환#104
isV6Format()이 IPv4 주소에 대해 true 반환#67
isV4Format()이 256 이상의 옥텟을 허용#105
address('public')이 private 주소를 반환하고 그 반대도 마찬가지 (필터가 반전됨)—
loopback('ipv6')이 링크 로컬인 fe80::1을 반환했으며 루프백이 아님 → 이제 ::1—
cidr()/cidrSubnet()이 잘못된 접두사 길이(/33, /x)에 대해 조용히 쓰레기 값을 생성 → 이제 throw—
address(name)이 알 수 없는 인터페이스 이름에 대해 TypeError로 충돌 → 이제 undefined 반환—
cidrSubnet()/subnet()이 IPv6에 대해 잘못된 답변 제공(/48이 인접한 /48을 포함) → 실제 IPv6 서브넷 계산#89, #110
브라우저용 번들링이 os에서 실패 (webpack 5) → browser 필드가 매핑하여 제거#127, #132
TypeScript 타입 내장 (@types/ip 불필요), ESM 명명된 내보내기—

ip에서 마이그레이션

- const ip = require('ip');
+ const ip = require('@bybrave/ip2');

API 표면은 동일합니다. 동작 차이는 보안 및 정확성 수정 사항입니다 — 업그레이드하기 전에 아래의 호환성 깨지는 변경 사항을 확인하세요.

호환성 깨지는 변경 사항 (v3)

버전 3.0.0인 이유는 엄격성이 수정 사항이기 때문입니다 — 이 라이브러리에 비정규 문자열을 제공했다면 v2는 호환 가능한 답변이 아니라 잘못된 답변을 제공한 것입니다:

  • isPrivate / isPublic / isLoopback / toBuffer / toLong은 잘못된 형식 또는 비정규 입력(8진수/16진수 옥텟, 127.1, 정수 문자열)에 대해 throw합니다. 레거시 표기법을 허용해야 하는 경우 먼저 normalizeToLong()을 사용하세요.
  • isLoopback('fe80::1')은 이제 false입니다 (링크 로컬 ≠ 루프백; 여전히 isPrivate). isLoopback('::')은 이제 false입니다 (미지정 ≠ 루프백; 여전히 isPrivate).
  • loopback('ipv6')은 'fe80::1' 대신 '::1'을 반환합니다.
  • isPrivate()는 더 많은 범위(CGNAT, TEST-NET, 멀티캐스트, …)에 대해 true를 반환합니다 — 이제 "전역적으로 도달 가능하지 않음"을 의미합니다.
  • address('public')은 이제 실제로 공용 주소를 반환합니다. LAN 주소를 얻기 위해 반전된 동작에 의존했다면 address('private')를 사용하세요 — 그것이 원래 제공하던 것입니다.
  • Node.js ≥ 18.

API

원본이 내보내는 모든 것에 더해 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)

const s6 = ip.cidrSubnet('2001:db8:f53b::/48')
s6.lastAddress                        // '2001:db8:f53b:ffff:ffff:ffff:ffff:ffff'
s6.contains('2001:db8:f53a::1')       // false
s6.length                             // 2 ** 80 (approximate above 2 ** 53)

IPv6 서브넷에는 브로드캐스트 또는 예약 호스트 주소가 없습니다: firstAddress는 네트워크 주소이고 broadcastAddress는 lastAddress와 같습니다. ip.subnet(addr, mask)는 주소와 마스크가 모두 IPv6일 때 IPv6 계산을 사용합니다. 일반 IPv4 주소는 IPv4-mapped 범위(::ffff:0:0/96)를 통해 IPv6 서브넷과 매칭됩니다.

잘못된 입력은 throw

isPrivate, isPublic, isLoopback, toBuffer 및 toLong은 잘못된 입력에 대해 추측을 반환하는 대신 throw합니다 (#146). 입력이 신뢰할 수 없는 경우 먼저 확인하거나 catch하세요:

const isInternal = ip.isValid(host) && ip.isPrivate(host)

브라우저 및 번들러

이 라이브러리는 Node 우선입니다. 번들에서 os는 browser 필드를 통해 매핑되어 제거되므로 address()를 사용할 수 없으며, Buffer를 사용할 수 있으면 나머지는 모두 작동합니다. webpack 5에서는 폴리필을 추가하세요 (#127, #132):

// webpack.config.js, after `npm i buffer`
resolve: { fallback: { buffer: require.resolve('buffer/') } }

private으로 처리되는 범위 (전역적으로 도달 가능하지 않음)

도구 다운로드