Usando SO_REUSEADDR e SO_EXCLUSIVEADDRUSE

O desenvolvimento de uma infraestrutura de rede segura de alto nível é uma prioridade para a maioria dos desenvolvedores de aplicativos de rede. No entanto, a segurança do soquete geralmente é ignorada, apesar de ser muito crítica ao considerar uma solução totalmente segura. A segurança do soquete, especificamente, lida com processos que se associam à mesma porta anteriormente associada por outro processo de aplicativo. No passado, era possível que um aplicativo de rede "sequestrasse" a porta de outro aplicativo, o que poderia facilmente levar a um ataque de "negação de serviço" ou roubo de dados.

Em geral, a segurança do soquete se aplica a processos do lado do servidor. Mais especificamente, a segurança do soquete se aplica a qualquer aplicativo de rede que aceite conexões e receba tráfego de datagrama de IP. Esses aplicativos normalmente se associam a uma porta conhecida e são destinos comuns para código de rede mal-intencionado.

Aplicativos cliente são menos propensos a serem alvos desses ataques, não porque são menos suscetíveis, mas porque a maioria dos clientes se associa a portas locais "efêmeras" em vez de portas de "serviço" estáticas. Os aplicativos de rede cliente sempre devem ser associados a portas efêmeras (especificando a porta 0 na estrutura SOCKADDR apontada pelo parâmetro name ao chamar a função bind ), a menos que haja um motivo de arquitetura atraente para não fazer isso. As portas locais efêmeras consistem em portas maiores que a porta 49151. A maioria dos aplicativos de servidor para serviços dedicados associa a uma porta reservada conhecida que é menor ou igual à porta 49151. Portanto, para a maioria dos aplicativos, geralmente não há um conflito para associar solicitações entre aplicativos cliente e servidor.

Esta seção descreve o nível padrão de segurança em várias plataformas Microsoft Windows e como as opções de soquete específicas SO_REUSEADDR e SO_EXCLUSIVEADDRUSE afetar e afetar a segurança do aplicativo de rede. Um recurso adicional chamado segurança de soquete aprimorada está disponível no Windows Server 2003 e posterior. A disponibilidade dessas opções de soquete e a segurança aprimorada do soquete variam entre as versões dos sistemas operacionais da Microsoft, conforme mostrado na tabela abaixo.

Plataforma SO_REUSEADDR SO_EXCLUSIVEADDRUSE Segurança aprimorada do soquete
Windows 95 Disponível Não disponível Não disponível
Windows 98 Disponível Não disponível Não disponível
Windows Me Disponível Não disponível Não disponível
Windows NT 4.0 Disponível Disponível no Service Pack 4 e posterior Não disponível
Windows 2000 Disponível Disponível Não disponível
Windows XP Disponível Disponível Não disponível
Windows Server 2003 Disponível Disponível Disponível
Windows Vista Disponível Disponível Disponível
Windows Server 2008 Disponível Disponível Disponível
Windows 7 e mais recente Disponível Disponível Disponível

Usando SO_REUSEADDR

A opção de soquete SO_REUSEADDR permite que um soquete se associe à força a uma porta em uso por outro soquete. O segundo soquete chama setsockopt com o parâmetro optname definido como SO_REUSEADDR e o parâmetro optval definido como um valor booliano de TRUE antes de chamar bind na mesma porta que o soquete original. Depois que o segundo soquete tiver sido associado com êxito, o comportamento de todos os soquetes associados a essa porta será indeterminado. Por exemplo, se todos os soquetes na mesma porta fornecerem serviço TCP, quaisquer solicitações de conexão TCP de entrada pela porta não poderão ser manipuladas pelo soquete correto , o comportamento não é determinístico. Um programa mal-intencionado pode usar SO_REUSEADDR para associar à força soquetes já em uso para serviços de protocolo de rede padrão, a fim de negar o acesso a esses serviços. Nenhum privilégio especial é necessário para usar essa opção.

Se um aplicativo cliente se associar a uma porta antes que um aplicativo de servidor seja capaz de se associar à mesma porta, os problemas poderão resultar. Se o aplicativo de servidor for associado à força usando a opção de soquete SO_REUSEADDR à mesma porta, o comportamento de todos os soquetes associados a essa porta será indeterminado.

A exceção a esse comportamento não determinístico são soquetes multicast. Se dois soquetes estiverem associados à mesma interface e porta e forem membros do mesmo grupo multicast, os dados serão entregues a ambos os soquetes, em vez de um arbitrariamente escolhido.

Usando SO_EXCLUSIVEADDRUSE

Antes da opção de soquete SO_EXCLUSIVEADDRUSE ser introduzida, havia muito pouco que um desenvolvedor de aplicativos de rede poderia fazer para impedir que um programa mal-intencionado se associasse à porta na qual o aplicativo de rede tinha seus próprios soquetes associados. Para resolver esse problema de segurança, o Windows Sockets introduziu a opção de soquete SO_EXCLUSIVEADDRUSE, que ficou disponível no Windows NT 4.0 com o Service Pack 4 (SP4) e posterior.

A opção de soquete SO_EXCLUSIVEADDRUSE só pode ser usada por membros do grupo de segurança Administradores no Windows XP e anteriores. Os motivos pelos quais esse requisito foi alterado no Windows Server 2003 e posteriores são discutidos posteriormente no artigo.

A opção SO_EXCLUSIVEADDRUSE é definida chamando a função setsockopt com o parâmetro optname definido como SO_EXCLUSIVEADDRUSE e o parâmetro optval definido como um valor booliano de TRUE antes que o soquete seja associado. Depois que a opção é definida, o comportamento das chamadas de associação subsequentes difere dependendo do endereço de rede especificado em cada chamada de associação .

A tabela a seguir descreve o comportamento que ocorre no Windows XP e anterior quando um segundo soquete tenta associar a um endereço anteriormente associado a um primeiro soquete usando opções de soquete específicas.

Observação

Na tabela abaixo, "curinga" indica o endereço curinga do protocolo especificado (como "0.0.0.0" para IPv4 e "::" para IPv6). "Específico" indica um endereço IP específico atribuído a uma interface. As células da tabela indicam se a associação foi ou não bem-sucedida ("Êxito") ou se um erro é retornado ("INUSE" para o erro WSAEADDRINUSE ; "ACCESS" para o erro WSAEACCES ).

Primeira chamada de associação Segunda chamada de associação
Padrão SO_REUSEADDR SO_EXCLUSIVEADDRUSE
Curinga Específicas Curinga Específicas Curinga Específicas
Padrão Curinga INUSE INUSE Êxito Êxito INUSE INUSE
Específicas INUSE INUSE Êxito Êxito INUSE INUSE
SO_REUSEADDR Curinga INUSE INUSE Êxito Êxito INUSE INUSE
Específicas INUSE INUSE Êxito Êxito INUSE INUSE
SO_EXCLUSIVEADDRUSE Curinga INUSE INUSE ACCESS ACCESS INUSE INUSE
Específicas INUSE INUSE ACCESS ACCESS INUSE INUSE

Quando dois soquetes estão associados ao mesmo número de porta, mas em interfaces explícitas diferentes, não há conflito. Por exemplo, no caso em que um computador tem duas interfaces IP, 10.0.0.1 e 10.99.99.99, se a primeira chamada para associar estiver em 10.0.0.1 com a porta definida como 5150 e SO_EXCLUSIVEADDRUSE especificada, uma segunda chamada para associar em 10.99.99.99 com a porta também definida como 5150 e nenhuma opção especificada terá êxito. No entanto, se o primeiro soquete estiver associado ao endereço curinga e à porta 5150, qualquer chamada de associação subsequente à porta 5150 com SO_EXCLUSIVEADDRUSE definida falhará com WSAEADDRINUSE ou WSAEACCES retornado pela operação de associação .

No caso em que a primeira chamada para associar conjuntos SO_REUSEADDR ou nenhuma opção de soquete, a segunda chamada de associação "sequestrará" a porta e o aplicativo não poderá determinar quais dos dois soquetes receberam pacotes específicos enviados para a porta "compartilhada".

Um aplicativo típico que chama a função bind não aloca o soquete associado para uso exclusivo, a menos que a opção de soquete SO_EXCLUSIVEADDRUSE seja chamada no soquete antes da chamada para a função de associação . Se um aplicativo cliente se associar a uma porta efêmera ou a uma porta específica antes que um aplicativo de servidor se associe à mesma porta, os problemas poderão resultar. O aplicativo de servidor pode se associar à mesma porta à força usando a opção de soquete SO_REUSEADDR no soquete antes de chamar a função de associação , mas o comportamento de todos os soquetes associados a essa porta é então indeterminado. Se o aplicativo de servidor tentar usar a opção de soquete SO_EXCLUSIVEADDRUSE para uso exclusivo da porta, a solicitação falhará.

Por outro lado, um soquete com o conjunto de SO_EXCLUSIVEADDRUSE não pode necessariamente ser reutilizado imediatamente após o fechamento do soquete. Por exemplo, se um soquete de escuta com SO_EXCLUSIVEADDRUSE conjunto aceitar uma conexão e for fechado posteriormente, outro soquete (também com SO_EXCLUSIVEADDRUSE) não poderá se associar à mesma porta que o primeiro soquete até que a conexão original fique inativa.

Esse problema pode se tornar complicado porque o protocolo de transporte subjacente pode não encerrar a conexão mesmo que o soquete tenha sido fechado. Mesmo depois que o soquete tiver sido fechado pelo aplicativo, o sistema deverá transmitir todos os dados armazenados em buffer, enviar uma mensagem de desconexão normal para o par e aguardar uma mensagem de desconexão normal correspondente do par. É possível que o protocolo de transporte subjacente nunca libere a conexão; por exemplo, o par que participa da conexão original pode anunciar uma janela de tamanho zero ou alguma outra forma de configuração de "ataque". Nesse caso, a conexão do cliente permanece em um estado ativo, apesar da solicitação para fechá-la, pois os dados não reconhecidos permanecem no buffer.

Para evitar essa situação, os aplicativos de rede devem garantir um desligamento normal chamando o desligamento com o sinalizador de SD_SEND definido e, em seguida, aguardar em um loop recv até que zero bytes sejam retornados pela conexão. Isso garante que todos os dados sejam recebidos pelo par e, da mesma forma, confirme com o par que ele recebeu todos os dados transmitidos, além de evitar o problema de reutilização de porta mencionado anteriormente.

A opção de soquete SO_LINGER pode ser definida em um soquete para impedir que a porta faça a transição para um estado de espera "ativo"; no entanto, isso é desencorajado, pois pode levar a efeitos indesejados, como conexões de redefinição. Por exemplo, se os dados forem recebidos pelo par, mas não forem reconhecidos por ele, e o computador local fechar o soquete com SO_LINGER definido nele, a conexão entre os dois computadores será redefinida e os dados não reconhecidos descartados pelo par. Escolher um tempo adequado para permanecer é difícil, pois um valor de tempo limite menor geralmente resulta em conexões anuladas repentinamente, enquanto valores de tempo limite maiores deixam o sistema vulnerável a ataques de negação de serviço (estabelecendo muitas conexões e potencialmente interrompendo/bloqueando threads de aplicativo). Fechar um soquete que tenha um valor de tempo limite persistente diferente de zero também pode fazer com que a chamada closesocket seja bloqueada.

Segurança de soquete aprimorada

A segurança de soquete aprimorada foi adicionada com o lançamento do Windows Server 2003. Em versões anteriores do sistema operacional do servidor Microsoft, a segurança de soquete padrão permitia facilmente que os processos sequestrassem portas de aplicativos desavisados. No Windows Server 2003, os soquetes não estão em um estado compartilhável por padrão. Portanto, se um aplicativo quiser permitir que outros processos reutilizem uma porta na qual um soquete já está associado, ele deverá habilitá-lo especificamente. Se esse for o caso, o primeiro soquete a chamar bind na porta deverá ter SO_REUSEADDR definido no soquete. A única exceção a esse caso ocorre quando a segunda chamada de associação é executada pela mesma conta de usuário que fez a chamada original para associar. Essa exceção existe apenas para fornecer compatibilidade com versões anteriores.

A tabela a seguir descreve o comportamento que ocorre no Windows Server 2003 e em sistemas operacionais posteriores quando um segundo soquete tenta associar a um endereço anteriormente associado a um primeiro soquete usando opções de soquete específicas.

Observação

Na tabela abaixo, "curinga" indica o endereço curinga para o protocolo especificado (como "0.0.0.0" para IPv4 e "::" para IPv6). "Específico" indica um endereço IP específico atribuído a uma interface. As células da tabela indicam se a associação foi ou não bem-sucedida ("Êxito") ou o erro retornado ("INUSE" para o erro WSAEADDRINUSE ; "ACCESS" para o erro WSAEACCES ).

Observe também que, nesta tabela específica, ambas as chamadas de associação são feitas na mesma conta de usuário.

Primeira chamada de associação Segunda chamada de associação
Padrão SO_REUSEADDR SO_EXCLUSIVEADDRUSE
Curinga Específicas Curinga Específicas Curinga Específicas
Padrão Curinga INUSE Êxito ACCESS Êxito INUSE Êxito
Específicas Êxito INUSE Êxito ACCESS INUSE INUSE
SO_REUSEADDR Curinga INUSE Êxito Êxito Êxito INUSE Êxito
Específicas Êxito INUSE Êxito Êxito INUSE INUSE
SO_EXCLUSIVEADDRUSE Curinga INUSE ACCESS ACCESS ACCESS INUSE ACCESS
Específicas Êxito INUSE Êxito ACCESS INUSE INUSE

Algumas entradas na tabela acima merecem explicação.

Por exemplo, se o primeiro chamador definir SO_EXCLUSIVEADDRUSE em um endereço específico e o segundo chamador tentar chamar associação com um endereço curinga na mesma porta, a segunda chamada de associação terá êxito. Nesse caso específico, o segundo chamador está associado a todas as interfaces, exceto o endereço específico ao qual o primeiro chamador está associado. Observe que o inverso desse caso não é verdadeiro: se o primeiro chamador definir SO_EXCLUSIVEADDRUSE e as chamadas se associarem ao sinalizador curinga, o segundo chamador não poderá chamar bind com a mesma porta.

O comportamento de associação de soquete muda quando as chamadas de associação de soquete são feitas em contas de usuário diferentes. A tabela a seguir especifica o comportamento que ocorre no Windows Server 2003 e em sistemas operacionais posteriores quando um segundo soquete tenta associar a um endereço anteriormente associado a um primeiro soquete usando opções de soquete específicas e uma conta de usuário diferente.

Primeira chamada de associação Segunda chamada de associação
Padrão SO_REUSEADDR SO_EXCLUSIVEADDRUSE
Curinga Específicas Curinga Específicas Curinga Específicas
Padrão Curinga INUSE ACCESS ACCESS ACCESS INUSE ACCESS
Específicas Êxito INUSE Êxito ACCESS INUSE INUSE
SO_REUSEADDR Curinga INUSE ACCESS Êxito Êxito INUSE ACCESS
Específicas Êxito INUSE Êxito Êxito INUSE INUSE
SO_EXCLUSIVEADDRUSE Curinga INUSE ACCESS ACCESS ACCESS INUSE ACCESS
Específicas Êxito INUSE Êxito ACCESS INUSE INUSE

Observe que o comportamento padrão é diferente quando as chamadas de associação são feitas em contas de usuário diferentes. Se o primeiro chamador não definir nenhuma opção no soquete e se associar ao endereço curinga, o segundo chamador não poderá definir a opção SO_REUSEADDR e associar com êxito à mesma porta. O comportamento padrão sem nenhum conjunto de opções também retorna um erro.

No Windows Vista e posteriores, é possível criar um soquete de pilha dupla que opera tanto no IPv6 quanto no IPv4. Quando um soquete de pilha dupla é associado ao endereço curinga, a porta fornecida é reservada nas pilhas de rede IPv4 e IPv6 e as verificações associadas a SO_REUSEADDR e SO_EXCLUSIVEADDRUSE (se definido) são feitas. Essas verificações devem ter êxito em ambas as pilhas de rede. Por exemplo, se um soquete TCP de pilha dupla definir SO_EXCLUSIVEADDRUSE e tentar associar à porta 5000, nenhum outro soquete TCP poderá ser associado anteriormente à porta 5000 (curinga ou específico). Nesse caso, se um soquete TCP IPv4 estivesse anteriormente associado ao endereço de loopback na porta 5000 , a chamada de associação para o soquete de pilha dupla falharia com WSAEACCES.

Estratégias de aplicativo

Ao desenvolver um aplicativo de rede que opera na camada de soquete, é importante considerar o tipo de segurança de soquete necessário. Os aplicativos cliente — aplicativos que se conectam ou enviam dados a um serviço — raramente exigem etapas adicionais, pois se associam a uma porta local aleatória (efêmera). Se o cliente exigir uma associação de porta local específica para funcionar corretamente, você deverá considerar a segurança do soquete.

A opção SO_REUSEADDR tem muito poucos usos em aplicativos normais, além de soquetes multicast em que os dados são entregues a todos os soquetes associados à mesma porta. Caso contrário, qualquer aplicativo que define essa opção de soquete deverá ser reprojetado para remover a dependência, pois é eminentemente vulnerável a "sequestro de soquete". Desde que SO_REUSEADDR opção de soquete possa ser usada para potencialmente sequestrar uma porta em um aplicativo de servidor, o aplicativo deve ser considerado não seguro.

Todos os aplicativos de servidor devem definir SO_EXCLUSIVEADDRUSE para um nível forte de segurança de soquete. Ele não só impede que softwares mal-intencionados sequestram a porta, mas também indica se outro aplicativo está associado ou não à porta solicitada. Por exemplo, uma chamada para associar no endereço curinga por um processo com o conjunto de opções de soquete SO_EXCLUSIVEADDRUSE falhará se outro processo estiver atualmente associado à mesma porta em uma interface específica.

Por fim, embora a segurança do soquete tenha sido aprimorada no Windows Server 2003, um aplicativo deve sempre definir a opção de soquete SO_EXCLUSIVEADDRUSE para garantir que ele se associe a todas as interfaces específicas que o processo solicitou. A segurança do soquete no Windows Server 2003 adiciona um nível maior de segurança para aplicativos herdados, mas os desenvolvedores de aplicativos ainda devem projetar seus produtos com todos os aspectos de segurança em mente.