Uma introdução ao Java SASL

Uma introdução ao Java SASL

1. Visão geral

Neste tutorial, passaremos pelos conceitos básicos deSimple Authentication and Security Layer (SASL). Vamos entender como o Java suporta a adoção do SASL para proteger a comunicação.

No processo, usaremos comunicação simples de cliente e servidor, protegendo-a com SASL.

2. O que é SASL?

SASL éa framework for authentication and data security in Internet protocols. O objetivo é dissociar os protocolos da Internet de mecanismos específicos de autenticação. Vamos entender melhor partes desta definição à medida que avançamos.

A necessidade de segurança na comunicação está implícita. Vamos tentarunderstand this in the context of client and server communication. Normalmente, cliente e servidor trocam dados pela rede. É imperativo que ambas as partes possam confiar umas nas outras e enviar dados com segurança.

2.1. Onde o SASL se encaixa?

Em um aplicativo, podemos usar o SMTP para enviar e-mails e o LDAP para acessar os serviços de diretório. Mas cada um desses protocolos pode oferecer suporte a outro mecanismo de autenticação, como Digest-MD5 ou Kerberos.

E se houvesse uma maneira de os protocolos trocarem mecanismos de autenticação de maneira mais declarativa? É exatamente aí que o SASL entra em cena. Os protocolos que suportam SASL podem, invariavelmente, suportar qualquer um dos mecanismos SASL.

Portanto,applications can negotiate a suitable mechanisme adote para autenticação e comunicação segura.

2.2. Como o SASL funciona?

Agora que vimos onde o SASL se encaixa no esquema geral de segurança, vamos entender como ele funciona.

SASLis a challenge-response framework. Aqui, o servidor emite um desafio ao cliente e envia uma resposta com base no desafio. O desafio e a resposta são matrizes de bytes de tamanho arbitrário e, portanto, podem transportar qualquer dado específico do mecanismo.

image

Esteexchange can continue for multiple iterationse finalmente termina quando o servidor não emitir mais nenhum desafio.

Além disso, o cliente e o servidor podem negociar uma pós-autenticação da camada de segurança. Toda a comunicação subsequente pode, então, aproveitar essa camada de segurança. No entanto, observe que alguns dos mecanismos podem oferecer suporte apenas à autenticação.

É importante entender aqui que os dados SASLonly provides a framework for the exchange of challenge and response. Ele não menciona nada sobre os dados em si ou como eles são trocados. Esses detalhes são deixados para os aplicativos que adotam o uso do SASL.

3. Suporte SASL em Java

Existem APIs em Java quesupport developing both client-side and server-side applications com SASL. A API não depende dos mecanismos reais. Os aplicativos que usam a API SASL Java podem selecionar um mecanismo com base nos recursos de segurança necessários.

3.1. API SASL Java

As principais interfaces a serem observadas, como parte do pacote “javax.security.sasl”, sãoSaslServereSaslClient.

SaslServer representa o mecanismo do lado do servidor de SASL.

Vamos ver como podemos instanciar umSaslServer:

SaslServer ss = Sasl.createSaslServer(
  mechanism,
  protocol,
  serverName,
  props,
  callbackHandler);

Estamos usando a classe de fábricaSasl para instanciarSaslServer. O métodocreateSaslServer aceita vários parâmetros:

  • mechanism - o nome registrado da IANA de um mecanismo com suporte SASL

  • protocol - o nome do protocolo para o qual a autenticação está sendo feita

  • serverName - o nome de host totalmente qualificado do servidor

  • props - um conjunto de propriedades usado para configurar a troca de autenticação

  • callbackHandler - um manipulador de retorno de chamada a ser usado pelo mecanismo selecionado para obter mais informações

Fora do exposto, apenas os dois primeiros são obrigatórios e o restante é nulo.

SaslClient representa o mecanismo do lado do cliente do SASL. Vamos ver como podemos instanciar umSaslClient:

SaslClient sc = Sasl.createSaslClient(
  mechanisms,
  authorizationId,
  protocol,
  serverName,
  props,
  callbackHandler);

Aqui, novamente, estamos usando a classe de fábricaSasl para instanciar nossoSaslClient. A lista de parâmetros quecreateSaslClient aceita é praticamente a mesma de antes.

No entanto, existem algumas diferenças sutis:

  • mechanisms - aqui, esta é uma lista de mecanismos para tentar

  • authorizationId - esta é uma identificação dependente de protocolo a ser usada para autorização

O restante dos parâmetros é semelhante em significado e em sua opcionalidade.

3.2. Fornecedor de segurança Java SASL

Sob a API SASL Java, estão os mecanismos reais que fornecem os recursos de segurança. Oimplementation of these mechanisms is provided by security providers registrado comJava Cryptography Architecture (JCA).

Pode haver vários provedores de segurança registrados na JCA. Cada um dessesmay support one or more of the SASL mechanisms.

O Java é fornecido com o SunSASL como provedor de segurança, que é registrado como provedor JCA por padrão. No entanto, isso pode ser removido ou reordenado com outros fornecedores disponíveis.

Além disso, é semprepossible to provide a custom security provider. Isso exigirá que implementemos as interfacesSaslClienteSaslServer. Ao fazer isso, também podemos implementar nosso mecanismo de segurança personalizado!

4. SASL através de um exemplo

Agora que vimos como criar umSaslServere umSaslClient, é hora de entender como usá-los. Estaremos desenvolvendo componentes de cliente e servidor. Eles trocarão desafio e resposta iterativamente para obter autenticação. Faremos uso do mecanismo DIGEST-MD5 em nosso exemplo simples aqui.

4.1. Cliente e ServidorCallbackHandler

Como vimos antes, precisamos fornecer implementações deCallbackHandler aSaslServereSaslClient. Agora,CallbackHandler é uma interface simples que define um único método -handle. Este método aceita uma matriz deCallback.

Aqui,Callback presents a way for the security mechanism to collect authentication data from the calling application. Por exemplo, um mecanismo de segurança pode exigir um nome de usuário e senha. Existem algumas implementações deCallback comoNameCallbackePasswordCallback disponíveis para uso.

Vamos ver como podemos definir umCallbackHandler para o servidor, para começar:

public class ServerCallbackHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] cbs) throws IOException, UnsupportedCallbackException {
        for (Callback cb : cbs) {
            if (cb instanceof AuthorizeCallback) {
                AuthorizeCallback ac = (AuthorizeCallback) cb;
                //Perform application-specific authorization action
                ac.setAuthorized(true);
            } else if (cb instanceof NameCallback) {
                NameCallback nc = (NameCallback) cb;
                //Collect username in application-specific manner
                nc.setName("username");
            } else if (cb instanceof PasswordCallback) {
                PasswordCallback pc = (PasswordCallback) cb;
                //Collect password in application-specific manner
                pc.setPassword("password".toCharArray());
            } else if (cb instanceof RealmCallback) {
                RealmCallback rc = (RealmCallback) cb;
                //Collect realm data in application-specific manner
                rc.setText("myServer");
            }
        }
    }
}

Agora, vamos ver nosso lado do cliente deCallbackhandler:

public class ClientCallbackHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] cbs) throws IOException, UnsupportedCallbackException {
        for (Callback cb : cbs) {
            if (cb instanceof NameCallback) {
                NameCallback nc = (NameCallback) cb;
                //Collect username in application-specific manner
                nc.setName("username");
            } else if (cb instanceof PasswordCallback) {
                PasswordCallback pc = (PasswordCallback) cb;
                //Collect password in application-specific manner
                pc.setPassword("password".toCharArray());
            } else if (cb instanceof RealmCallback) {
                RealmCallback rc = (RealmCallback) cb;
                //Collect realm data in application-specific manner
                rc.setText("myServer");
            }
        }
    }
}

Para esclarecer, somoslooping through the Callback array and handling only specific ones. Aqueles com os quais precisamos lidar são específicos para o mecanismo em uso, que é o DIGEST-MD5 aqui.

4.2. Autenticação SASL

Então, nós escrevemos nosso cliente e servidorCallbackHandler. Também instanciamosSaslClient eSaslServer para o mecanismo DIGEST-MD5.

Agora é a hora de vê-los em ação:

@Test
public void givenHandlers_whenStarted_thenAutenticationWorks() throws SaslException {
    byte[] challenge;
    byte[] response;

    challenge = saslServer.evaluateResponse(new byte[0]);
    response = saslClient.evaluateChallenge(challenge);

    challenge = saslServer.evaluateResponse(response);
    response = saslClient.evaluateChallenge(challenge);

    assertTrue(saslServer.isComplete());
    assertTrue(saslClient.isComplete());
}

Vamos tentar entender o que está acontecendo aqui:

  • Primeiro, nosso cliente recebe o desafio padrão do servidor

  • O cliente avalia o desafio e prepara uma resposta

  • Essa troca de desafio-resposta continua por mais um ciclo

  • No processo, o cliente e o servidor usam manipuladores de retorno de chamada para coletar quaisquer dados adicionais, conforme necessário pelo mecanismo

  • Isso conclui nossa autenticação aqui, mas, na realidade, pode iterar em vários ciclos

Atypical exchange of challenge and response byte arrays happens over the network. Mas, aqui para simplificar, assumimos a comunicação local.

4.3. Comunicação segura SASL

Como discutimos anteriormente, o SASL é uma estrutura capaz de suportar comunicação segura além da autenticação. No entanto,this is only possible if the underlying mechanism supports it.

Em primeiro lugar, vamos primeiro verificar se conseguimos negociar uma comunicação segura:

String qop = (String) saslClient.getNegotiatedProperty(Sasl.QOP);

assertEquals("auth-conf", qop);

Aqui,QOP stands for the quality of protection. Isso é algo que o cliente e o servidor negociam durante a autenticação. Um valor de "auth-int" indica autenticação e integridade. Enquanto, um valor de "auth-conf" indica autenticação, integridade e confidencialidade.

Depois de termos uma camada de segurança, podemos aproveitar isso para proteger nossa comunicação.

Vamos ver como podemos proteger a comunicação de saída no cliente:

byte[] outgoing = "example".getBytes();
byte[] secureOutgoing = saslClient.wrap(outgoing, 0, outgoing.length);

// Send secureOutgoing to the server over the network

E, da mesma forma, o servidor pode processar a comunicação recebida:

// Receive secureIncoming from the client over the network
byte[] incoming = saslServer.unwrap(secureIncoming, 0, netIn.length);

assertEquals("example", new String(incoming, StandardCharsets.UTF_8));

5. SASL no mundo real

Portanto, agora temos um entendimento justo do que é o SASL e como usá-lo em Java. Mas, normalmente, não é para isso que vamos acabar usando SASL, pelo menos em nossa rotina diária.

Como vimos anteriormente,SASL is primarily meant for protocols like LDAP and SMTP. Embora, cada vez mais aplicativos sejam integrados ao SASL - por exemplo, Kafka. Então, como usamos o SASL para autenticar com esses serviços?

Suponhamos que configuramos o Kafka Broker para SASL com PLAIN como o mecanismo de escolha. PLAIN simplesmente significa que se autentica usando uma combinação de nome de usuário e senha em texto sem formatação.

Vamos agora ver como podemos configurar um cliente Java para usar SASL / PLAIN para autenticar no Kafka Broker.

Começamos fornecendo uma configuração simples do JAAS, "kafka_jaas.conf":

KafkaClient {
  org.apache.kafka.common.security.plain.PlainLoginModule required
  username="username"
  password="password";
};

Utilizamos essa configuração JAAS ao iniciar a JVM:

-Djava.security.auth.login.config=kafka_jaas.conf

Por fim, precisamos adicionar algumas propriedades para passar para as instâncias de produtor e consumidor:

security.protocol=SASL_SSL
sasl.mechanism=PLAIN

É tudo o que há para isso. No entanto, esta é apenas uma pequena parte deKafka client configurations. Além do PLAIN, o Kafka também suporta o GSSAPI / Kerberos para autenticação.

6. SASL em comparação

Embora o SASL seja bastante eficaz em fornecer uma maneira neutra em mecanismo de autenticar e proteger a comunicação entre cliente e servidor. No entanto,SASL is not the only solution available a esse respeito.

O próprio Java fornece outros mecanismos para atingir esse objetivo. Vamos discuti-los brevemente e entender como eles se saem contra a SASL:

  • Java Secure Socket Extension (JSSE):JSSE is a set of packages in Java that implements Secure Sockets Layer (SSL) for Java. Ele fornece criptografia de dados, autenticação de cliente e servidor e integridade de mensagens. Ao contrário do SASL, o JSSE conta com uma PKI (Public Key Infrastructure) para funcionar. Portanto, o SASL é mais flexível e leve que o JSSE.

  • Java GSS API (JGSS):JGGS is the Java language binding for Generic Security Service Application Programming Interface (GSS-API). A GSS-API é um padrão IETF para aplicativos acessarem serviços de segurança. Em Java, no GSS-API, o Kerberos é o único mecanismo suportado. O Kerberos novamente requer uma infraestrutura Kerberizada para funcionar. Comparado ao SASL, aqui ainda, as opções são limitadas e pesadas.

No geral, o SASL é uma estrutura muito leve e oferece uma ampla variedade de recursos de segurança por meio de mecanismos conectáveis. Os aplicativos que adotam o SASL têm várias opções para implementar o conjunto correto de recursos de segurança, dependendo da necessidade.

7. Conclusão

Para resumir, neste tutorial, entendemos o básico da estrutura SASL, que fornece autenticação e comunicação segura. Também discutimos as APIs disponíveis em Java para implementar o SASL do lado do cliente e do servidor.

Vimos como usar um mecanismo de segurança através de um provedor JCA. Por fim, também falamos sobre o uso do SASL no trabalho com diferentes protocolos e aplicativos.

Como sempre, o código pode ser encontradoover on GitHub.