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


9

Я написав сценарій CGI Python, який викликає 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 .


Це приголомшливо, єдине рішення, що не має доступу до кореня.
jcubic

це працює su -c true bob && echo successвоно соромно , що су не приймає пароль в якості аргументу
jcubic

Я протестував suсценарій CGI, і для його роботи потрібен термінал.
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
Це працювало для вас? Я бачу, що ви перевіряєте наявний тіньовий пароль проти тіньового пароля, але де тут займається хешування?
Нікхіл Маллі

Я тестував, і це не працює
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

Якщо у вас є кореневий доступ і ви використовуєте паролі 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) відрізняється для системи, тоді найкраще використовувати пам-модулі.
Нікхіл Мюллі

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, він знає свою роботу.
Жил "ТАК - перестань бути злим"

Так, я знаю, але думав, що це неможливо без кореня. Усі інші рішення використовують також корінь, крім вашого.
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, і мені потрібен метод входу, який повертає маркер, маркер повинен бути повернутий, якщо вхід вдався. Тому в основному кожен користувач повинен мати можливість виконувати цей сценарій.
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 dir. Примітка. Можна просто натиснути клавішу введення, коли буде запропоновано ввести парольну фразу, яка зробить ключ без парольної фрази. Це погана ідея ™ для ідентифікаційного ключа, тому не робіть цього! Дивіться нижче про використання клавіш без парольних фраз.

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

Скопіюйте файл id_dsa.pub у файл .ssh dir другого хоста з ім'ям_позволений_keys2. Тепер Burly готовий прийняти ваш ssh ключ. Як сказати, які клавіші використовувати? Команда ssh-add зробить це. Для тестування введіть

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

Це запустить ssh-агент, додасть вашу ідентифікацію за замовчуванням (запропонує вам ввести парольну фразу) та породжує оболонку bash. З цієї нової оболонки ви зможете:

ssh burly

Ви повинні мати можливість увійти


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