0%
التحقق من انتماء عنوان IP إلى شبكة في Python

التحقق من انتماء عنوان IP إلى شبكة في Python

تعلّم كيفية التحقق من انتماء عنوان IP إلى شبكة باستخدام وحدة ipaddress. أمثلة عملية لجدران الحماية وقوائم السماح وتصفية الشبكة.

I

InSkillCoach

· دقيقة

التحقق من انتماء عنوان IP إلى شبكة في Python

من المهام الشائعة في إدارة الشبكات وأمن التطبيقات تحديد ما إذا كان عنوان IP معيّن ينتمي إلى شبكة محددة. توفّر وحدة ipaddress في Python أدوات أنيقة ومتينة لهذا التحقق. وسواء كنت تنفّذ قائمة سماح أو جدار حماية تطبيقي أو آلية للتحقق من صلاحيات الوصول، سيوضح لك هذا الدليل كيفية إتقان هذه الميزة.

1. وحدة ipaddress

تُعدّ وحدة ipaddress جزءًا من المكتبة القياسية (stdlib) في Python منذ الإصدار 3.3. وهي توفّر فئات لتمثيل عناوين IP والشبكات والتعامل معها.

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. التحقق من الانتماء باستخدام العامل in

يختبر العامل in ما إذا كان عنوان IP ينتمي إلى شبكة.

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)

دالة قابلة لإعادة الاستخدام

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. معالجة الأخطاء

قد تكون مدخلات المستخدم غير صالحة، لذا يجب التقاط الاستثناءات.

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

المعامل strict=False

افتراضيًا، تشترط ip_network أن يكون عنوان الشبكة منطبقًا تمامًا على حدّ كتلة CIDR. أما مع strict=False، فيُعاد حساب عنوان الشبكة تلقائيًا.

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. مقارنة الشبكات

توفّر الوحدة دوال لمقارنة الشبكات.

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

تدعم الوحدة IPv6 أيضًا، وبناء الجملة هو نفسه.

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. حالات استخدام عملية

قائمة سماح لواجهة 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

التصفية في جدار حماية تطبيقي

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. أفضل الممارسات

  • التقط ValueError: فمدخلات الشبكة قد تكون غير صالحة دائمًا.
  • استخدم strict=False عندما تقبل مدخلات من المستخدم وتريد التساهل معها.
  • أنشئ كائنات الشبكة مرة واحدة فقط وأعد استخدامها (فكلفتها قليلة من حيث الذاكرة).
  • اختبر الحالات الحدّية: عنوان الشبكة، وعنوان البث (broadcast)، والعناوين الخاصة (localhost، وlink-local، وغيرها).
  • سجّل حالات الرفض لأغراض التدقيق والأمان.

الخلاصة

تبسّط وحدة ipaddress كثيرًا عملية التحقق من انتماء عنوان IP إلى شبكة. بفضل العامل in والمعامل strict=False ومعالجة الأخطاء بشكل متين، تمتلك كل الأدوات اللازمة لتنفيذ قوائم السماح ومرشحات الشبكة وسياسات الوصول. وسواء كان الهدف تأمين واجهة API أو إدارة جدار حماية تطبيقي أو تصفية حركة المرور، فإن هذه المهارة أساسية لكل مطوّر Python يعمل مع الشبكات.

InSkillCoach

نبذة عن InSkillCoach

خبير في التدريب والتقنيات

مدرّب متخصص في التقنيات المتقدمة والذكاء الاصطناعي، تدعمه شركة GNeurone Inc.

الشهادات:

  • AWS Certified Solutions Architect – Professional
  • شهادات Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.6k
97

التعليقات

التعليقات مدعومة عبر GitHub Discussions

سجّل الدخول باستخدام GitHub للمشاركة في النقاش

تم نسخ الرابط!