Utilisation de SO_REUSEADDR et de SO_EXCLUSIVEADDRUSE
Le développement d’une infrastructure réseau sécurisée de haut niveau est une priorité pour la plupart des développeurs d’applications réseau. Toutefois, la sécurité des sockets est souvent ignorée bien qu’elle soit très critique lors de l’examen d’une solution entièrement sécurisée. La sécurité des sockets, en particulier, traite des processus qui se lient au même port précédemment lié par un autre processus d’application. Dans le passé, il était possible pour une application réseau de « pirater » le port d’une autre application, ce qui pouvait facilement entraîner une attaque par déni de service ou un vol de données.
En général, la sécurité des sockets s’applique aux processus côté serveur. Plus précisément, la sécurité du socket s’applique à toute application réseau qui accepte les connexions et reçoit le trafic de datagramme IP. Ces applications se lient généralement à un port connu et sont des cibles courantes pour le code réseau malveillant.
Les applications clientes sont moins susceptibles d’être la cible de telles attaques, non pas parce qu’elles sont moins sensibles, mais parce que la plupart des clients se lient à des ports locaux « éphémères » plutôt qu’à des ports « de service » statiques. Les applications réseau clientes doivent toujours être liées à des ports éphémères (en spécifiant le port 0 dans la structure SOCKADDR pointée par le paramètre name lors de l’appel de la fonction de liaison ), sauf s’il existe une raison architecturale convaincante de ne pas le faire. Les ports locaux éphémères se composent de ports supérieurs au port 49151. La plupart des applications serveur pour les services dédiés se lient à un port réservé connu inférieur ou égal au port 49151. Par conséquent, pour la plupart des applications, il n’y a généralement pas de conflit pour les demandes de liaison entre les applications clientes et serveurs.
Cette section décrit le niveau de sécurité par défaut sur différentes plateformes Microsoft Windows et la façon dont les options de socket spécifiques SO_REUSEADDR et SO_EXCLUSIVEADDRUSE impactent et affectent la sécurité des applications réseau. Une fonctionnalité supplémentaire appelée sécurité de socket renforcée est disponible sur Windows Server 2003 et versions ultérieures. La disponibilité de ces options de socket et l’amélioration de la sécurité des sockets varient selon les versions des systèmes d’exploitation Microsoft, comme indiqué dans le tableau ci-dessous.
Plateforme | SO_REUSEADDR | SO_EXCLUSIVEADDRUSE | Sécurité améliorée des sockets |
---|---|---|---|
Windows 95 | Disponible | Non disponible | Non disponible |
Windows 98 | Disponible | Non disponible | Non disponible |
Windows Me | Disponible | Non disponible | Non disponible |
Windows NT 4.0 | Disponible | Disponible dans Service Pack 4 et versions ultérieures | Non disponible |
Windows 2000 | Disponible | Disponible | Non disponible |
Windows XP | Disponible | Disponible | Non disponible |
Windows Server 2003 | Disponible | Disponible | Disponible |
Windows Vista | Disponible | Disponible | Disponible |
Windows Server 2008 | Disponible | Disponible | Disponible |
Windows 7 et ultérieur | Disponible | Disponible | Disponible |
Utilisation de SO_REUSEADDR
L’option de socket SO_REUSEADDR permet à un socket de se lier de force à un port utilisé par un autre socket. Le deuxième socket appelle setsockopt avec le paramètre optname défini sur SO_REUSEADDR et le paramètre optval défini sur une valeur booléenne TRUE avant d’appeler bind sur le même port que le socket d’origine. Une fois le deuxième socket correctement lié, le comportement de tous les sockets liés à ce port est indéterminé. Par exemple, si tous les sockets sur le même port fournissent un service TCP, les demandes de connexion TCP entrantes sur le port ne peuvent pas être gérées par le socket correct. Le comportement n’est pas déterministe. Un programme malveillant peut utiliser SO_REUSEADDR pour lier de force des sockets déjà utilisés pour les services de protocole réseau standard afin de refuser l’accès à ces services. Aucun privilège spécial n’est requis pour utiliser cette option.
Si une application cliente se lie à un port avant qu’une application serveur ne puisse se lier au même port, des problèmes peuvent survenir. Si l’application serveur lie de force à l’aide de l’option de socket SO_REUSEADDR au même port, le comportement de tous les sockets liés à ce port est indéterminé.
L’exception à ce comportement non déterministe est les sockets de multidiffusion. Si deux sockets sont liés à la même interface et au même port et sont membres du même groupe de multidiffusion, les données sont remises aux deux sockets, plutôt qu’à un autre choisi arbitrairement.
Utilisation de SO_EXCLUSIVEADDRUSE
Avant l’introduction de l’option de socket SO_EXCLUSIVEADDRUSE , il y avait très peu de choses qu’un développeur d’applications réseau pouvait faire pour empêcher un programme malveillant de se lier au port sur lequel l’application réseau avait ses propres sockets liés. Pour résoudre ce problème de sécurité, les sockets Windows ont introduit l’option de socket SO_EXCLUSIVEADDRUSE , qui est devenue disponible sur Windows NT 4.0 avec Service Pack 4 (SP4) et versions ultérieures.
L’option de socket SO_EXCLUSIVEADDRUSE ne peut être utilisée que par les membres du groupe de sécurité Administrateurs sur Windows XP et les versions antérieures. Les raisons pour lesquelles cette exigence a été modifiée sur Windows Server 2003 et versions ultérieures sont décrites plus loin dans l’article.
L’option SO_EXCLUSIVEADDRUSE est définie en appelant la fonction setsockopt avec le paramètre optname défini sur SO_EXCLUSIVEADDRUSE et le paramètre optval défini sur une valeur booléenne TRUE avant que le socket ne soit lié. Une fois l’option définie, le comportement des appels de liaison suivants diffère en fonction de l’adresse réseau spécifiée dans chaque appel de liaison .
Le tableau ci-dessous décrit le comportement qui se produit dans Windows XP et les versions antérieures lorsqu’un deuxième socket tente de se lier à une adresse précédemment liée par un premier socket à l’aide d’options de socket spécifiques.
Notes
Dans le tableau ci-dessous, « caractère générique » désigne l’adresse générique pour le protocole donné (par exemple, « 0.0.0.0 » pour IPv4 et « : : » pour IPv6). « Spécifique » désigne une adresse IP spécifique affectée à une interface. Les cellules de table indiquent si la liaison réussit ou non (« Réussite ») ou si une erreur est retournée (« INUSE » pour l’erreur WSAEADDRINUSE ; « ACCESS » pour l’erreur WSAEACCES ).
Premier appel de liaison | Deuxième appel de liaison | ||||||
Default | SO_REUSEADDR | SO_EXCLUSIVEADDRUSE | |||||
Caractère générique | Spécifique | Caractère générique | Spécifique | Caractère générique | Spécifique | ||
Default | Caractère générique | INUSE | INUSE | Succès | Opération réussie | INUSE | INUSE |
Spécifique | INUSE | INUSE | Succès | Opération réussie | INUSE | INUSE | |
SO_REUSEADDR | Caractère générique | INUSE | INUSE | Succès | Opération réussie | INUSE | INUSE |
Spécifique | INUSE | INUSE | Succès | Opération réussie | INUSE | INUSE | |
SO_EXCLUSIVEADDRUSE | Caractère générique | INUSE | INUSE | ACCESS | ACCESS | INUSE | INUSE |
Spécifique | INUSE | INUSE | ACCESS | ACCESS | INUSE | INUSE |
Lorsque deux sockets sont liés au même numéro de port, mais sur des interfaces explicites différentes, il n’y a pas de conflit. Par exemple, dans le cas où un ordinateur a deux interfaces IP, 10.0.0.1 et 10.99.99.99, si le premier appel à la liaison est sur 10.0.0.1 avec le port défini sur 5150 et SO_EXCLUSIVEADDRUSE spécifié, un deuxième appel à lier sur 10.99.99.99 avec le port également défini sur 5150 et aucune option spécifiée ne réussit. Toutefois, si le premier socket est lié à l’adresse générique et au port 5150, tout appel de liaison suivant au port 5150 avec SO_EXCLUSIVEADDRUSE défini échoue avec WSAEADDRINUSE ou WSAEACCES retourné par l’opération de liaison .
Dans le cas où le premier appel à la liaison définit SO_REUSEADDR ou aucune option de socket, le deuxième appel de liaison « détourne » le port et l’application ne peut pas déterminer lequel des deux sockets a reçu des paquets spécifiques envoyés au port « partagé ».
Une application classique qui appelle la fonction bind n’alloue pas le socket lié pour une utilisation exclusive, sauf si l’option de socket SO_EXCLUSIVEADDRUSE est appelée sur le socket avant l’appel à la fonction de liaison . Si une application cliente se lie à un port éphémère ou à un port spécifique avant qu’une application serveur ne se lie au même port, des problèmes peuvent survenir. L’application serveur peut lier de force au même port à l’aide de l’option de socket SO_REUSEADDR sur le socket avant d’appeler la fonction de liaison , mais le comportement de tous les sockets liés à ce port est alors indéterminé. Si l’application serveur tente d’utiliser l’option de socket SO_EXCLUSIVEADDRUSE pour une utilisation exclusive du port, la demande échoue.
À l’inverse, un socket avec le SO_EXCLUSIVEADDRUSE défini ne peut pas nécessairement être réutilisé immédiatement après la fermeture du socket. Par exemple, si un socket d’écoute avec SO_EXCLUSIVEADDRUSE défini accepte une connexion et est ensuite fermé, un autre socket (également avec SO_EXCLUSIVEADDRUSE) ne peut pas se lier au même port que le premier socket tant que la connexion d’origine n’est pas inactive.
Ce problème peut devenir compliqué, car le protocole de transport sous-jacent peut ne pas arrêter la connexion même si le socket a été fermé. Même une fois le socket fermé par l’application, le système doit transmettre toutes les données mises en mémoire tampon, envoyer un message de déconnexion gracieux à l’homologue et attendre un message de déconnexion correct de l’homologue correspondant. Il est possible que le protocole de transport sous-jacent ne libère jamais la connexion ; par exemple, l’homologue participant à la connexion d’origine peut publier une fenêtre de taille nulle ou une autre forme de configuration « d’attaque ». Dans ce cas, la connexion client reste active malgré la demande de fermeture, car les données non reconnues restent dans la mémoire tampon.
Pour éviter cette situation, les applications réseau doivent garantir un arrêt normal en appelant l’arrêt avec l’indicateur SD_SEND défini, puis attendre dans une boucle recv jusqu’à ce que zéro octet soit retourné sur la connexion. Cela garantit que toutes les données sont reçues par l’homologue et confirme également avec l’homologue qu’il a reçu toutes les données transmises, ainsi que d’éviter le problème de réutilisation de port mentionné ci-dessus.
L’option de socket SO_LINGER peut être définie sur un socket pour empêcher le port de passer à un état d’attente « actif » ; toutefois, cela est déconseillé, car cela peut entraîner des effets indésirables, tels que la réinitialisation des connexions. Par exemple, si les données sont reçues par l’homologue, mais qu’elles ne sont pas reconnues, et que l’ordinateur local ferme le socket avec SO_LINGER défini dessus, la connexion entre les deux ordinateurs est réinitialisée et les données non reconnues sont ignorées par l’homologue. Il est difficile de choisir un délai approprié pour s’attarder, car une valeur de délai d’attente plus petite entraîne souvent l’abandon soudain des connexions, tandis que les valeurs de délai d’expiration plus importantes laissent le système vulnérable aux attaques par déni de service (en établissant de nombreuses connexions et en bloquant potentiellement les threads d’application). La fermeture d’un socket dont la valeur de délai d’expiration est différente de zéro peut également entraîner le blocage de l’appel closesocket .
Sécurité améliorée des sockets
Une sécurité renforcée des sockets a été ajoutée avec la version de Windows Server 2003. Dans les versions précédentes du système d’exploitation du serveur Microsoft, la sécurité de socket par défaut permettait facilement aux processus de détourner les ports d’applications sans se méfier. Dans Windows Server 2003, les sockets ne sont pas dans un état partageable par défaut. Par conséquent, si une application souhaite autoriser d’autres processus à réutiliser un port sur lequel un socket est déjà lié, elle doit l’activer spécifiquement. Si tel est le cas, le premier socket à appeler bind sur le port doit avoir SO_REUSEADDR défini sur le socket. La seule exception à ce cas se produit lorsque le deuxième appel de liaison est effectué par le même compte d’utilisateur qui a effectué l’appel d’origine à lier. Cette exception existe uniquement pour assurer la compatibilité descendante.
Le tableau ci-dessous décrit le comportement qui se produit dans les systèmes d’exploitation Windows Server 2003 et ultérieur lorsqu’un deuxième socket tente de se lier à une adresse précédemment liée par un premier socket à l’aide d’options de socket spécifiques.
Notes
Dans le tableau ci-dessous, « caractère générique » désigne l’adresse générique pour le protocole donné (par exemple, « 0.0.0.0 » pour IPv4 et « : : » pour IPv6). « Spécifique » désigne une adresse IP spécifique affectée à une interface. Les cellules de tableau indiquent si la liaison réussit ou non (« Réussite ») ou si l’erreur retournée (« INUSE » pour l’erreur WSAEADDRINUSE ; « ACCESS » pour l’erreur WSAEACCES ).
Notez également que dans cette table spécifique, les deux appels de liaison sont effectués sous le même compte d’utilisateur.
Premier appel de liaison | Deuxième appel de liaison | ||||||
Default | SO_REUSEADDR | SO_EXCLUSIVEADDRUSE | |||||
Caractère générique | Spécifique | Caractère générique | Spécifique | Caractère générique | Spécifique | ||
Default | Caractère générique | INUSE | Succès | ACCESS | Succès | INUSE | Succès |
Spécifique | Succès | INUSE | Succès | ACCESS | INUSE | INUSE | |
SO_REUSEADDR | Caractère générique | INUSE | Succès | Opération réussie | Opération réussie | INUSE | Succès |
Spécifique | Succès | INUSE | Succès | Opération réussie | INUSE | INUSE | |
SO_EXCLUSIVEADDRUSE | Caractère générique | INUSE | ACCESS | ACCESS | ACCESS | INUSE | ACCESS |
Spécifique | Succès | INUSE | Succès | ACCESS | INUSE | INUSE |
Quelques entrées dans le tableau ci-dessus explication du mérite.
Par exemple, si le premier appelant définit SO_EXCLUSIVEADDRUSE sur une adresse spécifique et que le deuxième appelant tente d’appeler bind avec une adresse générique sur le même port, le deuxième appel de liaison réussit. Dans ce cas particulier, le deuxième appelant est lié à toutes les interfaces, à l’exception de l’adresse spécifique à laquelle le premier appelant est lié. Notez que l’inverse de ce cas n’est pas vrai : si le premier appelant définit SO_EXCLUSIVEADDRUSE et que les appels sont liés avec l’indicateur générique, le deuxième appelant ne peut pas appeler bind avec le même port.
Le comportement de la liaison de socket change lorsque les appels de liaison de socket sont effectués sous différents comptes d’utilisateur. Le tableau ci-dessous spécifie le comportement qui se produit dans les systèmes d’exploitation Windows Server 2003 et ultérieur lorsqu’un deuxième socket tente de se lier à une adresse précédemment liée à par un premier socket à l’aide d’options de socket spécifiques et d’un autre compte d’utilisateur.
Premier appel de liaison | Deuxième appel de liaison | ||||||
Default | SO_REUSEADDR | SO_EXCLUSIVEADDRUSE | |||||
Caractère générique | Spécifique | Caractère générique | Spécifique | Caractère générique | Spécifique | ||
Default | Caractère générique | INUSE | ACCESS | ACCESS | ACCESS | INUSE | ACCESS |
Spécifique | Succès | INUSE | Succès | ACCESS | INUSE | INUSE | |
SO_REUSEADDR | Caractère générique | INUSE | ACCESS | Succès | Opération réussie | INUSE | ACCESS |
Spécifique | Succès | INUSE | Succès | Opération réussie | INUSE | INUSE | |
SO_EXCLUSIVEADDRUSE | Caractère générique | INUSE | ACCESS | ACCESS | ACCESS | INUSE | ACCESS |
Spécifique | Succès | INUSE | Succès | ACCESS | INUSE | INUSE |
Notez que le comportement par défaut est différent lorsque les appels de liaison sont effectués sous des comptes d’utilisateur différents. Si le premier appelant ne définit aucune option sur le socket et se lie à l’adresse générique, le deuxième appelant ne peut pas définir l’option SO_REUSEADDR et lier correctement au même port. Le comportement par défaut sans jeu d’options retourne également une erreur.
Sur Windows Vista et versions ultérieures, un socket double pile peut être créé qui fonctionne à la fois sur IPv6 et IPv4. Lorsqu’un socket double pile est lié à l’adresse générique, le port donné est réservé sur les piles réseau IPv4 et IPv6, et les vérifications associées à SO_REUSEADDR et SO_EXCLUSIVEADDRUSE (le cas échéant) sont effectuées. Ces vérifications doivent réussir sur les deux piles réseau. Par exemple, si un socket TCP à double pile définit SO_EXCLUSIVEADDRUSE puis tente de le lier au port 5000, aucun autre socket TCP ne peut être lié au port 5000 (générique ou spécifique). Dans ce cas, si un socket TCP IPv4 était précédemment lié à l’adresse de bouclage sur le port 5000, l’appel de liaison pour le socket double pile échoue avec WSAEACCES.
Stratégies d’application
Lorsque vous développez une application réseau qui fonctionne au niveau de la couche socket, il est important de prendre en compte le type de sécurité de socket nécessaire. Les applications clientes ( applications qui se connectent ou envoient des données à un service ) nécessitent rarement des étapes supplémentaires, car elles se lient à un port local aléatoire (éphémère). Si le client nécessite une liaison de port local spécifique pour fonctionner correctement, vous devez prendre en compte la sécurité du socket.
L’option SO_REUSEADDR a très peu d’utilisations dans les applications normales à l’exception des sockets multidiffusion où les données sont remises à tous les sockets liés sur le même port. Sinon, toute application qui définit cette option de socket doit être repensée pour supprimer la dépendance, car elle est éminemment vulnérable au « détournement de socket ». Tant que SO_REUSEADDR option de socket peut être utilisée pour potentiellement pirater un port dans une application serveur, l’application doit être considérée comme non sécurisée.
Toutes les applications serveur doivent définir SO_EXCLUSIVEADDRUSE pour un niveau élevé de sécurité des sockets. Non seulement il empêche les logiciels malveillants de détourner le port, mais il indique également si une autre application est liée ou non au port demandé. Par exemple, un appel à lier sur l’adresse générique par un processus avec l’option de socket SO_EXCLUSIVEADDRUSE définie échoue si un autre processus est actuellement lié au même port sur une interface spécifique.
Enfin, même si la sécurité du socket a été améliorée dans Windows Server 2003, une application doit toujours définir l’option de socket SO_EXCLUSIVEADDRUSE pour s’assurer qu’elle est liée à toutes les interfaces spécifiques demandées par le processus. La sécurité du socket dans Windows Server 2003 ajoute un niveau de sécurité accru pour les applications héritées, mais les développeurs d’applications doivent toujours concevoir leurs produits en tenant compte de tous les aspects de la sécurité.