Как мне написать тест для входа в систему?


9

Я написал Python CGI-скрипт, который вызывает bashкоманды, и он должен проверить успешность входа на хост.

Как мне написать тест для этого?

Например, могу ли я создать bashскрипт, который проверяет данную комбинацию имени пользователя и пароля с зарегистрированным пользователем на хосте?


1
Возможно, вы могли бы посмотреть на код позади loginпрограммы.
Кевин

Не имеет отношения к вопросу, но я надеюсь, что вы шифруете трафик на свой веб-сервер, чтобы логины пользователей не могли быть перехвачены.
jw013

Ответы:


8

Использование PAM - лучшее решение. Вы можете написать небольшой код на C или установить пакет python-pam и использовать скрипт python, который входит в пакет python-pam. Видеть/usr/share/doc/python-pam/examples/pamtest.py


Я пробую PAM, но это не сработало. Но я попробую еще раз этот пример, и это сработает.
jcubic

1
В OpenSUSE 12.3 (python-pam 0.5.0-84.1.1) и 13.1 (0.5.0-87.1.2) полный путь к pamtest.py равен /usr/share/doc/packages/python-pam/examples/pamtest.pyСценарий pamtest.py можно использовать для проверки учетных данных в системах, использующих PAM. для аутентификации включен в пакет python-pam (для которого требуется python), а в некоторых дистрибутивах полный путь - /usr/share/doc/python-pam/examples/pamtest.py.
ShadSterling

5

Правильный подход к проверке того, может ли пользователь войти в систему, состоит в том, чтобы фактически войти в систему как этот пользователь.

Поэтому я рекомендую использовать сценарий CGI expectдля запуска su, передачи пароля и запуска команды, которая должна быть выполнена. Вот черновик ожидаемого сценария, который делает именно это (предупреждение: абсолютно не проверено, и я не бегу в ожидании). Подставьте имя пользователя, пароль и команду (где я писал bob, swordfishа somecommand); не забудьте правильно процитировать.

spawn "/bin/su" "bob"
expect "Password:"
send "swordfish\r"
expect "^\\$"
send "somecommand"
expect -re "^\\$"
send "exit\r"
expect eof

Если вы действительно не хотите выполнять команду через слой su(например, потому что то, что вы делаете, должно быть выполнено самим процессом CGI), то используйте команду ожидают, чтобы запустить команду trueи проверить, что возвращаемый статус равен 0.

Другой подход заключается в использовании PAM непосредственно в вашем приложении через привязку PAM к Python .


Это потрясающе, Единственное решение, которое без root-доступа.
jcubic

это работает, su -c true bob && echo successжаль, что su не принимает пароль в качестве аргумента
jcubic

Я тестировал на suCGI-скрипте, и ему нужен терминал для работы.
jcubic

3
@jcubic Это действительно глупая идея - вводить пароль в аргумент командной строки, потому что аргументы командной строки являются общедоступными в системе Unix. Удаление пароля обеспечит тот же уровень безопасности, что и ввод его в командной строке. И было бы намного проще проверить: /bin/trueбыло бы достаточно.
празднование

2

Более конкретно, ответьте: «Можно ли создать bash-скрипт, который будет проверять заданную комбинацию имени пользователя и пароля на зарегистрированном пользователе на хосте?»

Да.

#!/bin/bash
uid=`id -u`

if [ $uid -ne 0 ]; then 
    echo "You must be root to run this"
    exit 1
fi

if [ $# -lt 1 ]; then
    echo "You must supply a username to check - ($# supplied)"
    exit 1
fi

username=$1
salt=`grep $username /etc/shadow | awk -F: ' {print substr($2,4,8)}'`

if [ "$salt" != "" ]; then

        newpass=`openssl passwd -1 -salt $salt`
        grep $username  /etc/shadow | grep -q  $newpass && echo "Success" || echo "Failure"

fi

2
Это сработало для вас? Я вижу, что вы проверяете существующий теневой пароль по теневому паролю, но где здесь хеширование?
Nikhil Mulley

Я проверил, и это не работает
Jcubic

3
Плохая идея! Вы предполагаете, что ваша программа работает как root или, по крайней мере, как shadowгруппа, что очень не рекомендуется для CGI: вам понадобится еще один уровень повышения привилегий. И вы предполагаете конкретный алгоритм хеширования паролей (поддерживаемый openssl) и место хранения паролей ( /etc/shadowв отличие, например, от NIS или LDAP), которые могут или не могут быть теми, которые фактически используются для этого конкретного пользователя.
Жиль "ТАК ... перестать быть злым"

2

Здесь приведено PAM-решение 'C', 'Python', позвольте мне также поставить perl :-)

Источник: http://search.cpan.org/~nikip/Authen-PAM-0.16/PAM/FAQ.pod#1._Can_I_authenticate_a_user_non_interactively ?

#!/usr/bin/perl

  use Authen::PAM;
  use POSIX qw(ttyname);

  $service = "login";
  $username = "foo";
  $password = "bar";
  $tty_name = ttyname(fileno(STDIN));

  sub my_conv_func {
    my @res;
    while ( @_ ) {
        my $code = shift;
        my $msg = shift;
        my $ans = "";

        $ans = $username if ($code == PAM_PROMPT_ECHO_ON() );
        $ans = $password if ($code == PAM_PROMPT_ECHO_OFF() );

        push @res, (PAM_SUCCESS(),$ans);
    }
    push @res, PAM_SUCCESS();
    return @res;
  }

  ref($pamh = new Authen::PAM($service, $username, \&my_conv_func)) ||
         die "Error code $pamh during PAM init!";

  $res = $pamh->pam_set_item(PAM_TTY(), $tty_name);
  $res = $pamh->pam_authenticate;
  print $pamh->pam_strerror($res),"\n" unless $res == PAM_SUCCESS();

Да, хорошо, но вопрос был о скрипте CGI, написанном на Python.
Жиль "ТАК ... перестать быть злым"

1

Если у вас есть root-доступ и вы используете пароли md5 и вам просто нужно сравнить пароли, вы можете использовать модуль perl Crypt :: PasswdMD5 . Возьмите хеш MD5 из / etc / shadow, уберите $ 1 $, а затем разделите оставшиеся $. Поле 1 = соль, поле 2 = зашифрованный текст. Затем хэшируйте текстовый ввод в ваш CGI, сравните его с зашифрованным текстом, и Боб - ваш дядя.

#!/usr/bin/env perl

use Crypt::PasswdMD5;

my $user                = $ARGV[0];
my $plain               = $ARGV[1];
my $check               = qx{ grep $user /etc/shadow | cut -d: -f2 };
chomp($check);
my($salt,$md5txt)       = $check =~ m/\$1\$([^\$].+)\$(.*)$/;
my $pass                = unix_md5_crypt($plain, $salt);

if ( "$check" eq "$pass" ) {
        print "OK","\n";
} else {
        print "ERR","\n";
}

2
легко и просто :-). Это будет работать до тех пор, пока / etc / passwd использует хеширование md5. Если аутентификация (nsswitch) отличается для системы, тогда лучше использовать модули pam.
Nikhil Mulley

2
Плохая идея! Вы предполагаете, что ваша программа работает как root или, по крайней мере, как shadowгруппа, что очень не рекомендуется для CGI: вам понадобится еще один уровень повышения привилегий. И вы предполагаете конкретный алгоритм хеширования паролей (MD5, а не bcrypt или другой рекомендуемый алгоритм) и место хранения паролей ( /etc/shadowв отличие, например, от NIS или LDAP), которое может или не может быть тем, которое фактически используется для этого конкретного пользователя.
Жиль "ТАК ... перестать быть злым"

0

После некоторого поиска я написал эту программу на C, которую можно использовать из скрипта

#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <shadow.h>
#include <string.h>
#include <crypt.h>
#include <unistd.h>
#include <libgen.h>

int main(int argc, char **argv) {
    struct spwd *pwd;
    if (argc != 3) {
        printf("usage:\n\t%s [username] [password]\n", basename(argv[0]));
        return 1;
    } else if (getuid() == 0) {
        pwd = getspnam(argv[1]);
        return strcmp(crypt(argv[2], pwd->sp_pwdp), pwd->sp_pwdp);
    } else {
        printf("You need to be root\n");
        return 1;
    }
}

Вы компилируете это с:

gcc -Wall password_check.c /usr/lib/libcrypt.a -o check_passwd

Вы можете использовать его как

sudo ./check_passwd <user> <password> && echo "success" || echo "failure"

1
Плохая идея! Вы предполагаете, что ваша программа работает как root или, по крайней мере, как shadowгруппа, что очень не рекомендуется для CGI: вам понадобится еще один уровень повышения привилегий. И вы предполагаете конкретный алгоритм хеширования паролей (поддерживаемый openssl) и место хранения паролей ( /etc/shadowв отличие, например, от NIS или LDAP), которые могут или не могут быть теми, которые фактически используются для этого конкретного пользователя. Используйте PAM, он знает свою работу.
Жиль "ТАК - перестань быть злым"

Да, я знаю, но думал, что это невозможно без рута. Все остальные решения также используют root, кроме вашего.
jcubic

0

Поскольку вы упомянули, что вы используете CGI в python, вероятно, уместно предположить, что вы используете Apache в качестве httpd-сервера. Если это так, оставьте процесс аутентификации вашей программы Apache и разрешайте только аутентифицированным людям выполнять ваши скрипты / программы cgi.

Существует множество модулей, которые могут выполнять аутентификацию для вас на Apache, это действительно зависит от того, какой механизм аутентификации вы ищете. То, как вы цитировали этот вопрос, похоже, связано с аутентификацией учетной записи локального хоста на основе / etc / passwd, теневых файлов. Модуль, который приходит к моему быстрому поиску по этому поводу mod_auth_shadow. Преимущество заключается в том, что вы позволяете кому-либо из авторитетных (работающих на привилегированном порту 80) аутентифицировать пользователя / пароль для вас, и вы можете полагаться на аутентифицированную информацию пользователя для запуска команд от имени пользователя, если это необходимо.

Хорошие ссылки для начала:

http://adam.shand.net/archives/2008/apache_tips_and_tricks/#index5h2

http://mod-auth-shadow.sourceforge.net/

http://www.howtoforge.com/apache_mod_auth_shadow_debian_ubuntu

Другой подход заключается в использовании модуля SuEXEc из Apache, который выполняет процессы (программы cgi) от имени аутентифицированного пользователя.


Этот CGI-скрипт - это служба JSON-RPC, вызываемая через Ajax, и мне нужен метод login, который возвращает токен, токен должен быть возвращен, если вход выполнен успешно. Таким образом, каждый пользователь должен иметь возможность выполнять этот скрипт.
jcubic

0

Этот код с использованием PAM работал для меня:

#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdio.h>
#include <string.h>

// Define custom PAM conversation function
int custom_converation(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr)
{
    // Provide password for the PAM conversation response that was passed into appdata_ptr
    struct pam_response* reply = (struct pam_response* )malloc(sizeof(struct pam_response));
    reply[0].resp = (char*)appdata_ptr;
    reply[0].resp_retcode = 0;

    *resp = reply;

    return PAM_SUCCESS;
}

int main (int argc, char* argv[]) 
{
    if (argc > 2)
    {
        // Set up a custom PAM conversation passing in authentication password
        char* password = (char*)malloc(strlen(argv[2]) + 1);
        strcpy(password, argv[2]);        
        struct pam_conv pamc = { custom_converation, password };
        pam_handle_t* pamh; 
        int retval;

        // Start PAM - just associate with something simple like the "whoami" command
        if ((retval = pam_start("whoami", argv[1], &pamc, &pamh)) == PAM_SUCCESS)
        {
            // Authenticate the user
            if ((retval = pam_authenticate(pamh, 0)) == PAM_SUCCESS) 
                fprintf(stdout, "OK\n"); 
            else 
                fprintf(stderr, "FAIL: pam_authentication failed.\n"); 

            // All done
            pam_end(pamh, 0); 
            return retval; 
        }
        else
        {
            fprintf(stderr, "FAIL: pam_start failed.\n"); 
            return retval;
        }
    }

    fprintf(stderr, "FAIL: expected two arguments for user name and password.\n"); 
    return 1; 
}

Не могли бы вы добавить некоторую контекстную информацию, чтобы она «выглядела» больше как ответ?
Фолькер Сигел

-3

Лучшее, что вы можете сделать, если вам нужен скрипт для входа на хост, это настроить ключ ssh между хостами.

Ссылка: http://pkeck.myweb.uga.edu/ssh/

Я в значительной степени снял это со страницы


Во-первых, установите OpenSSH на двух машинах UNIX, быстро и надежно. Насколько я могу судить, это лучше всего работает с использованием ключей DSA и SSH2 по умолчанию. Все другие HOWTO, которые я видел, похоже, имеют дело с ключами RSA и SSH1, и инструкции не удивительно не работают с SSH2. На каждой машине введите ssh somemachine.example.com и установите соединение с обычным паролем. Это создаст .ssh dir в вашем домашнем каталоге с правильными привилегиями. На вашей основной машине, где вы хотите, чтобы ваши секретные ключи жили (скажем поспешно), введите

ssh-keygen -t dsa

Это попросит вас ввести секретную фразу-пароль. Если это ваш основной идентификационный ключ, обязательно используйте хорошую фразу-пароль. Если это работает правильно, вы получите два файла с именами id_dsa и id_dsa.pub в вашей директории .ssh. Примечание: можно просто нажать клавишу ввода, когда будет запрошена фраза-пароль, которая создаст ключ без ключевой фразы. Это плохая идея ™ для ключа идентификации, так что не делайте этого! Смотрите ниже для использования ключей без парольных фраз.

scp ~/.ssh/id_dsa.pub burly:.ssh/authorized_keys2

Скопируйте файл id_dsa.pub в каталог .ssh другого хоста с именем authorized_keys2. Теперь Burly готов принять ваш ключ SSH. Как сказать, какие ключи использовать? Команда ssh-add сделает это. Для теста введите

ssh-agent sh -c 'ssh-add < /dev/null && bash'

Это запустит ssh-agent, добавит вашу личность по умолчанию (запросит у вас пароль) и создаст оболочку bash. Из этой новой оболочки вы сможете:

ssh burly

Вы должны быть в состоянии войти


Хотя это действительно так, это не относится к вопросу, касающемуся приложения, доступ к которому осуществляется через веб-браузер.
Жиль "ТАК ... перестать быть злым"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.