Comprobar si una dirección IP pertenece a una red en Python
Aprende a comprobar si una dirección IP pertenece a una red con el módulo ipaddress. Ejemplos prácticos para cortafuegos, listas de permitidos y filtrado de red.
InSkillCoach
Comprobar si una dirección IP pertenece a una red en Python
Una tarea habitual en la administración de redes y en la seguridad de aplicaciones es determinar si una dirección IP dada pertenece a una red concreta. El módulo ipaddress de Python ofrece herramientas elegantes y robustas para esta comprobación. Tanto si implementas una lista de permitidos, un cortafuegos de aplicación o una validación de acceso, esta guía te mostrará cómo dominar esta funcionalidad.
1. El módulo ipaddress
El módulo ipaddress está incluido en la biblioteca estándar de Python desde la versión 3.3. Proporciona clases para representar y manipular direcciones IP y redes.
import ipaddress
# Créer une adresse IP
ip = ipaddress.ip_address("192.168.1.100")
print(ip) # 192.168.1.100
print(type(ip)) # <class 'ipaddress.IPv4Address'>
# Créer un réseau
reseau = ipaddress.ip_network("192.168.1.0/24")
print(reseau) # 192.168.1.0/24
print(type(reseau)) # <class 'ipaddress.IPv4Network'>
2. Comprobar la pertenencia con el operador in
El operador in comprueba si una dirección IP pertenece a una red.
import ipaddress
reseau = ipaddress.ip_network("192.168.1.0/24")
# Tester si des adresses appartiennent au réseau
print("192.168.1.50" in reseau) # True
print("192.168.2.50" in reseau) # False
print("10.0.0.1" in reseau) # False
# Note : l'adresse réseau et l'adresse broadcast font partie du réseau
print("192.168.1.0" in reseau) # True (adresse réseau)
print("192.168.1.255" in reseau) # True (adresse broadcast)
Función reutilizable
import ipaddress
def is_ip_in_network(ip_string, network_string):
"""
Vérifie si une adresse IP appartient à un réseau.
Args:
ip_string (str): Adresse IP à vérifier (ex: "192.168.1.50")
network_string (str): Réseau CIDR (ex: "192.168.1.0/24")
Returns:
bool: True si l'IP est dans le réseau, False sinon
"""
try:
ip = ipaddress.ip_address(ip_string)
reseau = ipaddress.ip_network(network_string)
return ip in reseau
except ValueError as e:
print(f"Erreur : {e}")
return False
# Tests
print(is_ip_in_network("192.168.1.100", "192.168.1.0/24")) # True
print(is_ip_in_network("192.168.2.100", "192.168.1.0/24")) # False
print(is_ip_in_network("10.0.0.5", "10.0.0.0/8")) # True
3. Gestión de errores
La entrada del usuario puede ser inválida. Hay que capturar las excepciones.
import ipaddress
def verifier_ip_securisee(ip_string, network_string):
"""Vérification robuste avec gestion d'erreur."""
try:
ip = ipaddress.ip_address(ip_string)
reseau = ipaddress.ip_network(network_string, strict=False)
return ip in reseau
except ValueError as e:
print(f"Entrée invalide : {e}")
return False
# Tests d'erreur
print(verifier_ip_securisee("999.999.999.999", "192.168.1.0/24")) # ValueError
print(verifier_ip_securisee("192.168.1.a", "192.168.1.0/24")) # ValueError
print(verifier_ip_securisee("192.168.1.100", "192.168.1.0/999")) # ValueError
print(verifier_ip_securisee("192.168.1.100", "invalid")) # ValueError
# Résultat : tous affichent un message d'erreur et retournent False
Parámetro strict=False
Por defecto, ip_network exige que la red esté exactamente en el límite del bloque CIDR. Con strict=False, la dirección de red se recalcula automáticamente.
import ipaddress
# Strict (par défaut)
try:
# 192.168.1.100 n'est pas l'adresse réseau de /24
reseau = ipaddress.ip_network("192.168.1.100/24")
except ValueError as e:
print(f"Erreur (strict=True) : {e}")
# Erreur (strict=True) : 192.168.1.100/24 does not have the correct netmask
# Non-strict
reseau = ipaddress.ip_network("192.168.1.100/24", strict=False)
print(reseau) # 192.168.1.0/24 (corrigé automatiquement)
4. Comparación de redes
El módulo ofrece funciones para comparar redes.
import ipaddress
reseau_grand = ipaddress.ip_network("10.0.0.0/8")
reseau_petit = ipaddress.ip_network("10.1.0.0/16")
# Vérifier si un réseau est contenu dans un autre
print(reseau_petit.subnet_of(reseau_grand)) # True
print(reseau_grand.supernet_of(reseau_petit)) # True
# Vérifier l'égalité
reseau_copy = ipaddress.ip_network("10.0.0.0/8")
print(reseau_grand == reseau_copy) # True
5. IPv6
El módulo también gestiona IPv6. La sintaxis es idéntica.
import ipaddress
# IPv6
reseau_ipv6 = ipaddress.ip_network("2001:db8::/32")
print(reseau_ipv6) # 2001:db8::/32
# Tester l'appartenance
print("2001:db8::1" in reseau_ipv6) # True
print("2001:db9::1" in reseau_ipv6) # False
# Adresse link-local (spéciale)
ip_link_local = ipaddress.ip_address("fe80::1")
print(ip_link_local.is_link_local) # True
6. Casos de uso prácticos
Lista de permitidos para una API
import ipaddress
from flask import Flask, request, abort
app = Flask(__name__)
# IPs autorisées (interne + partenaires)
ALLOWED_NETWORKS = [
ipaddress.ip_network("192.168.0.0/16"), # Réseau interne
ipaddress.ip_network("10.0.0.0/8"), # Réseau principal
ipaddress.ip_network("203.0.113.0/24"), # Partenaire A
]
def is_ip_authorized(ip_string):
"""Vérifie si une IP est autorisée."""
try:
ip = ipaddress.ip_address(ip_string)
return any(ip in network for network in ALLOWED_NETWORKS)
except ValueError:
return False
@app.before_request
def check_ip():
"""Middleware : bloquer les IPs non autorisées."""
client_ip = request.remote_addr
if not is_ip_authorized(client_ip):
abort(403) # Accès refusé
@app.route("/api/data")
def get_data():
return {"data": "Secret"}
# Résultat : seules les IPs dans ALLOWED_NETWORKS accèdent à l'API
Filtrado en un cortafuegos de aplicación
import ipaddress
class SimpleFirewall:
def __init__(self):
self.blocked_networks = [
ipaddress.ip_network("203.0.113.0/24"), # Pays bloqué
ipaddress.ip_network("198.51.100.0/24"), # Spam connu
]
def is_blocked(self, ip_string):
"""Vérifie si une IP est bloquée."""
try:
ip = ipaddress.ip_address(ip_string)
return any(ip in network for network in self.blocked_networks)
except ValueError:
return False
def log_connection(self, ip_string, allowed):
status = "ALLOWED" if allowed else "BLOCKED"
print(f"{ip_string} : {status}")
firewall = SimpleFirewall()
test_ips = [
"192.168.1.1", # OK
"203.0.113.50", # Bloquée
"198.51.100.100", # Bloquée
]
for ip in test_ips:
allowed = not firewall.is_blocked(ip)
firewall.log_connection(ip, allowed)
7. Buenas prácticas
- Captura
ValueError: la entrada de red siempre puede ser inválida. - Utiliza
strict=Falsecuando aceptes entradas del usuario y quieras ser tolerante. - Crea los objetos de red una sola vez y reutilízalos (cuestan poca memoria).
- Prueba los casos límite: dirección de red, dirección de broadcast, direcciones especiales (localhost, link-local, etc.).
- Registra los rechazos con fines de auditoría y seguridad.
Conclusión
El módulo ipaddress simplifica enormemente la comprobación de pertenencia de una IP a una red. Gracias al operador in, al parámetro strict=False y a una gestión de errores robusta, dispones de todas las herramientas necesarias para implementar listas de permitidos, filtros de red y políticas de acceso. Ya sea para proteger una API, gestionar un cortafuegos de aplicación o filtrar tráfico, esta competencia es esencial para cualquier desarrollador Python que trabaje con redes.
Acerca de InSkillCoach
Experto en formación y tecnologías
Coach especializado en tecnologías avanzadas e IA, impulsado por GNeurone Inc.
Certificaciones:
- AWS Certified Solutions Architect – Professional
- Certificaciones de Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comentarios
Los comentarios funcionan con GitHub Discussions
Inicia sesión con GitHub para participar en la discusión