ฉันจะเขียนการทดสอบเพื่อเข้าสู่ระบบได้อย่างไร


9

ฉันเขียนสคริปต์ Python CGI ที่เรียกใช้bashคำสั่งและต้องทดสอบการลงชื่อเข้าใช้ที่สำเร็จบนโฮสต์

ฉันจะเขียนแบบทดสอบได้อย่างไร

ตัวอย่างเช่นฉันสามารถสร้างbashสคริปต์ที่ทดสอบรวมชื่อผู้ใช้และรหัสผ่านที่กำหนดกับผู้ใช้ที่ลงทะเบียนในโฮสต์ได้หรือไม่


1
บางทีคุณสามารถดูรหัสที่อยู่ด้านหลังloginโปรแกรม
Kevin

ไม่เกี่ยวข้องกับคำถาม แต่ฉันหวังว่าคุณกำลังเข้ารหัสปริมาณการใช้งานไปยังเว็บเซิร์ฟเวอร์ของคุณเพื่อให้การเข้าสู่ระบบของผู้ใช้ไม่สามารถดักฟังสายได้
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สำหรับการตรวจสอบจะรวมอยู่ในแพคเกจหลามแพม (ซึ่งต้องหลาม) /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 ธ ผูกพัน


มันยอดเยี่ยมทางออกเดียวที่ไม่มีการเข้าถึงรูท
jcubic

su -c true bob && echo successมันใช้งานได้ดีที่ su ไม่ยอมรับรหัสผ่านเป็นอาร์กิวเมนต์
jcubic

ฉันทดสอบsuจากสคริปต์ CGI และต้องการเทอร์มินัลเพื่อให้ทำงานได้
jcubic

3
@jcubic มันเป็นความคิดที่โง่จริงๆที่จะใส่รหัสผ่านในอาร์กิวเมนต์บรรทัดคำสั่งเพราะอาร์กิวเมนต์บรรทัดคำสั่งเป็นสาธารณะในระบบ Unix การลบรหัสผ่านจะให้ระดับความปลอดภัยเช่นเดียวกับที่ใส่ไว้ในบรรทัดคำสั่ง และมันจะง่ายกว่าในการตรวจสอบ: /bin/trueเพียงพอแล้ว
ceving

2

หากต้องการคำตอบเพิ่มเติมโดยเฉพาะ: "เป็นไปได้ไหมที่จะสร้างสคริปต์ทุบตีที่จะทดสอบชื่อผู้ใช้และรหัสผ่านที่กำหนดร่วมกับผู้ใช้ที่ลงทะเบียนบนโฮสต์"

ใช่.

#!/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
มันเหมาะกับคุณหรือไม่? ฉันเห็นว่าคุณกำลังตรวจสอบรหัสผ่านเงาที่มีอยู่กับรหัสผ่านเงา แต่ที่ hashing ที่เกี่ยวข้องที่นี่
Nikhil Mulley

ผมทดสอบและมันจะไม่ได้ทำงาน
jcubic

3
ความคิดที่ไม่ดี! คุณกำลังสมมติว่าโปรแกรมของคุณทำงานเป็น root หรืออย่างน้อยก็เป็นshadowกลุ่มซึ่งไม่แนะนำอย่างยิ่งสำหรับ CGI: คุณต้องมีการเพิ่มระดับสิทธิ์อีกระดับ และคุณสมมติว่าอัลกอริทึมการแฮชรหัสผ่านหนึ่งอัน (อันหนึ่งสนับสนุนโดย openssl) และตำแหน่งที่เก็บรหัสผ่าน ( /etc/shadowตรงข้ามกับเช่น NIS หรือ LDAP) ซึ่งอาจใช้หรือไม่ใช้กับผู้ใช้นั้น
Gilles 'หยุดความชั่วร้าย'

2

มีคำว่า 'C', 'Python' PAM ที่ยกมาที่นี่ให้ฉันใส่ 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
Gilles 'หยุดความชั่วร้าย'

1

หากคุณมีสิทธิ์เข้าถึงรูตและใช้รหัสผ่าน md5 และคุณเพียงต้องการเปรียบเทียบรหัสผ่านคุณสามารถใช้โมดูลCrypt :: PasswdMD5 perlได้ ใช้ MD5 Hash จาก / 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 hashing ในการตรวจสอบความถูกต้อง (nsswitch) นั้นแตกต่างกันสำหรับระบบจากนั้นโมดูล pam จะใช้งานได้ดีที่สุด
Nikhil Mulley

2
ความคิดที่ไม่ดี! คุณกำลังสมมติว่าโปรแกรมของคุณทำงานเป็น root หรืออย่างน้อยก็เป็นshadowกลุ่มซึ่งไม่แนะนำอย่างยิ่งสำหรับ CGI: คุณต้องมีการเพิ่มระดับสิทธิ์อีกระดับ และคุณสมมติว่าอัลกอริทึมการแฮ็นรหัสผ่านโดยเฉพาะ (MD5 ไม่ใช่ bcrypt หรืออัลกอริทึมอื่น ๆ ที่แนะนำ) และที่เก็บรหัสผ่าน ( /etc/shadowตรงข้ามกับ NIS หรือ LDAP) ซึ่งอาจใช้หรือไม่ใช้กับผู้ใช้นั้น
Gilles 'หยุดความชั่วร้าย'

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 มันรู้หน้าที่ของมัน
Gilles 'หยุดความชั่วร้าย'

ใช่ฉันรู้ แต่คิดว่านี่เป็นไปไม่ได้หากไม่มีราก โซลูชันอื่นทั้งหมดใช้รูทเช่นกัน
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; 
}

คุณสามารถเพิ่มข้อมูลบริบทเพื่อให้ "รู้สึก" เหมือนคำตอบได้ไหม
Volker Siegel

-3

สิ่งที่ดีที่สุดที่คุณสามารถทำได้หากคุณต้องการสคริปต์เพื่อเข้าสู่โฮสต์คือกำหนดค่าคีย์ ssh ระหว่างโฮสต์

ลิงก์: http://pkeck.myweb.uga.edu/ssh/

ฉันยกนี่ขึ้นมาจากหน้า


ก่อนอื่นให้ติดตั้ง OpenSSH ลงในเครื่อง UNIX สองเครื่องก่อนและหลัง สิ่งนี้ทำงานได้ดีที่สุดโดยใช้คีย์ DSA และ SSH2 โดยค่าเริ่มต้นเท่าที่ฉันจะบอกได้ HOWTO อื่น ๆ ทั้งหมดที่ฉันเคยเห็นดูเหมือนจะจัดการกับคีย์ RSA และ SSH1 และคำแนะนำที่ไม่น่าแปลกใจที่ล้มเหลวในการทำงานกับ SSH2 ในแต่ละประเภทเครื่อง ssh somemachine.example.com และทำการเชื่อมต่อกับรหัสผ่านปกติของคุณ สิ่งนี้จะสร้าง. ssh dir ในโฮมไดเร็กตอรี่ของคุณด้วย perms ที่เหมาะสม บนเครื่องหลักของคุณที่คุณต้องการให้รหัสลับของคุณมีชีวิตอยู่ (สมมติว่ารีบ) พิมพ์

ssh-keygen -t dsa

สิ่งนี้จะแจ้งให้คุณทราบสำหรับข้อความรหัสผ่านลับ หากนี่คือรหัสประจำตัวหลักของคุณตรวจสอบให้แน่ใจว่าคุณใช้ข้อความรหัสผ่านที่ดี หากสิ่งนี้ถูกต้องคุณจะได้รับสองไฟล์ชื่อ id_dsa และ id_dsa.pub ใน. ssh dir ของคุณ หมายเหตุ: มันเป็นไปได้ที่จะเพียงแค่กดปุ่ม Enter เมื่อได้รับแจ้งสำหรับวลีรหัสผ่านซึ่งจะทำให้คีย์ที่ไม่มีข้อความรหัสผ่าน นี่คือ Bad Idea ™สำหรับรหัสประจำตัวดังนั้นอย่าทำ! ดูด้านล่างสำหรับการใช้งานของคีย์โดยไม่มีข้อความรหัสผ่าน

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

คัดลอกไฟล์ id_dsa.pub ไปยัง. ssh dir ของโฮสต์อื่นด้วยชื่อ authorized_keys2 ตอนนี้แข็งแรงพร้อมที่จะรับกุญแจ ssh ของคุณแล้ว จะบอกได้อย่างไรว่าควรใช้คีย์ใด คำสั่ง ssh-add จะทำ สำหรับการทดสอบให้พิมพ์

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

สิ่งนี้จะเริ่มต้น ssh-agent เพิ่มตัวตนเริ่มต้นของคุณ (พร้อมท์ให้คุณใส่ข้อความรหัสผ่าน) และวางเปลือก bash จากเปลือกใหม่นี้คุณควรจะสามารถ:

ssh burly

คุณควรจะสามารถเข้าสู่ระบบได้


แม้ว่าจะเป็นจริง แต่ดูเหมือนว่าจะไม่เกี่ยวข้องกับคำถามซึ่งเป็นเรื่องเกี่ยวกับแอปพลิเคชันที่เข้าถึงได้ผ่านเว็บเบราว์เซอร์
Gilles 'หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.