Bagaimana cara saya menulis tes untuk login sistem?

9

Saya telah menulis skrip Python CGI yang menjalankan bashperintah, dan perlu menguji agar login berhasil di host.

Bagaimana saya menulis tes untuk itu?

Misalnya, dapatkah saya membuat bashskrip yang menguji kombinasi nama pengguna dan kata sandi yang diberikan terhadap pengguna terdaftar di host?

jcubic
sumber
1
Mungkin Anda bisa melihat kode di belakang loginprogram.
Kevin
Tidak terkait dengan pertanyaan, tapi saya harap Anda mengenkripsi lalu lintas ke server web Anda sehingga login pengguna tidak dapat diendus.
jw013

Jawaban:

8

Menggunakan PAM adalah solusi terbaik. Anda dapat menulis kode C kecil, atau menginstal paket python-pam dan menggunakan skrip python yang datang dengan paket python-pam. Lihat/usr/share/doc/python-pam/examples/pamtest.py

Michał Šrajer
sumber
Saya mencoba PAM tetapi tidak berhasil. Tapi saya coba lagi contoh ini dan berhasil.
jcubic
1
Dalam OpenSUSE 12.3 (python-pam 0.5.0-84.1.1) dan 13.1 (0.5.0-87.1.2), path lengkap ke pamtest.py adalah /usr/share/doc/packages/python-pam/examples/pamtest.pyScript pamtest.py dapat digunakan untuk menguji kredensial pada sistem menggunakan PAM untuk otentikasi, termasuk dalam paket python-pam (yang membutuhkan python), dan dalam beberapa distribusi path lengkapnya adalah /usr/share/doc/python-pam/examples/pamtest.py.
ShadSterling
5

Pendekatan yang tepat untuk menguji apakah pengguna dapat masuk adalah benar-benar masuk sebagai pengguna itu.

Jadi yang saya rekomendasikan adalah membuat skrip CGI digunakan expectuntuk menjalankan su, meneruskan kata sandi dan menjalankan perintah yang harus dijalankan. Berikut adalah konsep skrip ekspektasi yang melakukan hal ini (peringatan: benar-benar belum teruji, dan saya tidak fasih dalam berharap). Mengganti nama pengguna, kata sandi, dan perintah (tempat saya menulis bob, swordfishdan somecommand); pastikan untuk mengutip dengan benar.

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

Jika Anda benar-benar tidak ingin menjalankan perintah melalui lapisan su(misalnya karena apa yang Anda lakukan harus dilakukan oleh proses CGI itu sendiri), maka gunakan harapkan untuk menjalankan perintah truedan periksa bahwa status pengembalian adalah 0.

Pendekatan lain adalah dengan menggunakan PAM secara langsung dalam aplikasi Anda, melalui pengikatan PAM Python .

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Ini luar biasa, Satu-satunya solusi yang tanpa akses root.
jcubic
ini bekerja su -c true bob && echo successsangat memalukan bahwa su tidak menerima kata sandi sebagai argumen
jcubic
Saya diuji sudari skrip CGI dan perlu terminal agar bisa berfungsi.
jcubic
3
@ jcubic Adalah ide yang sangat bodoh untuk memasukkan kata sandi dalam argumen baris perintah, karena argumen baris perintah bersifat publik pada sistem Unix. Menghapus kata sandi akan memberikan tingkat keamanan yang sama seperti menempatkannya di baris perintah. Dan akan lebih mudah untuk memeriksa: /bin/trueapakah cukup.
ceving
2

Untuk menjawab secara lebih spesifik: "Apakah mungkin membuat skrip bash yang akan menguji kombinasi nama pengguna dan kata sandi yang diberikan terhadap pengguna terdaftar di host?"

Iya.

#!/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
Brian Redbeard
sumber
2
Apakah ini berhasil untuk Anda? Saya melihat bahwa Anda sedang memeriksa kata sandi bayangan yang ada terhadap kata sandi bayangan tetapi di mana hashing terlibat di sini?
Nikhil Mulley
Saya diuji dan tidak berhasil
jcubic
3
Ide buruk! Anda berasumsi bahwa program Anda berjalan sebagai root, atau setidaknya sebagai shadowgrup, yang sangat tidak disarankan untuk CGI: Anda perlu eskalasi lapisan privilege lain. Dan Anda mengasumsikan algoritma hashing kata sandi tertentu (yang didukung oleh openssl) dan lokasi penyimpanan kata sandi ( /etc/shadowsebagai lawan dari NIS atau LDAP) yang mungkin atau mungkin bukan yang sebenarnya digunakan untuk pengguna tertentu.
Gilles 'SANGAT berhenti menjadi jahat'
2

Ada solusi PAM 'C', 'Python' yang dikutip di sini, izinkan saya meletakkan satu perl juga :-)

Sumber: 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();
Nikhil Mulley
sumber
Ya, oke, tapi pertanyaannya adalah tentang skrip CGI yang ditulis dengan Python.
Gilles 'SANGAT berhenti menjadi jahat'
1

Jika Anda memiliki akses root, dan menggunakan kata sandi md5, dan Anda hanya perlu membandingkan kata sandi, maka Anda dapat menggunakan modul perl Crypt :: PasswdMD5 . Ambil MD5 Hash dari / etc / shadow, lepas $ 1 $, lalu bagi sisa $. Field 1 = Garam, Field 2 = teks crypted. Lalu masukkan input teks ke dalam CGI Anda, bandingkan dengan teks yang di-crypted, dan Bob, paman Anda.

#!/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";
}
Tim Kennedy
sumber
2
polos dan sederhana :-). Ini akan berfungsi selama / etc / passwd menggunakan hashing md5. Membuat otentikasi (nsswitch) berbeda untuk sistem, kemudian modul pam yang terbaik untuk digunakan.
Nikhil Mulley
2
Ide buruk! Anda berasumsi bahwa program Anda berjalan sebagai root, atau setidaknya sebagai shadowgrup, yang sangat tidak disarankan untuk CGI: Anda perlu eskalasi lapisan privilege lain. Dan Anda mengasumsikan algoritma hashing kata sandi tertentu (MD5 dan bukan bcrypt atau algoritma yang disarankan lainnya) dan lokasi penyimpanan kata sandi ( /etc/shadowsebagai lawan dari misalnya NIS atau LDAP) yang mungkin atau mungkin bukan yang benar-benar digunakan untuk pengguna tertentu.
Gilles 'SO- berhenti bersikap jahat'
0

Setelah beberapa pencarian saya menulis program C ini yang dapat digunakan dari skrip

#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;
    }
}

Anda mengompilasinya dengan:

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

Anda dapat menggunakannya sebagai

sudo ./check_passwd <user> <password> && echo "success" || echo "failure"
jcubic
sumber
1
Ide buruk! Anda berasumsi bahwa program Anda berjalan sebagai root, atau setidaknya sebagai shadowgrup, yang sangat tidak disarankan untuk CGI: Anda perlu eskalasi lapisan privilege lain. Dan Anda mengasumsikan algoritma hashing kata sandi tertentu (yang didukung oleh openssl) dan lokasi penyimpanan kata sandi ( /etc/shadowsebagai lawan dari NIS atau LDAP) yang mungkin atau mungkin bukan yang sebenarnya digunakan untuk pengguna tertentu. Gunakan PAM, ia tahu tugasnya.
Gilles 'SANGAT berhenti menjadi jahat'
Ya saya tahu, tetapi berpikir bahwa ini tidak mungkin dengan tanpa root. Semua solusi lain juga menggunakan root, kecuali milik Anda.
jcubic
0

Karena Anda menyebutkan bahwa Anda menggunakan CGI dalam python, mungkin tepat untuk menganggap bahwa Anda menggunakan Apache sebagai server httpd Anda. Jika demikian, biarkan proses otentikasi program Anda ke Apache dan hanya biarkan orang yang diautentikasi mengeksekusi skrip / program cgi Anda.

Ada banyak modul yang dapat melakukan otentikasi untuk Anda di Apache, itu benar-benar tergantung pada jenis mekanisme otentikasi yang Anda cari. Cara Anda mengutip dalam pertanyaan tampaknya terkait dengan otentikasi akun host lokal berdasarkan / etc / passwd, file shadow. Modul yang datang ke pencarian cepat saya tentang ini mod_auth_shadow. Keuntungannya adalah Anda mengizinkan seseorang yang berwenang (berjalan pada privilege port 80) untuk mengotentikasi pengguna / kata sandi untuk Anda dan Anda dapat mengandalkan informasi yang diautentikasi dari pengguna untuk menjalankan perintah atas nama pengguna, jika diperlukan.

Tautan yang baik untuk memulai:

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

Pendekatan lain adalah dengan menggunakan modul SuEXEc dari Apache, yang mengeksekusi proses (program cgi) atas nama pengguna yang diautentikasi.

Nikhil Mulley
sumber
Skrip CGI ini adalah layanan JSON-RPC yang dipanggil melalui Ajax dan saya memerlukan metode login yang mengembalikan token, token harus dikembalikan jika login berhasil. Jadi pada dasarnya setiap pengguna harus dapat menjalankan skrip itu.
jcubic
0

Kode ini menggunakan PAM bekerja untuk saya:

#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; 
}
Ritchie Carroll
sumber
Bisakah Anda menambahkan beberapa informasi konteks agar "merasa" lebih seperti jawaban?
Volker Siegel
-3

Hal terbaik yang dapat Anda lakukan, jika Anda memerlukan skrip untuk login ke sebuah host, adalah mengkonfigurasi kunci ssh antara host.

Tautan: http://pkeck.myweb.uga.edu/ssh/

Saya cukup banyak mengangkat ini dari halaman


Pertama, instal OpenSSH pada dua mesin UNIX, cepat dan kekar. Ini berfungsi paling baik menggunakan kunci DSA dan SSH2 secara default sejauh yang saya tahu. Semua HOWTO lain yang saya lihat sepertinya berhubungan dengan kunci RSA dan SSH1, dan instruksi yang tidak mengejutkan gagal untuk bekerja dengan SSH2. Pada setiap mesin ketik ssh somemachine.example.com dan buat koneksi dengan kata sandi Anda yang biasa. Ini akan membuat dir .ssh di direktori home Anda dengan perms yang tepat. Di mesin utama Anda di mana Anda ingin kunci rahasia Anda hidup (katakan saja cepat), ketik

ssh-keygen -t dsa

Ini akan meminta Anda untuk frasa sandi rahasia. Jika ini adalah kunci identitas utama Anda, pastikan untuk menggunakan kata sandi yang baik. Jika ini berfungsi dengan baik, Anda akan mendapatkan dua file bernama id_dsa dan id_dsa.pub di dir .ssh Anda. Catatan: dimungkinkan untuk hanya menekan tombol enter saat diminta untuk frasa sandi, yang akan membuat kunci tanpa frasa sandi. Ini adalah Ide Buruk ™ untuk kunci identitas, jadi jangan lakukan itu! Lihat di bawah untuk penggunaan kunci tanpa frasa sandi.

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

Salin file id_dsa.pub ke direktori .ssh host lain dengan namaotorisasi_keys2. Sekarang burly siap menerima kunci ssh Anda. Bagaimana cara mengetahui kunci mana yang digunakan? Perintah ssh-add akan melakukannya. Untuk tes, ketik

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

Ini akan memulai ssh-agent, menambahkan identitas default Anda (meminta Anda untuk frasa sandi Anda), dan menelurkan shell bash. Dari shell baru ini Anda harus dapat:

ssh burly

Anda harus bisa masuk

Roy Rico
sumber
Meskipun ini benar, tampaknya tidak relevan dengan pertanyaan, yaitu tentang aplikasi yang diakses melalui browser web.
Gilles 'SO- stop being evil'