1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Tokens de segurança assinam dentro do dispositivo sem exportar a chave privada para fora dele e exigem uma ação física do usuário, o que dificulta que um atacante remoto produza assinaturas arbitrárias
  • Eles podem ser usados para autenticação SSH, U2F, login local sem senha, sudo e assinatura de commits do git, e os dispositivos de segurança embutidos em notebooks e smartphones modernos podem substituir uma YubiKey
  • O arquivo de “chave privada” criado com ssh-keygen -t ed25519-sk não é a chave privada real, mas um handle que aponta para a chave dentro do token, e é possível gerar o mesmo arquivo de chave SSH em outros computadores com o mesmo token
  • Em um MacBook, foi possível configurar o secure element como chave SSH para permitir login SSH baseado em Touch ID, e para assinatura de commits do git foi necessário usar ssh-agent e definir user.signingKey no formato key::, em vez de usar um caminho de arquivo
  • Tokens de segurança não permitem recuperar a chave privada em caso de perda e têm um risco de usabilidade por acostumar o usuário a tocar repetidamente no dispositivo; em notebooks Windows, o Windows Hello pôde confirmar o uso da chave SSH com reconhecimento facial, impressão digital ou PIN

Vantagens e limitações dos tokens de segurança

  • Estrutura central para bloquear ataques remotos

    • Um token de segurança mantém o par de chave privada/pública dentro do dispositivo; a chave pública pode ser extraída facilmente, mas a chave privada não sai do dispositivo
    • Ao enviar ao dispositivo o pacote de dados a ser assinado, ele faz a assinatura internamente com a chave privada e normalmente exige alguma ação física do usuário, como pressionar um botão tátil piscando
    • Mesmo que um atacante remoto consiga acesso ao computador, se o usuário não agir no mundo real o token de segurança não fará assinaturas arbitrárias, então isso parece melhor do que manter um par completo de chave privada/pública SSH como arquivo no diretório ~/.ssh
    • Há opções para quem quer firmware FOSS, como SoloKeys e Nitrokeys
    • Tokens de segurança mais avançados adicionam recursos biométricos, como leitor de impressão digital embutido, mas o ponto central continua sendo a estrutura que não permite que a chave privada saia do dispositivo
  • Riscos de usabilidade

    • Se o usuário se acostumar a pressionar o token de segurança sempre que ele pisca, pode acabar respondendo sem pensar até a solicitações maliciosas
    • Durante sequências contínuas de assinatura, quando o token precisa ser pressionado repetidamente, pode ser difícil perceber de fato mais um pedido piscando
    • Apple e Microsoft usam, em apps autenticadores no smartphone, um método que mostra um código numérico aleatório a cada solicitação de acesso para o usuário digitá-lo, mas isso é incômodo e reduz a vantagem de usabilidade dos tokens de segurança em relação a apps como Authy ou Google Authenticator que usam TOTP
  • Perda e backup

    • Se você perder um token de segurança, aquela chave privada desaparece para sempre e não há como fazer backup dela
    • Para evitar o risco de ficar bloqueado em várias contas, o ideal é comprar pelo menos 2 tokens de segurança e registrá-los no mesmo serviço
    • Como alternativa, existe um método de backup e restauração que transforma a chave privada em uma lista de palavras legíveis por humanos, como no BIP 39
    • Se a chave privada puder sair do enclave seguro, também se torna possível um ataque de phishing que induza o usuário a anotar a lista de palavras em um lugar indevido
    • Se a possibilidade de perder todos os tokens de segurança for uma preocupação séria, a lista de palavras do BIP 39 pode ser o último recurso para recuperar o acesso ao sistema

Usando tokens de segurança com SSH e git

  • Colocando a chave privada SSH em um token de segurança

    • Normalmente, ao executar ssh-keygen, é criado um par de arquivos contendo a chave privada completa
    • Para manter a chave privada em um token de segurança, instala-se o libfido2 seguindo as instruções FIDO/U2F da Yubico e, com o token conectado, executa-se ssh-keygen -t ed25519-sk
    • Mesmo nesse caso, é gerado um par de arquivos, mas o arquivo de “chave privada” não é a chave privada real e sim um handle que aponta para a chave privada dentro do token de segurança
    • Se você executar ssh-keygen -t ed25519-sk novamente com o mesmo token de segurança, poderá criar os mesmos arquivos de chave privada/pública em qualquer computador, então o acesso SSH se desloca com o token de segurança, não ficando preso a um arquivo específico de um computador específico
  • Autenticação no git e assinatura de commits

    • Cerca de 90% das vezes em que se precisa tocar no token de segurança é por causa do uso de git
    • Os forges de git implementam autenticação SSH para operações de push e pull, e ao enviar o arquivo id_ed25519_sk.pub criado acima é possível autorizar o par de chaves do token de segurança
    • O git também aceita chaves SSH para assinatura de commits; depois de seguir a documentação do GitHub em Configurar sua chave de assinatura com uma chave SSH e executar git config --global commit.gpgsign true, todos os commits passam a ser assinados automaticamente
    • Para que o forge de git reconheça o commit como assinado por você, é preciso enviar novamente a chave pública, e esse campo geralmente é separado do campo usado para autenticação SSH
  • Incômodos da assinatura de commits

    • Ao fazer rebase em uma lista longa de commits, é preciso assinar novamente todos eles
    • Em uma YubiKey com leitor de impressão digital, a taxa de falha do reconhecimento foi alta demais para assinar dezenas de commits seguidos, a ponto de abandonar o uso
    • O jujutsu, um wrapper do git centrado em “rebase/amend”, tem uma forma de assinar commits apenas no momento do push
  • Login local no Linux e sudo

Usando o secure element do MacBook como chave SSH

  • Deixar um token de segurança conectado o tempo todo à porta USB-C faz com que ele fique para fora como uma pequena alavanca, podendo danificar a porta e o token se for esbarrado ou derrubado
  • Em um MacBook Air M1 de 2020, o secure element embutido foi configurado como chave SSH seguindo as instruções de Arian van Putten
sc_auth create-ctk-identity -l ssh -k p-256-ne -t bio
ssh-keygen -w /usr/lib/ssh-keychain.dylib -K -N ""
  • Esses comandos criam um par de arquivos de chave privada/pública id_ecdsa_sk_rk, e esses arquivos são movidos para o diretório ~/.ssh
  • Também aqui o arquivo de chave privada não é a chave privada real, mas um handle para a chave dentro do dispositivo, então ele pode ser copiado e colado publicamente
  • Para adicionar a chave pública como authorized key em um servidor de homelab, executa-se o seguinte
ssh-copy-id -i ~/.ssh/id_ecdsa_sk_rk.pub <server nickname>
  • Depois disso, adiciona-se a seguinte configuração em ~/.ssh/config
Host *
  IdentityFile ~/.ssh/id_ecdsa_sk_rk
  SecurityKeyProvider=/usr/lib/ssh-keychain.dylib
  • Ao executar ssh <server nickname>, o macOS mostra automaticamente a solicitação de impressão digital antes do login, e em seguida o login via SSH prossegue normalmente

Assinando commits do git com o secure element do MacBook

  • Mesmo configurando git config --global user.signingKey /Users/ahelwer/.ssh/id_ecdsa_sk_rk e atualizando o arquivo .ssh/allowed_signers, a assinatura de commits do git não funciona de imediato
  • O git falha ao assinar commits e exibe erros como device not found?
error: Signing file /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO
Confirm user presence for key ECDSA-SK SHA256:oQDA2SNYb2MoSQcxJVSmWyAeAWPqMp7rxliBRfi87as
Couldn't sign message: device not found?
Signing /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO failed: device not found?

fatal: failed to write commit object
  • A solução é usar o ssh-agent em vez de apontar diretamente para os arquivos no diretório ~/.ssh
  • Seguindo o tutorial acima, registra-se o par de chaves no ssh-agent com o comando abaixo
ssh-add -K -S /usr/lib/ssh-keychain.dylib
  • Depois disso, em user.signingKey não se usa um caminho de arquivo: coloca-se na ~/.gitconfig a própria chave com o prefixo key:: antes do conteúdo de ~/.ssh/id_ecdsa_sk_rk.pub
[user]
	name = Andrew Helwer
	signingKey = "key::sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGxFEdnIg6ppz+pQCdd1eisjOV4gxrjMv1Y4SbtdLoSm6CJCgPZ6q7lnNyuQQsdnS4/Tllsc656AQL7BO3OS47cAAAAEc3NoOg== ssh:"
  • Depois dessa configuração, foi possível assinar arquivos com a chave armazenada no secure element do MacBook e fazer push do site no GitLab Pages

Resultados no Windows e no Linux

  • Também foi feito um teste rápido no notebook Windows fornecido pela empresa
winget install Microsoft.OpenSSH.preview
ssh-keygen -t ecdsa-sk
  • Esse comando também gerou um par de arquivos de chave privada/pública e, nas conexões SSH, aceitou reconhecimento facial, impressão digital ou PIN pelo fluxo padrão de login do Windows Hello
  • No Linux, não foi possível demonstrar porque não havia acesso a um notebook com secure element após uma verificação semelhante de presença real do usuário

1 comentários

 
GN⁺ 4 시간 전
Comentários no Lobste.rs
  • Excelente artigo, e já é muito útil só por mostrar que isso é possível
    Pessoalmente, não consegui achar uma versão de biblioteca adequada para fazer isso funcionar, mas descobri que o 1Password 8 armazena chaves SSH com segurança e o agente suporta desbloquear a chave com biometria
    Então agora consigo fazer operações com git e também entrar em hosts SSH só encostando o dedo
    Instruções: https://developer.1password.com/docs/ssh/get-started/

  • Isso parece ser só para Mac

    • Como no trabalho posso usar um notebook Windows recente, configurei o OpenSSH para se integrar ao Windows Hello da seguinte forma
      winget install Microsoft.OpenSSH.preview  
      ssh-keygen -t ecdsa-sk  
      
      Depois disso funcionou como antes, e sempre que eu usava a chave para me conectar por SSH a qualquer lugar, passava pelo fluxo padrão do Windows Hello, podendo usar leitor de digital, reconhecimento facial ou PIN
      Não tive a chance de testar um sistema Linux com esse tipo de elemento de segurança, e minha workstation Linux tem TPM V1, mas não sei bem como garantir que as operações de assinatura só sejam executadas depois de confirmar a presença real do usuário
      Talvez alguém com um notebook Linux como um Framework possa tentar. Talvez até funcione de verdade no Asahi
    • Quase isso. Não sei se existe uma camada de emulação TPM2 FIDO em Linux ou Windows
  • Então o que exatamente fica dentro do arquivo de chave privada fornecido?

    • O @wrs já respondeu antes, mas a parte ssh:, ou seja, o aplicativo, corresponde à origin da passkey e é útil para criar resident keys por host ou domínio
      Por exemplo, isso é usado para separar chaves por finalidade mesmo dentro da mesma Yubikey física
      flags especifica como o hardware deve tratar a chave [1]. O agente também pode acrescentar suas próprias restrições
      Tecnicamente, também dá para armazenar outros blobs ou extensões na chave FIDO, e num emprego anterior eu já usei isso para passar credenciais auxiliares, como uma chave pública X.509, junto com a autenticação. É uma abordagem bem interessante
      [1]
      #define SSH_SK_USER_PRESENCE_REQD  0x01  
      #define SSH_SK_USER_VERIFICATION_REQD  0x04  
      #define SSH_SK_FORCE_OPERATION    0x10  
      #define SSH_SK_RESIDENT_KEY    0x20  
      
    • Segundo o Claude, e confirmando com o openssh_key_parser, a estrutura é a seguinte
      O invólucro externo tem o valor mágico openssh-key-v1\0, cipher=none, kdf=none, então não é texto cifrado
      O blob da chave pública, com 74 bytes, contém o tipo de chave sk-ssh-ed25519@openssh.com, o ponto Ed25519 de 32 bytes fdcce889…03e7852b e o aplicativo ssh:, e esse valor separa por namespace as credenciais FIDO de SSH e WebAuthn
      A seção privada tem 248 bytes e, como cipher=none, está em texto puro. Ela contém o valor aleatório checkint1 == checkint2 == 0x46744267, o tipo de chave e a chave pública repetidos, o aplicativo ssh: e flags: 0x01
      Esse flag significa USER_PRESENCE_REQUIRED, então exige toque, mas não PIN/verificação do usuário, e é uma chave não residente
      O key_handle é um ID opaco de credencial de 128 bytes passado para authenticatorGetAssertion, que o dispositivo decodifica internamente para recuperar a seed Ed25519
      Além disso, há um reserved vazio, o comentário ahelwer@ah-mbair.local e padding 01 02 03
    • Se você jogar isso num decodificador base64, sai o seguinte

      openssh-key-v1����none���none����������J���sk-ssh-ed25519@openssh.com��� 盘˪<F$KW+���ssh:���FtBgFtBg���sk-ssh-ed25519@openssh.com��� 盘˪<F$KW+���ssh:���fІpF$D8"&0[X 'L=Ev ')BjM]$}rTv6Z+p9O8ݹ%V* f.|қ.%I{9 .W !D"8N ai*W�y53 �������ahelwer@ah-mbair.local
      Aqui há a versão v1 do padrão de chave, o tipo de chave sk-ssh-ed25519@openssh.com, repetido por algum motivo, e também um nome de chave legível por humanos, ahelwer@ah-mbair.local
      O resto provavelmente são flags do OpenSSH, como exigir PIN ou presença do usuário, e um GUID de handle que o OpenSSH pode enviar para a API FIDO/U2F junto com o desafio
      O OpenSSH consegue inferir pelo tipo de chave, em especial sk, que isso não é uma chave privada de verdade, e sim que ele precisa chamar o elemento de segurança
      Depois disso, ele verifica a configuração SecurityKeyProvider ou a variável de ambiente SSH_SK_PROVIDER para saber de onde carregar a biblioteca dinâmica que permite se comunicar com o elemento de segurança

  • Esse texto parece tratar só de SSH, mas existe alguma forma de usar o Secure Enclave ou o TPM do meu computador como chave FIDO2 ou U2F?

    • Claro, é possível, e em geral deve funcionar quase só com a configuração padrão
      Passkeys também são uma forma desse mesmo modelo, usando uma chave privada separada para cada site
  • Isso faz parecer estranho que o suporte a chaves de API assimétricas ou HMAC que possam ser vinculadas ao hardware não seja mais comum
    É bom ver mais especificações empurrando nessa direção, como WebAuthn, DBSC (Device-Bound Session Credentials) e OAuth2 DPOP