El módulo ipaddress en Python crea, valida y manipula direcciones IPv4, IPv6, redes CIDR e interfaces. Permite comprobar pertenencia, calcular red y broadcast, dividir bloques, resumir rangos, clasificar direcciones especiales y convertir entre texto, enteros y bytes.
La biblioteca no abre sockets, no resuelve DNS y no modifica la configuración del sistema. Trabaja con valores y reglas de direccionamiento. Esta separación es útil en firewalls, listas permitidas, inventarios, logs, APIs y validadores de configuración.
Crea una dirección automáticamente
from ipaddress import ip_address
ipv4 = ip_address("192.0.2.10")
ipv6 = ip_address("2001:db8::10")
print(ipv4.version)
print(ipv6.compressed)ip_address() elige IPv4 o IPv6 y lanza ValueError si la entrada no es válida. Cuando conoces la versión esperada y necesitas errores más específicos, utiliza IPv4Address o IPv6Address.
Valida datos del usuario
import ipaddress
def validar_ip(texto: str):
try:
return ipaddress.ip_address(texto.strip())
except ValueError as error:
raise ValueError("dirección IP inválida") from errorLas direcciones IPv4 con ceros iniciales, como 010.0.0.1, se rechazan para evitar ambigüedad con notación octal. No corrijas automáticamente entradas sospechosas porque una cadena inválida podría convertirse silenciosamente en otra dirección.
Dirección, red e interfaz
ip_address("10.0.0.5")representa una dirección.ip_network("10.0.0.0/24")representa un rango.ip_interface("10.0.0.5/24")conserva el host y muestra la red asociada.
from ipaddress import ip_interface
interfaz = ip_interface("10.0.0.5/24")
print(interfaz.ip)
print(interfaz.network)
print(interfaz.with_netmask)Modo strict en redes
Por defecto, ip_network() exige que los bits de host estén a cero:
from ipaddress import ip_network
red = ip_network("192.168.1.0/24")
normalizada = ip_network("192.168.1.42/24", strict=False)
print(normalizada)Usa strict=False solo cuando la normalización sea intencional y esté documentada. En reglas de seguridad, ocultar bits de host puede esconder un error de configuración.
Comprueba pertenencia
from ipaddress import ip_address, ip_network
red = ip_network("10.20.0.0/16")
cliente = ip_address("10.20.8.15")
if cliente in red:
print("dirección permitida")Los objetos IPv4 deben compararse con redes IPv4 y los IPv6 con redes IPv6. Mezclar versiones normalmente produce TypeError.
Propiedades importantes
from ipaddress import ip_address
ip = ip_address("127.0.0.1")
print(ip.is_loopback)
print(ip.is_private)
print(ip.is_global)
print(ip.is_multicast)
print(ip.is_link_local)
print(ip.reverse_pointer)reverse_pointer devuelve el nombre utilizado en una consulta PTR; no realiza la consulta DNS. Las clasificaciones is_private e is_global siguen los registros especiales de IANA y pueden corregirse entre versiones. Python 3.13 corrigió varias clasificaciones, por lo que no conviene reemplazar la biblioteca con listas antiguas.
Privado no significa confiable
Una dirección privada, loopback o link-local no demuestra identidad. En aplicaciones web, X-Forwarded-For puede falsificarse si la cadena de proxies no está controlada. Obtén la dirección efectiva desde una fuente confiable.
is_global describe alcance de direccionamiento, no reputación, propiedad, autenticación ni autorización.
Detalles de una red
from ipaddress import ip_network
red = ip_network("192.0.2.0/29")
print(red.network_address)
print(red.broadcast_address)
print(red.netmask)
print(red.hostmask)
print(red.num_addresses)
print(list(red.hosts()))En IPv4, hosts() suele excluir red y broadcast. En /31 incluye ambos extremos para enlaces punto a punto y en /32 devuelve un host. IPv6 tiene reglas distintas; no traslades suposiciones de broadcast de IPv4.
No materialices redes enormes
Una red IPv6 /64 contiene una cantidad impracticable de direcciones. Usa pertenencia, índices, propiedades y operaciones de subred en lugar de convertirla en lista.
from ipaddress import ip_network
red = ip_network("2001:db8::/64")
print(red.num_addresses)
print(red[0])
print(red[-1])Divide una red
from ipaddress import ip_network
red = ip_network("10.0.0.0/24")
for subred in red.subnets(new_prefix=26):
print(subred)
superred = red.supernet(new_prefix=22)El prefijo de una subred debe ser mayor y el de una superred debe ser menor.
Compara redes
from ipaddress import ip_network
principal = ip_network("10.0.0.0/8")
filial = ip_network("10.20.0.0/16")
segmento = ip_network("10.20.128.0/17")
print(filial.subnet_of(principal))
print(principal.supernet_of(segmento))
print(filial.overlaps(segmento))Estas operaciones ayudan a detectar reglas redundantes, conflictos de rutas y rangos superpuestos.
Excluye un rango
from ipaddress import ip_network
bloque = ip_network("192.0.2.0/28")
reservado = ip_network("192.0.2.4/30")
for restante in bloque.address_exclude(reservado):
print(restante)La red excluida debe estar totalmente contenida. El resultado cubre las direcciones restantes con redes CIDR.
Resume y colapsa
import ipaddress
inicio = ipaddress.ip_address("192.0.2.0")
fin = ipaddress.ip_address("192.0.2.130")
print(list(ipaddress.summarize_address_range(inicio, fin)))
redes = [
ipaddress.ip_network("10.0.0.0/25"),
ipaddress.ip_network("10.0.0.128/25"),
]
print(list(ipaddress.collapse_addresses(redes)))collapse_addresses() combina bloques adyacentes o redundantes. No mezcles IPv4 e IPv6.
Conversión a entero y bytes
from ipaddress import ip_address
ip = ip_address("192.0.2.1")
print(int(ip))
print(ip.packed)
print(str(ip))packed devuelve cuatro bytes para IPv4 y dieciséis para IPv6 en orden de red. Combínalo con struct en Python para cabeceras binarias, documentando versión y longitud.
IPv6 compacto, expandido y con scope
from ipaddress import IPv6Address
ip = IPv6Address("2001:db8::1")
print(ip.compressed)
print(ip.exploded)
local = IPv6Address("fe80::1234%eth0")
print(local.scope_id)Las direcciones link-local pueden incluir zone ID. La misma dirección numérica con ámbitos distintos no es igual. Convertir a entero elimina el ámbito, así que no lo hagas cuando la interfaz forme parte de la identidad.
IPv4 mapeado en IPv6
ipv4_mapped extrae el IPv4 contenido en direcciones como ::ffff:192.0.2.1. Las versiones recientes hacen que is_private e is_global sigan la semántica del IPv4 subyacente. Normaliza representaciones equivalentes para evitar bypass de políticas.
Construye una lista permitida
from ipaddress import ip_address, ip_network
PERMITIDAS = tuple(map(ip_network, [
"192.0.2.0/24",
"2001:db8:abcd::/48",
]))
def permitido(texto: str) -> bool:
direccion = ip_address(texto)
return any(
direccion.version == red.version and direccion in red
for red in PERMITIDAS
)Analiza las redes al iniciar, no en cada petición. configparser en Python puede leer configuración, pero las reglas críticas necesitan validación y revisión.
SSRF requiere varias defensas
ipaddress ayuda a bloquear loopback, privados y link-local, pero no resuelve SSRF por sí solo. Un hostname puede devolver varios IPs, cambiar entre validación y conexión o redirigir. Resuelve y valida todos los resultados, conecta a la dirección validada, restringe protocolos y puertos y vuelve a validar redirecciones. Analiza URLs con urllib.parse, no con concatenación.
Hash y ordenación
Direcciones y redes son inmutables y hashable, por lo que sirven como claves y elementos de sets. Las direcciones de la misma versión pueden ordenarse. Para mezclar direcciones y redes deliberadamente, usa get_mixed_type_key().
Observabilidad
Devuelve mensajes simples al usuario y registra un valor sanitizado, la regla coincidente y la versión IP. Para seguir validaciones complejas, usa trace en Python. Los lotes grandes pueden distribuirse con queue en Python, manteniendo las redes preanalizadas.
Pruebas recomendadas
Incluye IPv4 e IPv6 válidos, ceros iniciales, prefijos inválidos, bits de host en modo estricto, /31, /32, /127, /128, direcciones mapeadas, zone IDs, redes superpuestas, rangos enormes, espacios, Unicode confusable y cadenas demasiado largas.
Buenas prácticas
- Convierte cadenas en objetos una sola vez.
- Usa modo estricto en reglas críticas.
- No materialices redes enormes.
- No trates una IP privada como identidad.
- Soporta IPv4 e IPv6 deliberadamente.
- Normaliza representaciones equivalentes.
- Limita listas e intervalos.
- Combina validación IP con DNS, proxies y política de aplicación.
Conclusión
ipaddress en Python proporciona una base sólida para validar direcciones, calcular CIDR e implementar políticas consistentes en IPv4 e IPv6. Los objetos inmutables sustituyen manipulaciones frágiles de cadenas.
Consulta la documentación oficial de ipaddress y el registro especial de IANA. La clasificación de alcance ayuda, pero la autorización necesita contexto adicional.







