Como verificar se a senha digitada é uma senha válida para esse usuário?

11

O cenário:

Em um script bash, tenho que verificar se uma senha dada por um usuário é uma senha de usuário válida.

Suponho que eu tenha um usuário A com senha PA .. No script, pedi ao usuário A para digitar sua senha. Então, como verificar se a string digitada é realmente sua senha? ...

    
por Maythux 20.04.2015 / 12:45

3 respostas

12

Como você quer fazer isso em um shell script, algumas contribuições em Como verificar senha com o Linux? (em Unix.SE , sugerido pelo AB ) são especialmente relevantes :

Para verificar manualmente se uma string é realmente a senha de algum usuário, você deve usar o mesmo algoritmo de hash da entrada de sombra do usuário, com a mesma salt como na entrada de sombra do usuário. Então pode ser comparado com o hash de senha armazenado lá.

Eu escrevi um script completo e funcional demonstrando como fazer isso.

  • Se você nomear chkpass , poderá executar chkpass user e ele lerá uma linha da entrada padrão e verificará se ela é a senha de user .
  • Instale o whois pacote para obter o utilitário mkpasswd do qual esse script depende.
  • Este script deve ser executado como root para ter sucesso.
  • Antes de usar este script ou qualquer parte dele para fazer um trabalho real, consulte Notas de segurança abaixo.
#!/usr/bin/env bash

xcorrect=0 xwrong=1 enouser=2 enodata=3 esyntax=4 ehash=5  IFS=$
die() {
    printf '%s: %s\n' "
set -f; ent=($(getent shadow "" | awk -F: '{print }')); set +f
" "" >&2 exit } report() { if (( == xcorrect)) then echo 'Correct password.' else echo 'Wrong password.' fi exit } (($# == 1)) || die $esyntax "Usage: $(basename "
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
") <username>" case "$(getent passwd "" | awk -F: '{print }')" in x) ;; '') die $enouser "error: user '' not found";; *) die $enodata "error: 's password appears unshadowed!";; esac if [ -t 0 ]; then IFS= read -rsp "[$(basename "%pre%")] password for : " pass printf '\n' else IFS= read -r pass fi set -f; ent=($(getent shadow "" | awk -F: '{print }')); set +f case "${ent[1]}" in 1) hashtype=md5;; 5) hashtype=sha-256;; 6) hashtype=sha-512;; '') case "${ent[0]}" in \*|!) report $xwrong;; '') die $enodata "error: no shadow entry (are you root?)";; *) die $enodata 'error: failure parsing shadow entry';; esac;; *) die $ehash "error: password hash type is unsupported";; esac if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]] then report $xcorrect else report $xwrong fi

Notas de segurança

Pode não ser a abordagem correta.

Se uma abordagem como essa deve ou não ser considerada segura e apropriada depende de detalhes sobre o caso de uso que você não forneceu (até o momento).

Não foi auditado.

Embora eu tenha tentado tomar cuidado ao escrever este script, ele não foi devidamente auditado para vulnerabilidades de segurança . Ele é uma demonstração e seria um software "alfa" se lançado como parte de um projeto. Além disso ...

Outro usuário que está "assistindo" pode descobrir o sal do usuário.

Devido a limitações em como mkpasswd aceita dados de salt, este script contém uma falha de segurança conhecida , que você pode ou não considerar aceitável dependendo do caso de uso. Por padrão, os usuários no Ubuntu e na maioria dos outros sistemas GNU / Linux podem visualizar informações sobre processos executados por outros usuários (incluindo root), incluindo seus argumentos de linha de comando. Nem a entrada do usuário nem o hash da senha armazenada são passados como um argumento de linha de comando para qualquer utilitário externo. Mas o salt , extraído do banco de dados shadow , é dado como um argumento de linha de comando para mkpasswd , já que esta é a única maneira que o utilitário aceita um sal como entrada.

Se

  • outro usuário no sistema, ou
  • qualquer pessoa que tenha a capacidade de criar uma conta de usuário (por exemplo, www-data ) execute o código, ou
  • qualquer pessoa que, de outra forma, possa visualizar informações sobre processos em execução (incluindo inspecionar manualmente as entradas em /proc )

é capaz de verificar os argumentos da linha de comando para mkpasswd enquanto é executado por este script, então eles podem obter uma cópia do salt do usuário do banco de dados shadow . Eles podem ter que ser capazes de adivinhar quando esse comando é executado, mas isso às vezes é possível.

Um atacante com seu sal não é tão ruim quanto um atacante com seu sal e hash , mas não é o ideal. O sal não fornece informações suficientes para que alguém descubra sua senha. Mas permite que alguém gere tabelas de arco-íris ou hashes de dicionário pré-computados específicos desse usuário nesse sistema. Isto é inicialmente inútil, mas se a sua segurança for comprometida mais tarde e o hash completo for obtido, então poderá ser cracked mais rapidamente para obter a senha do usuário antes que eles tenham a chance de alterá-la.

Assim, esta falha de segurança é um fator exacerbante em um cenário de ataque mais complexo do que uma vulnerabilidade totalmente explorável. E você pode considerar a situação acima forçada. Mas estou relutante em recomendar qualquer método para uso geral no mundo real que vaze qualquer dados não públicos de /etc/shadow para um usuário não-root.

Você pode evitar esse problema completamente:

  • escrevendo parte do seu script em Perl ou em algum outro idioma que permita chamar funções em C, como mostrado na resposta de Gilles para a questão Unix.SE relacionada , ou
  • escrevendo todo o seu script / programa em tal linguagem, ao invés de usar o bash. (Com base na maneira como você marcou a pergunta, parece que você prefere usar o bash.)

Cuidado como você chama esse script.

Se você permitir que um usuário não confiável execute esse script como root ou execute qualquer processo como root que chame esse script, tenha cuidado . Alterando o ambiente, eles podem fazer este script - ou qualquer script que é executado como root - qualquer coisa . A menos que você possa evitar que isso ocorra, você não deve permitir privilégios elevados aos usuários para executar scripts de shell.

Veja 10.4. Linguagens de script Shell (derivadas sh e csh) em Programação segura para Linux e Unix HOWTO para mais informações sobre isso. Embora sua apresentação se concentre em scripts setuid, outros mecanismos podem ser afetados por alguns dos mesmos problemas se não limparem corretamente o ambiente.

Outras Notas

Ele suporta a leitura de hashes apenas do banco de dados shadow .

As senhas devem ser sombreadas para que esse script funcione (ou seja, seus hashes devem estar em um arquivo /etc/shadow separado que somente o root possa ler, não em /etc/passwd ).

Este sempre deve ser o caso no Ubuntu. Em qualquer caso, se necessário, o script pode ser trivialmente estendido para ler os hashes de senha de passwd , bem como shadow .

Tenha em mente IFS ao modificar este script.

Eu defino IFS=$ no começo, já que os três dados no campo hash de uma entrada de sombra são separados por $ .

  • Eles também têm um $ líder, e é por isso que o tipo de hash e o sal são "${ent[1]}" e "${ent[2]}" , em vez de "${ent[0]}" e "${ent[1]}" , respectivamente.

Os únicos locais nesse script em que $IFS determina como o do shell é dividido ou combina palavras são

  • quando esses dados são divididos em uma matriz, inicializando-os a partir da substituição do comando $( ) sem aspas em:

    %pre%
  • quando a matriz é reconstituída em uma string para comparar com o campo completo de shadow , a expressão "${ent[*]}" em:

    %pre%

Se você modificar o script e fazer com que ele execute a divisão de palavras (ou a junção de palavras) em outras situações, será necessário definir IFS para valores diferentes para comandos diferentes ou partes diferentes do script.

Se você não tiver isso em mente e assumir que $IFS está definido para o espaço em branco normal ( $' \t\n' ), você pode acabar fazendo com que seu script se comporte de maneiras bem estranhas.

    
por Eliah Kagan 10.05.2015 / 23:01
8

Você pode usar incorretamente sudo para isso. sudo tem a opção -l , para testar os privilégios de sudo que o usuário possui e -S para ler a senha de stdin. No entanto, independentemente do nível de privilégio que o usuário tenha, se autenticado com êxito, sudo retornará com o status de saída 0. Assim, você pode tomar qualquer outro status de saída como indicação de que a autenticação não funcionou (supondo que sudo não funciona) t tem algum problema, como erros de permissão ou configuração sudoers inválida).

Algo como:

#! /bin/bash
IFS= read -rs PASSWD
sudo -k
if sudo -lS &> /dev/null << EOF
$PASSWD
EOF
then
    echo 'Correct password.'
else 
    echo 'Wrong password.'
fi

Este script depende bastante da configuração sudoers . Eu assumi a configuração padrão. Coisas que podem causar falhas:

  • targetpw ou runaspw está definido
  • listpw é never
  • etc.

Outros problemas incluem (obrigado Eliah):

  • As tentativas incorretas serão registradas em /var/log/auth.log
  • sudo deve ser executado como o usuário para o qual você está autenticando. A menos que você tenha sudo privilégios para poder executar sudo -u foo sudo -lS , isso significa que você terá que executar o script como o usuário de destino.

Agora, a razão pela qual eu usei aqui-docs é dificultar a escuta. Uma variável usada como parte da linha de comando é mais facilmente revelada usando top ou ps ou outras ferramentas para inspecionar processos.

    
por muru 11.05.2015 / 20:05
3

Outro método (provavelmente mais interessante para seus conteúdos teóricos do que para suas aplicações práticas).

As senhas do usuário são armazenadas em /etc/shadow .

As senhas armazenadas aqui são criptografadas, nos últimos lançamentos do Ubuntu usando SHA-512 .

Especificamente, após a criação da senha, a senha em texto não criptografado é salgada e criptografada por SHA-512 .

Uma solução seria salgar / criptografar a senha fornecida e associá-la à senha do usuário criptografado armazenada na entrada /etc/shadow do usuário em questão.

Para dar uma rápida descrição de como as senhas são armazenadas em cada usuário /etc/shadow entry, aqui está uma amostra /etc/shadow entry para um usuário foo com a senha bar :

foo:$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/:16566:0:99999:7:::
  • foo : nome de usuário
  • 6 : tipo de criptografia da senha
  • lWS1oJnmDlaXrx1F : sal de criptografia da senha
  • h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/ : SHA-512 salgados / senha criptografada

Para corresponder à senha dada bar para o usuário dado foo , a primeira coisa a fazer é obter o sal:

$ sudo getent shadow foo | cut -d$ -f3
lWS1oJnmDlaXrx1F

Em seguida, deve-se obter a senha completa salgada / criptografada:

$ sudo getent shadow foo | cut -d: -f2
$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/

Em seguida, a senha fornecida pode ser salgada / criptografada e comparada com a senha do usuário salgada / criptografada armazenada em /etc/shadow :

$ python -c 'import crypt; print crypt.crypt("bar", "$lWS1oJnmDlaXrx1F")'
$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/

Eles combinam! Tudo colocado em um script bash :

#!/bin/bash

read -p "Username >" username
IFS= read -p "Password >" password
salt=$(sudo getent shadow $username | cut -d$ -f3)
epassword=$(sudo getent shadow $username | cut -d: -f2)
match=$(python -c 'import crypt; print crypt.crypt("'"${password}"'", "$'${salt}'")')
[ ${match} == ${epassword} ] && echo "Password matches" || echo "Password doesn't match"

Saída:

$ ./script.sh 
Username >foo
Password >bar
Password matches
$ ./script.sh 
Username >foo
Password >bar1
Password doesn't match
    
por kos 11.05.2015 / 21:54

Tags