Share via


Uso de SO_REUSEADDR y SO_EXCLUSIVEADDRUSE

El desarrollo de una infraestructura de red segura de alto nivel es una prioridad para la mayoría de los desarrolladores de aplicaciones de red. Sin embargo, a menudo se pasa por alto la seguridad del socket a pesar de ser muy crítico al considerar una solución totalmente segura. La seguridad del socket, en concreto, se ocupa de los procesos que se enlazan al mismo puerto enlazado previamente por otro proceso de aplicación. En el pasado, era posible que una aplicación de red "secuestre" el puerto de otra aplicación, lo que podría conducir fácilmente a un ataque de "denegación de servicio" o robo de datos.

En general, la seguridad del socket se aplica a los procesos del lado servidor. Más concretamente, la seguridad del socket se aplica a cualquier aplicación de red que acepte conexiones y reciba tráfico de datagramas IP. Normalmente, estas aplicaciones se enlazan a un puerto conocido y son destinos comunes para el código de red malintencionado.

Es menos probable que las aplicaciones cliente sean los destinos de estos ataques, no porque son menos susceptibles, sino porque la mayoría de los clientes se enlazan a puertos locales "efímeros" en lugar de a puertos "servicio" estáticos. Las aplicaciones de red cliente siempre deben enlazarse a puertos efímeros (especificando el puerto 0 en la estructura SOCKADDR a la que apunta el parámetro name al llamar a la función de enlace ), a menos que haya una razón arquitectónica atractiva para no hacerlo. Los puertos locales efímeros constan de puertos mayores que el puerto 49151. La mayoría de las aplicaciones de servidor para servicios dedicados se enlazan a un puerto reservado conocido que es menor o igual que el puerto 49151. Por lo tanto, para la mayoría de las aplicaciones, normalmente no hay un conflicto para las solicitudes de enlace entre las aplicaciones cliente y servidor.

En esta sección se describe el nivel de seguridad predeterminado en varias plataformas de Microsoft Windows y cómo las opciones de socket específicas SO_REUSEADDR y SO_EXCLUSIVEADDRUSE afectan a la seguridad de las aplicaciones de red. Hay disponible una característica adicional denominada seguridad de socket mejorada en Windows Server 2003 y versiones posteriores. La disponibilidad de estas opciones de socket y la seguridad mejorada del socket varía en todas las versiones de los sistemas operativos Microsoft, como se muestra en la tabla siguiente.

Plataforma SO_REUSEADDR SO_EXCLUSIVEADDRUSE Seguridad mejorada del socket
Windows 95 Disponible No disponible No disponible
Windows 98 Disponible No disponible No disponible
Windows Me Disponible No disponible No disponible
Windows NT 4.0 Disponible Disponible en Service Pack 4 y versiones posteriores No disponible
Windows 2000 Disponible Disponible No disponible
Windows XP Disponible Disponible No disponible
Windows Server 2003 Disponible Disponible Disponible
Windows Vista Disponible Disponible Disponible
Windows Server 2008 Disponible Disponible Disponible
Windows 7 y más recientes Disponible Disponible Disponible

Uso de SO_REUSEADDR

La opción de socket SO_REUSEADDR permite que un socket se vincule forzosamente a un puerto en uso por otro socket. El segundo socket llama a setsockopt con el parámetro optname establecido en SO_REUSEADDR y el parámetro optval establecido en un valor booleano de TRUE antes de llamar al enlace en el mismo puerto que el socket original. Una vez que el segundo socket se ha enlazado correctamente, el comportamiento de todos los sockets enlazados a ese puerto es indeterminado. Por ejemplo, si todos los sockets del mismo puerto proporcionan un servicio TCP, no se puede garantizar que el socket correcto controle las solicitudes de conexión TCP entrantes a través del puerto, el comportamiento no es determinista. Un programa malintencionado puede usar SO_REUSEADDR para enlazar forzadamente sockets que ya están en uso para los servicios de protocolo de red estándar para denegar el acceso a esos servicios. No se requieren privilegios especiales para usar esta opción.

Si una aplicación cliente se enlaza a un puerto antes de que una aplicación de servidor pueda enlazarse al mismo puerto, pueden producirse problemas. Si la aplicación de servidor enlaza forzosamente mediante la opción de socket SO_REUSEADDR al mismo puerto, el comportamiento de todos los sockets enlazados a ese puerto es indeterminado.

La excepción a este comportamiento no determinista es los sockets de multidifusión. Si dos sockets están enlazados a la misma interfaz y puerto y son miembros del mismo grupo de multidifusión, los datos se entregarán a ambos sockets, en lugar de a uno elegido arbitrariamente.

Uso de SO_EXCLUSIVEADDRUSE

Antes de que se introdujera la opción de socket SO_EXCLUSIVEADDRUSE , había muy poco que un desarrollador de aplicaciones de red podía hacer para evitar que un programa malintencionado se enlazara al puerto en el que la aplicación de red tenía sus propios sockets enlazados. Para solucionar este problema de seguridad, Windows Sockets introdujo la opción de socket SO_EXCLUSIVEADDRUSE , que se puso a disposición en Windows NT 4.0 con Service Pack 4 (SP4) y versiones posteriores.

Los miembros del grupo de seguridad Administradores de Windows XP y versiones anteriores solo pueden usar la opción de socket SO_EXCLUSIVEADDRUSE . Las razones por las que se cambió este requisito en Windows Server 2003 y versiones posteriores se describen más adelante en el artículo.

La opción SO_EXCLUSIVEADDRUSE se establece llamando a la función setsockopt con el parámetro optname establecido en SO_EXCLUSIVEADDRUSE y el parámetro optval establecido en un valor booleano de TRUE antes de enlazar el socket. Una vez establecida la opción, el comportamiento de las llamadas de enlace posteriores difiere en función de la dirección de red especificada en cada llamada de enlace .

En la tabla siguiente se describe el comportamiento que se produce en Windows XP y versiones anteriores cuando un segundo socket intenta enlazar a una dirección enlazada previamente a mediante un primer socket mediante opciones de socket específicas.

Nota

En la tabla siguiente, "comodín" indica la dirección comodín del protocolo especificado (como "0.0.0.0.0" para IPv4 y "::" para IPv6). "Específico" indica una dirección IP específica asignada a una interfaz. Las celdas de la tabla indican si el enlace es correcto ("Correcto") o si se devuelve un error ("INUSE" para el error WSAEADDRINUSE ; "ACCESS" para el error WSAEACCES ).

Primera llamada de enlace Segunda llamada de enlace
Default SO_REUSEADDR SO_EXCLUSIVEADDRUSE
Wildcard (Carácter comodín) Específico Wildcard (Carácter comodín) Específico Wildcard (Carácter comodín) Específico
Default Wildcard (Carácter comodín) INUSE INUSE Correcto Correcto INUSE INUSE
Específico INUSE INUSE Correcto Correcto INUSE INUSE
SO_REUSEADDR Wildcard (Carácter comodín) INUSE INUSE Correcto Correcto INUSE INUSE
Específico INUSE INUSE Correcto Correcto INUSE INUSE
SO_EXCLUSIVEADDRUSE Wildcard (Carácter comodín) INUSE INUSE ACCESS ACCESS INUSE INUSE
Específico INUSE INUSE ACCESS ACCESS INUSE INUSE

Cuando dos sockets están enlazados al mismo número de puerto, pero en interfaces explícitas diferentes, no hay ningún conflicto. Por ejemplo, en el caso de que un equipo tenga dos interfaces IP, 10.0.0.1 y 10.99.99.99, si la primera llamada a bind está en la 10.0.0.1 con el puerto establecido en 5150 y SO_EXCLUSIVEADDRUSE especificado, una segunda llamada para enlazar en 10.99.99.99 con el puerto también establecido en 5150 y ninguna opción especificada se realizará correctamente. Sin embargo, si el primer socket está enlazado a la dirección de comodín y al puerto 5150, cualquier llamada de enlace posterior al puerto 5150 con SO_EXCLUSIVEADDRUSE establecido producirá un error con WSAEADDRINUSE o WSAEACCES devuelto por la operación de enlace .

En caso de que la primera llamada a enlace establezca SO_REUSEADDR o ninguna opción de socket en absoluto, la segunda llamada de enlace "secuestrará" el puerto y la aplicación no podrá determinar cuál de los dos sockets recibió paquetes específicos enviados al puerto "compartido".

Una aplicación típica que llama a la función de enlace no asigna el socket enlazado para uso exclusivo, a menos que se llame a la opción de socket SO_EXCLUSIVEADDRUSE en el socket antes de llamar a la función bind . Si una aplicación cliente se enlaza a un puerto efímero o a un puerto específico antes de que una aplicación de servidor se enlace al mismo puerto, pueden producirse problemas. La aplicación de servidor puede enlazar forzosamente al mismo puerto mediante la opción de socket SO_REUSEADDR en el socket antes de llamar a la función de enlace , pero el comportamiento de todos los sockets enlazados a ese puerto es indeterminado. Si la aplicación de servidor intenta usar la opción de socket SO_EXCLUSIVEADDRUSE para el uso exclusivo del puerto, se producirá un error en la solicitud.

Por el contrario, un socket con el conjunto de SO_EXCLUSIVEADDRUSE no se puede reutilizar necesariamente inmediatamente después del cierre del socket. Por ejemplo, si un socket de escucha con SO_EXCLUSIVEADDRUSE conjunto acepta una conexión y, a continuación, se cierra, otro socket (también con SO_EXCLUSIVEADDRUSE) no se puede enlazar al mismo puerto que el primer socket hasta que la conexión original quede inactiva.

Este problema puede resultar complicado porque es posible que el protocolo de transporte subyacente no finalice la conexión aunque se haya cerrado el socket. Incluso después de que la aplicación haya cerrado el socket, el sistema debe transmitir los datos almacenados en búfer, enviar un mensaje de desconexión estable al mismo nivel y esperar un mensaje de desconexión correcto correspondiente del mismo nivel. Es posible que el protocolo de transporte subyacente nunca libere la conexión; por ejemplo, el elemento del mismo nivel que participa en la conexión original podría anunciar una ventana de tamaño cero o alguna otra forma de configuración de "ataque". En tal caso, la conexión de cliente permanece en un estado activo a pesar de la solicitud de cerrarla, ya que los datos no reconocidos permanecen en el búfer.

Para evitar esta situación, las aplicaciones de red deben garantizar un apagado correcto llamando al apagado con la marca SD_SEND establecida y, a continuación, esperar en un bucle recv hasta que se devuelvan cero bytes a través de la conexión. Esto garantiza que el par recibe todos los datos y, del mismo modo, confirma con el mismo nivel que ha recibido todos los datos transmitidos, así como evitar el problema de reutilización del puerto mencionado anteriormente.

La opción de socket SO_LINGER puede establecerse en un socket para evitar que el puerto pase a un estado de espera "activo"; sin embargo, esto se desaconseja, ya que puede provocar efectos no deseados, como restablecer conexiones. Por ejemplo, si el elemento del mismo nivel recibe los datos, pero sigue sin saberlo, y el equipo local cierra el socket con SO_LINGER establecido en él, la conexión entre los dos equipos se restablece y los datos no reconocidos descartados por el mismo nivel. La selección de un tiempo de espera adecuado para persistir es difícil, ya que un valor de tiempo de espera más pequeño suele dar lugar a conexiones anuladas repentinamente, mientras que los valores de tiempo de espera mayores dejan al sistema vulnerable a los ataques por denegación de servicio (estableciendo muchas conexiones y subprocesos de aplicación potencialmente detenidos o bloqueando). Cerrar un socket que tenga un valor de tiempo de espera persistente distinto de cero también puede provocar que se bloquee la llamada a Closesocket .

Seguridad mejorada del socket

Se agregó la seguridad mejorada del socket con la versión de Windows Server 2003. En versiones anteriores del sistema operativo del servidor de Microsoft, la seguridad de socket predeterminada permite fácilmente los procesos para secuestrar puertos de aplicaciones no especificadas. En Windows Server 2003, los sockets no están en un estado que se puede compartir de forma predeterminada. Por lo tanto, si una aplicación quiere permitir que otros procesos vuelvan a usar un puerto en el que ya está enlazado un socket, debe habilitarlo específicamente. Si es así, el primer socket al que llamar al enlace en el puerto debe tener SO_REUSEADDR establecido en el socket. La única excepción a este caso se produce cuando la segunda llamada de enlace se realiza mediante la misma cuenta de usuario que realizó la llamada original para enlazar. Esta excepción existe únicamente para proporcionar compatibilidad con versiones anteriores.

En la tabla siguiente se describe el comportamiento que se produce en los sistemas operativos Windows Server 2003 y versiones posteriores cuando un segundo socket intenta enlazar a una dirección enlazada previamente a un primer socket mediante opciones de socket específicas.

Nota

En la tabla siguiente, "comodín" indica la dirección comodín del protocolo especificado (como "0.0.0.0.0" para IPv4 y "::" para IPv6). "Específico" indica una dirección IP específica asignada a una interfaz. Las celdas de la tabla indican si el enlace es correcto ("Correcto") o el error devuelto ("INUSE" para el error WSAEADDRINUSE ; "ACCESS" para el error WSAEACCES ).

Tenga en cuenta también que, en esta tabla específica, ambas llamadas de enlace se realizan en la misma cuenta de usuario.

Primera llamada de enlace Segunda llamada de enlace
Default SO_REUSEADDR SO_EXCLUSIVEADDRUSE
Wildcard (Carácter comodín) Específico Wildcard (Carácter comodín) Específico Wildcard (Carácter comodín) Específico
Default Wildcard (Carácter comodín) INUSE Correcto ACCESS Correcto INUSE Correcto
Específico Correcto INUSE Correcto ACCESS INUSE INUSE
SO_REUSEADDR Wildcard (Carácter comodín) INUSE Correcto Correcto Correcto INUSE Correcto
Específico Correcto INUSE Correcto Correcto INUSE INUSE
SO_EXCLUSIVEADDRUSE Wildcard (Carácter comodín) INUSE ACCESS ACCESS ACCESS INUSE ACCESS
Específico Correcto INUSE Correcto ACCESS INUSE INUSE

Algunas entradas de la tabla anterior merecen explicación.

Por ejemplo, si el primer llamador establece SO_EXCLUSIVEADDRUSE en una dirección específica y el segundo llamador intenta llamar a enlazar con una dirección comodín en el mismo puerto, la segunda llamada de enlace se realizará correctamente. En este caso concreto, el segundo autor de la llamada se enlaza a todas las interfaces, excepto la dirección específica a la que está enlazado el primer llamador. Tenga en cuenta que la inversa de este caso no es true: si el primer autor de la llamada establece SO_EXCLUSIVEADDRUSE y llama a enlazar con la marca de caracteres comodín, el segundo llamador no puede llamar al enlace con el mismo puerto.

El comportamiento del enlace de socket cambia cuando las llamadas de enlace de socket se realizan en cuentas de usuario diferentes. En la tabla siguiente se especifica el comportamiento que se produce en windows Server 2003 y sistemas operativos posteriores cuando un segundo socket intenta enlazar a una dirección enlazada previamente a mediante un primer socket mediante opciones de socket específicas y una cuenta de usuario diferente.

Primera llamada de enlace Segunda llamada de enlace
Default SO_REUSEADDR SO_EXCLUSIVEADDRUSE
Wildcard (Carácter comodín) Específico Wildcard (Carácter comodín) Específico Wildcard (Carácter comodín) Específico
Default Wildcard (Carácter comodín) INUSE ACCESS ACCESS ACCESS INUSE ACCESS
Específico Correcto INUSE Correcto ACCESS INUSE INUSE
SO_REUSEADDR Wildcard (Carácter comodín) INUSE ACCESS Correcto Correcto INUSE ACCESS
Específico Correcto INUSE Correcto Correcto INUSE INUSE
SO_EXCLUSIVEADDRUSE Wildcard (Carácter comodín) INUSE ACCESS ACCESS ACCESS INUSE ACCESS
Específico Correcto INUSE Correcto ACCESS INUSE INUSE

Tenga en cuenta que el comportamiento predeterminado es diferente cuando las llamadas de enlace se realizan en diferentes cuentas de usuario. Si el primer llamador no establece ninguna opción en el socket y se enlaza a la dirección comodín, el segundo autor de la llamada no puede establecer la opción SO_REUSEADDR y enlazar correctamente al mismo puerto. El comportamiento predeterminado sin establecer opciones devuelve también un error.

En Windows Vista y versiones posteriores, se puede crear un socket de pila dual que funciona a través de IPv6 e IPv4. Cuando un socket de pila dual está enlazado a la dirección comodín, el puerto especificado se reserva en las pilas de red IPv4 e IPv6 y las comprobaciones asociadas a SO_REUSEADDR y SO_EXCLUSIVEADDRUSE (si se establece). Estas comprobaciones deben realizarse correctamente en ambas pilas de red. Por ejemplo, si un socket TCP de pila dual establece SO_EXCLUSIVEADDRUSE e intenta enlazar al puerto 5000, ningún otro socket TCP se puede enlazar previamente al puerto 5000 (ya sea comodín o específico). En este caso, si un socket TCP IPv4 se enlazaba anteriormente a la dirección de bucle invertido en el puerto 5000, la llamada de enlace para el socket de pila dual produciría un error con WSAEACCES.

Estrategias de aplicación

Al desarrollar aplicaciones de red que funcionan en la capa de socket, es importante tener en cuenta el tipo de seguridad de socket necesaria. Las aplicaciones cliente ( aplicaciones que se conectan o envían datos a un servicio) rara vez requieren pasos adicionales, ya que se enlazan a un puerto local aleatorio (efímero). Si el cliente requiere un enlace de puerto local específico para funcionar correctamente, debe considerar la seguridad del socket.

La opción SO_REUSEADDR tiene muy pocos usos en aplicaciones normales aparte de sockets de multidifusión donde los datos se entregan a todos los sockets enlazados en el mismo puerto. De lo contrario, cualquier aplicación que establezca esta opción de socket se debe rediseñar para eliminar la dependencia, ya que es eminentemente vulnerable a "secuestro de sockets". Siempre que SO_REUSEADDR opción de socket se pueda usar para secuestrar un puerto en una aplicación de servidor, la aplicación debe considerarse no segura.

Todas las aplicaciones de servidor deben establecer SO_EXCLUSIVEADDRUSE para un nivel seguro de seguridad de socket. No solo impide que el software malintencionado secuestre el puerto, sino que también indica si otra aplicación está enlazada al puerto solicitado. Por ejemplo, se producirá un error en una llamada para enlazar en la dirección comodín por un proceso con el conjunto de opciones de socket SO_EXCLUSIVEADDRUSE si otro proceso está enlazado actualmente al mismo puerto en una interfaz específica.

Por último, aunque la seguridad del socket se ha mejorado en Windows Server 2003, una aplicación siempre debe establecer la opción de socket SO_EXCLUSIVEADDRUSE para asegurarse de que se enlaza a todas las interfaces específicas que ha solicitado el proceso. La seguridad del socket en Windows Server 2003 agrega un mayor nivel de seguridad para las aplicaciones heredadas, pero los desarrolladores de aplicaciones deben seguir diseñando sus productos teniendo en cuenta todos los aspectos de la seguridad.