Mostrando postagens com marcador OpenID. Mostrar todas as postagens
Mostrando postagens com marcador OpenID. Mostrar todas as postagens

domingo, 12 de agosto de 2007

Mais sobre OpenID

OpenID Outro dia publiquei um artigo sobre OpenID, que é um sistema de descentralização de autenticação e autorização.

Como ainda estava começando a entender seu funcionamento, cometi alguns erros, nada muito esdrúxulo ou que atrapalhe seu entendimento posterior.

Bem, agora, depois de duas semanas estudando sobre o funcionamento de um consumidor, vou explicar aqui um pouco do que descobri.

Numa transação de autenticação OpenID, há pelo menos três nós envolvidos: cliente, consumidor (RP, relying party) e provedor (OP, provider).

O cliente é o usuário que quer se autenticar, geralmente com seu navegador.

O consumidor é o sistema onde o cliente quer se autenticar.

O provedor é o serviço OpenID onde o cliente se cadastrou.

A autenticação então funciona mais ou menos assim:
  1. o cliente acessa o consumidor e informa sua URL de identificação;
  2. o consumidor «canonicaliza» a URL, e procura saber qual a identificação verdadeira (IDP) e quem é o provedor;
  3. o consumidor toma quaisquer medidas necessárias para verificar a identificação e redireciona o cliente para o provedor;
  4. cliente e provedor se entendem e, a partir daí, a conexão volta para o consumidor;
  5. a partir dos dados voltados, o consumidor sabe se o cliente está autorizado ou não.


Alternativamente o provedor pode enviar dados extra sobre o cliente, em formato SRE (ou sreg) ou AX.

Há dois modos de transação entre consumidor e provedor: dumb mode e smart mode.

Dumb mode


O dumb mode é usado quando o consumidor não suporta cálculos de criptografia.

Primeiro o cliente informa sua URL e o consumidor a usa para descobrir qual sua identificação e quem é o provedor. Na versão 1.0 esses dados se encontram nas seguintes tags:
<link rel="openid.delegate" href="identidade" />
<link rel="openid.server" href="provedor" />


Na versão 2.0:
<link rel="openid2.local_id" href="identidade" />
<link rel="openid2.provider" href="provedor" />


É interessante verificar todos.

Obtidos esses dados, o próximo passo é o ajuste de verificação (checkid_setup): o consumidor redireciona o cliente para o provedor, passando os seguintes dados em GET ou POST:
  • openid.mode: o modo, checkid_setup;
  • openid.identity: a identidade do cliente;
  • openid.return_to: a URL para onde o provedor deve redirecionar o cliente depois da autenticação;
  • openid.trust_root: o nome do servidor do consumidor;
  • openid.ns: a versão do OpenID, http://specs.openid.net/auth/2.0;
  • openid.claimed_id: a URL original fornecida pelo cliente (geralmente igual à identidade).


Depois disso o cliente se resolverá com o provedor. Depois o provedor redirecionará o cliente de volta ao consumidor, para a URL informada por return_to, passando os seguintes parâmetros em POST:
  • openid.mode: pode ser id_res se tudo correu bem, ou cancel;
  • openid.identity: a identidade do cliente;
  • openid.return_to: a mesma URL que o provedor recebeu no passo anterior;
  • openid.signed: lista de parâmetros cobertos pela assinatura;
  • openid.sig: string chave da assinatura;
  • openid.invalid_handle: se o provedor rejeitou algum parâmetro, ele será informado aqui.


No entanto é preciso verificar se esses dados vieram do provedor mesmo ou se foram forjados. Para tanto, valos ao próximo passo, a verificação da autenticação (check_authentication).

O consumidor acessa diretamente o provedor via HTTP reenviando os dados recebidos, mas mudando o parâmetro openid.mode para check_authentication.

O provedor deve responder o consumidor com uma página texto, com os parâmetros terminados por mudança de linha (LF), cada linha no formato chave:valor.

As chaves recebidas são:
  • is_valid: true caso o provedor tenha mesmo enviado os dados ou false;
  • invalid_handle: caso algum campo não tenha sido enviada pelo provedor, ele será informado aqui.


No entanto não é possível trocar dados de persona, que são as informações sobre o usuário.

Se for necessário obter informações de persona, é preciso usar o smart mode.

Smart mode


Esse modo é usado quando se deseja trocar dados de persona, como nome completo, endereço de correio, língua ou país.

Após o cliente ter enviado ao consumidor sua URL e o consumidor tenha identificado o provedor e a identidade, o consumidor precisa então escolher um par gerador-módulo para criptografia Diffie-Hellman. Geralmente é usado 2 como gerador e um primo seguro de 1024 bits como módulo.

O consumidor deve então escolher uma chave privada, um número grande maior que um e menor que o módulo menos um, e calcular a chave pública:
geradorchave_privada  chave_publica mod módulo


Todos esses números grandes devem ser convertidos em formato btwoc (big-endian signed two's complement), representados como base64.

Então é feita a associação: o consumidor acessa diretamente o provedor via HTTP, passando os seguintes dados via GET ou POST:
  • openid.mode: associate, indicando que se trata de uma associação;
  • openid.assoc_type: HMAC-SHA1 para chaves de 160 bits ou HMAC-SHA256 para chaves de 256 bits (acoselho 256b);
  • openid.session_type: DH-SHA1 ou DH-SHA256, conforme o parâmetro anterior;
  • openid.dh_modulus: o módulo escolhido no formato correto (base64 !! btwoc);
  • openid.dh_gen: o gerador no formato correto(se for 2, será Ag==);
  • openid.dh_consumer_public: a chave pública calculada.


A resposta será similar à resposta ao check_authorization, ou seja, parâmetros encerrados por mudança de linha e dois pontos (:) separando chave e valor.

Os parâmetros são:
  • assoc_type: o tipo de associação (o mesmo enviado pelo consumidor);
  • assoc_handle: uma chave que deve ser informada a cada conexão posterior;
  • expires_in: indica quando o assoc_handle expira;
  • session_type: o mesmo tipo enviado pelo consumidor;
  • dh_server_public: a chave pública do servidor;
  • enc_mac_key: o segredo criptogrado.


Então o consumidor deve armazenar esses dados e, assim como no dumb mode, efetuar a verificação de autenticação (checkid_setup), da mesma forma vista anteriormente, mas com umas chaves a mais.

O primeiro é a chave openid.assoc_handle, informando a mesma chave recebida anteriormente.

Caso queira obter dados de SRE (Simple Registration Extension, ou sreg), os parâmetros extra passados são:
  • openid.ns.reg: a versão: http://openid.net/extensions/sreg/1.1;
  • openid.sreg.required: os campos requeridos separados por vírgulas;
  • openid.sreg.optional: campos opcionais separados por vírgulas;
  • openid.sreg.policy_url: uma URL que informe o que será feito com os dados.


Caso queira obter dados de AX (Attribute Exchange), siga a URL. =P

Quando o cliente for redirecionado de volta para o consumidor, o consumidor receberá via POST os mesmos dados recebidos no dumb mode, mas com alguns parâmetros extra: openid.sreg.*, onde * é cada um dos campos requeridos e opcionais solicitados.

Resumo


Ou seja, em dumb mode:
  • checkid_setup
  • check_authentication


Em smart mode:
  • associate
  • checkid_setup


Alternativamente pode ser usado checkid_immadiate em vez de checkid_setup, geralmente quando trabalando com AJAX. Veja as especificações.

Espero ter ajudado.

[]'s
Cacilhas

sábado, 28 de julho de 2007

OpenID

OpenID Passei essa semana quebrando a cabeça no trabalho para desenvolver um protótipo de módulo relying party em Lua.

Relying party – também chamado consumer – é um dos nós que participam de uma autenticação OpenID.

O que é OpenID?


OpenID é um sistema livre descentralizado de autenticação e autorização centrado no usuário.

Ou seja, atualmente em cada sistema com autenticação, você precisa fazer um cadastro. Então você usuário é obrigado a manter uma redundância inconveniente de dados, que eventualmente precisam ser atualizados.

Para piorar os sistemas centralizam dados de todos os usuários, inclusive as senhas. Daí você usuário precisa ter uma senha para cada sistema, tendo de lembrar toda uma fauna de sEnH45 complicadas – ou de 123mudar.

Alguns arriscam sua identidade digital usando a mesma senha para tudo, o que não é aconselhável nem de longe.

OpenID surge em meio a essa realidade como uma solução para o problema.

Você usuário centraliza todos seus dados e sua senha em um único servidor de sua confiança, chamado server-agent ou OpenID provider.

Então você pode autenticar-se em sistemas que ofereçam suporte a OpenID – chamados relying parties ou consumers – sem que o sistema conheça sua senha, apenas fornecendo uma URL conhecida como identidade OpenID (identity). Ainda mais: quando for atualizar seus dados, só é preciso atualizar no agente servidor, pois não há redundância.

É um sistema descentralizado porque você não é obrigado a cadastrar seus dados no sistema onde deseja ser autenticado.

Protocolo


Há ferramentas prontas para Python, JSP, Ruby e PHP.

No entanto Lua é uma linguagem com uma comunidade muito pequena ainda para reagir tão rápido às novidades. Considerando-se ainda a filosofia da linguagem – SIMPLE: simple, light-weight and extensible, simples, leve e extensível –, é compreensível a necessidade de desenvolver os próprios módulos.

Se por um lado é inconveniente, por outro, se você realmente gosta de programar, Lua se torna uma linguagem muito divertida.

Em miúdos: se você trabalha com programação mas não gosta, faz isso só pra ganhar dinheiro, pare de ler por aqui e vai trabalhar. Espere alguém fazer um módulo por você para poder usar OpenID. =P


Continuando então o artigo – agora para programadores de verdade e curiosos –, o problema para desenvolver um módulo consumidor é entender o protocolo.

O protocolo OpenID negocia tunelado dentro do protocolo HTTP.

Resumidamente, a lógica é a seguinte:
  1. O cliente (navegador) acessa o consumidor;
  2. O consumidor envia um formulário ao cliente solicitando sua identidade OpenID;
  3. O cliente informa ao consumidor sua identidade;
  4. O consumidor normaliza a identidade – fica algo como http://user.example.com/;
  5. O consumidor acessa via HTTP a URL da identidade e pega o código HTML;
  6. O consumidor procura na URL marcadores link com atributos rel contendo openid.server, openid.delegate, openid2.provider e openid2.local_id – esses marcadores informam através do atributo href quem é o agente servidor e qual a identidade real do cliente;
  7. O consumidor acessa via HTTP o agente servidor em busca das chaves de comunicação – esse processo é chamado associação;
  8. Tendo sido feita a associação, o consumidor usa as chaves para redirecionar o cliente para a página de autenticação do agente servidor;
  9. O cliente se autentica no agente servidor informando sua senha, se já não estiver atenticado;
  10. O agente servidor pergunta ao cliente se ele autoriza o consumidor a ter acesso a seu perfil;
  11. Se o cliente permitir, o agente servidor redireciona o cliente para o consumidor, passando os dados solicitados.


Vamos então para o primeiro passo que precisamos implementar:

Formulário


O action do formulário deve apontar para o script que irá negociar com o agente servidor e precisa ter uma entrada texto para a identidade.

Esta entrada geralmente tem a identificação openid_login e seu formato CSS padrão pode ser encontrado aqui.

Normalização


O próximo passo já é executado no script.

Ele deve pegar a URL e normalizá-la.

Isto é feito assim:
  • =id não muda;
  • xri://=id vira =id;
  • xri://$dns*id.ex.com vira http://id.ex.com/;
  • xri://$ip*a.b.c.d vira http://a.b.c.d/;
  • http://id.ex.com/ e https://id.ex.com/ não muda;
  • id.ex.com vira http://id.ex.com/.


Na verdade estamos aqui neste artigo interessados apenas nas normalizações que geram URLs HTTP e HTTPS.

Associação


Na associação o consumidor acessa o agente servidor via HTTP.

O agente servidor é obtido de um desses marcadores tirados da consulta ao HTML da identidade:
  • <link rel="openid.server" href="http://example.com/server" />
  • <link rel="openid2.provider" href="http://example.com/server" />
  • <link rel="openid.server openid2.provider" href="http://example.com/server" />


Observação: para uma consulta HTTP em Lua você precisa da função socket.http.request() de LuaSocket.

Na associação envie ao agente servidor via GET os seguintes parâmetros:
  • openid.mode=associate
  • openid.assoc_type=HMAC-SHA1
  • openid.sesion_type=no-encryption


Quanto a criptografia, o ideal é usar DH-SHA1 (Diffie Hellman), mas, até que alguém faça um módulo de baixo nível para Lua de gerenciamento de chaves grandes, Lua é muito lenta para lidar com tais chaves e somos obrigados a trabalhar sem criptografia. =(

Feita esta consulta, o agente servidor deve retornar duas chaves no corpo:
  • assoc_handle – «maçaneta» (alguma tradução melhor?) de associação;
  • mac_key – chave de código de autenticação de mensagem.


Guarde essas informações!

Redirecionamento


Agora é preciso redirecionar o cliente para a página de login do agente servidor.

Faça isso contruindo uma URL apontando para o agente servidor e levando os seguintes parâmetros (via GET mesmo):
  • openid.ns – indica a versão de OpenID usada, para 2.0: http://specs.openid.net/auth/2.0
  • openid.mode=checkid_setup
  • openid.identity – a identidade do cliente
  • openid.claimed_id – novamente a identidade
  • openid.assoc_handle – a «maçaneta» de associação recebida
  • openid.return_to – a URL do script que irá tratar a resposta


Depois disso o cliente vai negociar diretamente com o agente servidor.

Recebendo de volta


No final o cliente será redirecionado de volta para o consumidor, mais especificamente para a URL informada por openid.return_to.

Esse script receberá parâmetros POST, sendo o principal openid.mode, que, se for id_res, o cliente está autenticado e autorizou o consumidor.

Outros parâmetros


Todos os parâmetros podem ser encontrados na especificação do protocolo.

TODO


Este artigo aborda o básico do básico da atenticação.

Com ele ainda não é possível recolher dados de SRE (informações de perfil), que deveriam ser passados nos subparâmetros de openid.sreg.

Mas para tanto é preciso usar criptografia – pelo que entendi, posso estar errado.

De qualquer forma, aqui está uma boa explicação do funcionamento do protocolo OpenID.

[]'s
Cacilhas