Làm thế nào để tôi viết một bài kiểm tra để đăng nhập hệ thống?


9

Tôi đã viết một kịch bản Python CGI gọi bashcác lệnh và nó cần kiểm tra để đăng nhập thành công trên máy chủ.

Làm thế nào để tôi viết một bài kiểm tra cho điều đó?

Ví dụ: tôi có thể tạo bashtập lệnh kiểm tra kết hợp tên người dùng và mật khẩu cụ thể đối với người dùng đã đăng ký trên máy chủ không?


1
Có lẽ bạn có thể nhìn vào mã đằng sau loginchương trình.
Kevin

Không liên quan đến câu hỏi, nhưng tôi hy vọng bạn đang mã hóa lưu lượng truy cập vào máy chủ web của mình để thông tin đăng nhập của người dùng không bị đánh hơi.
jw013

Câu trả lời:


8

Sử dụng PAM là giải pháp tốt nhất. Bạn có thể viết mã C nhỏ hoặc cài đặt gói python-pam và sử dụng tập lệnh python đi kèm với gói python-pam. Xem/usr/share/doc/python-pam/examples/pamtest.py


Tôi thử PAM nhưng không được. Nhưng tôi thử lại ví dụ này và nó hoạt động.
jcubic

1
Trong OpenSUSE 12.3 (python-pam 0.5.0-84.1.1) và 13.1 (0.5.0-87.1.2), đường dẫn đầy đủ đến pamtest.py là /usr/share/doc/packages/python-pam/examples/pamtest.pyTập lệnh pamtest.py có thể được sử dụng để kiểm tra thông tin đăng nhập trên các hệ thống bằng PAM để xác thực, được bao gồm trong gói python-pam (yêu cầu python) và trong một số bản phân phối, đường dẫn đầy đủ là /usr/share/doc/python-pam/examples/pamtest.py.
ShadSterling

5

Cách tiếp cận đúng để kiểm tra xem người dùng có thể đăng nhập hay không là đăng nhập thực sự với người dùng đó.

Vì vậy, những gì tôi khuyên là làm cho tập lệnh CGI sử dụng expectđể chạy su, truyền mật khẩu và chạy lệnh phải được thực thi. Đây là bản nháp của một kịch bản kỳ vọng thực hiện điều này (cảnh báo: hoàn toàn chưa được kiểm tra và tôi không thông thạo về mong đợi). Thay vào tên người dùng, mật khẩu và lệnh (nơi tôi đã viết bob, swordfishsomecommand); hãy chắc chắn để trích dẫn chính xác.

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

Nếu bạn thực sự không muốn thực thi lệnh thông qua một lớp su(ví dụ vì những gì bạn làm phải được thực hiện bởi chính quy trình CGI), thì hãy sử dụng kỳ vọng để chạy lệnh truevà kiểm tra trạng thái trả về là 0.

Một cách tiếp cận khác là sử dụng PAM trực tiếp trong ứng dụng của bạn, thông qua liên kết PAM của Python .


Thật tuyệt vời, Giải pháp duy nhất mà không cần truy cập root.
jcubic

điều này su -c true bob && echo successthật xấu hổ khi su không chấp nhận mật khẩu làm đối số
jcubic

Tôi đã thử nghiệm sutừ kịch bản CGI và nó cần một thiết bị đầu cuối để nó hoạt động.
jcubic

3
@jcubic Một ý tưởng thực sự ngu ngốc khi đặt mật khẩu trong một đối số dòng lệnh, bởi vì các đối số dòng lệnh được công khai trên hệ thống Unix. Xóa mật khẩu sẽ cung cấp mức độ bảo mật tương tự như đặt nó vào dòng lệnh. Và nó sẽ dễ dàng hơn nhiều để kiểm tra: /bin/truesẽ là đủ.
ceving

2

Để trả lời cụ thể hơn: "Có thể tạo tập lệnh bash sẽ kiểm tra kết hợp tên người dùng và mật khẩu nhất định đối với người dùng đã đăng ký trên máy chủ không?"

Đúng.

#!/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
Điều này có làm việc cho bạn? Tôi đang thấy rằng bạn đang kiểm tra mật khẩu bóng hiện có so với mật khẩu bóng nhưng việc băm có liên quan ở đâu?
Nikhil Mulley

Tôi đã thử nghiệm và nó không hoạt động
jcubic

3
Ý kiến ​​tồi! Bạn đang giả định rằng chương trình của bạn đang chạy với quyền root hoặc ít nhất là shadownhóm, điều này rất không được khuyến nghị cho CGI: bạn cần một lớp leo thang đặc quyền khác. Và bạn đang giả sử một thuật toán băm mật khẩu cụ thể (một thuật toán được hỗ trợ bởi openssl) và vị trí lưu trữ mật khẩu ( /etc/shadowtrái ngược với NIS hoặc LDAP) có thể hoặc không thực sự được sử dụng cho người dùng cụ thể đó.
Gilles 'SO- ngừng trở nên xấu xa'

2

Có một giải pháp PAM 'C', 'Python' được trích dẫn ở đây, hãy để tôi đặt một perl nữa :-)

Nguồn: 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();

Vâng, ok, nhưng câu hỏi là về một kịch bản CGI được viết bằng Python.
Gilles 'SO- ngừng trở nên xấu xa'

1

Nếu bạn có quyền truy cập root và đang sử dụng mật khẩu md5 và bạn chỉ cần so sánh mật khẩu, thì bạn có thể sử dụng mô-đun perl Crypt :: PasswdMD5 . Lấy Hash MD5 từ / etc / bóng, tách $ 1 $, rồi chia cho $ còn lại. Trường 1 = Muối, Trường 2 = văn bản được mã hóa. Sau đó băm đầu vào văn bản vào CGI của bạn, so sánh nó với văn bản được mã hóa và Bob là chú của bạn.

#!/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
đơn giản và đơn giản :-). Điều này sẽ hoạt động miễn là / etc / passwd sử dụng băm md5. Sử dụng xác thực (nsswitch) là khác nhau cho hệ thống, sau đó mô-đun pam là tốt nhất để sử dụng.
Nikhil Mulley

2
Ý kiến ​​tồi! Bạn đang giả định rằng chương trình của bạn đang chạy với quyền root hoặc ít nhất là shadownhóm, điều này rất không được khuyến nghị cho CGI: bạn cần một lớp leo thang đặc quyền khác. Và bạn đang giả sử một thuật toán băm mật khẩu cụ thể (MD5 chứ không phải bcrypt hoặc thuật toán được đề xuất khác) và vị trí lưu trữ mật khẩu ( /etc/shadowtrái ngược với NIS hoặc LDAP) có thể hoặc không thực sự được sử dụng cho người dùng cụ thể đó.
Gilles 'SO- ngừng trở nên xấu xa'

0

Sau một số tìm kiếm, tôi đã viết chương trình C này có thể được sử dụng từ tập lệnh

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

Bạn biên dịch nó với:

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

Bạn có thể sử dụng nó như

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

1
Ý kiến ​​tồi! Bạn đang giả định rằng chương trình của bạn đang chạy với quyền root hoặc ít nhất là shadownhóm, điều này rất không được khuyến nghị cho CGI: bạn cần một lớp leo thang đặc quyền khác. Và bạn đang giả sử một thuật toán băm mật khẩu cụ thể (một thuật toán được hỗ trợ bởi openssl) và vị trí lưu trữ mật khẩu ( /etc/shadowtrái ngược với NIS hoặc LDAP) có thể hoặc không thực sự được sử dụng cho người dùng cụ thể đó. Sử dụng PAM, nó biết công việc của nó.
Gilles 'SO- ngừng trở nên xấu xa'

Có tôi biết, nhưng nghĩ rằng điều này là không thể nhìn thấy mà không có root. Tất cả các giải pháp khác cũng sử dụng root, ngoại trừ của bạn.
jcubic

0

Vì bạn đã đề cập rằng bạn đang sử dụng CGI trong python, nên có thể phù hợp để cho rằng bạn đang sử dụng Apache làm máy chủ httpd của mình. Nếu vậy, hãy để quá trình xác thực chương trình của bạn cho Apache và chỉ để những người được xác thực thực thi các tập lệnh / chương trình cgi của bạn.

Có nhiều mô-đun có thể xác thực cho bạn trên Apache, nó thực sự phụ thuộc vào loại cơ chế xác thực mà bạn đang tìm kiếm. Cách bạn trích dẫn trong câu hỏi dường như có liên quan đến xác thực tài khoản máy chủ cục bộ dựa trên / etc / passwd, các tệp bóng. Mô-đun đi kèm với tìm kiếm nhanh của tôi về điều này là mod_auth_shadow. Ưu điểm là bạn cho phép ai đó có thẩm quyền (chạy trên cổng đặc quyền 80) để xác thực người dùng / mật khẩu cho bạn và bạn có thể dựa vào thông tin xác thực của người dùng để chạy các lệnh thay mặt cho người dùng, nếu cần.

Liên kết tốt để bắt đầu:

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_ubfox

Một cách tiếp cận khác là sử dụng mô-đun SuEXEc của Apache, thực thi các quy trình (chương trình cgi) thay mặt cho người dùng được xác thực.


Kịch bản CGI này là dịch vụ JSON-RPC được gọi thông qua Ajax và tôi cần đăng nhập phương thức trả lại mã thông báo, mã thông báo sẽ được trả lại nếu đăng nhập thành công. Vì vậy, cơ bản mỗi người dùng cần có thể thực hiện kịch bản đó.
jcubic

0

Mã này sử dụng PAM làm việc cho tôi:

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

Bạn có thể thêm một số thông tin ngữ cảnh để làm cho nó "cảm thấy" giống như một câu trả lời hơn không?
Volker Siegel

-3

Điều tốt nhất bạn có thể làm, nếu bạn cần một tập lệnh để đăng nhập vào máy chủ, là cấu hình một khóa ssh giữa các máy chủ.

Liên kết: http://pkeck.myweb.uga.edu/ssh/

Tôi đã nhấc cái này lên khỏi trang


Đầu tiên, cài đặt OpenSSH trên hai máy UNIX, nhanh và nặng. Điều này hoạt động tốt nhất bằng cách sử dụng các khóa DSA và SSH2 theo mặc định như tôi có thể nói. Tất cả các HOWTO khác mà tôi thấy dường như đều xử lý các khóa RSA và SSH1, và các hướng dẫn không đáng ngạc nhiên khi không hoạt động với SSH2. Trên mỗi loại máy ssh somemachine.example.com và tạo kết nối với mật khẩu thông thường của bạn. Điều này sẽ tạo ra một thư mục .ssh trong thư mục nhà của bạn với các perm thích hợp. Trên máy chính của bạn, nơi bạn muốn các khóa bí mật của mình tồn tại (hãy nói nhanh lên), nhập

ssh-keygen -t dsa

Điều này sẽ nhắc bạn cho một mật khẩu bí mật. Nếu đây là khóa nhận dạng chính của bạn, hãy đảm bảo sử dụng cụm mật khẩu tốt. Nếu điều này hoạt động đúng, bạn sẽ nhận được hai tệp có tên id_dsa và id_dsa.pub trong thư mục .ssh của bạn. Lưu ý: có thể chỉ cần nhấn phím enter khi được nhắc nhập cụm mật khẩu, sẽ tạo một khóa không có cụm mật khẩu. Đây là một ý tưởng tồi ™ cho khóa nhận dạng, vì vậy đừng làm điều đó! Xem bên dưới để sử dụng các phím không có cụm mật khẩu.

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

Sao chép tệp id_dsa.pub vào thư mục .ssh của máy chủ khác với tên ủy quyền_keys2. Bây giờ burly đã sẵn sàng để chấp nhận khóa ssh của bạn. Làm thế nào để nói với nó những phím nào để sử dụng? Lệnh ssh-add sẽ làm điều đó. Đối với một bài kiểm tra, gõ

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

Điều này sẽ bắt đầu tác nhân ssh, thêm danh tính mặc định của bạn (nhắc bạn nhập cụm mật khẩu của bạn) và sinh ra một bash shell. Từ lớp vỏ mới này, bạn sẽ có thể:

ssh burly

Bạn sẽ có thể đăng nhập


Mặc dù điều này là đúng, nhưng nó dường như không liên quan đến câu hỏi, đó là về một ứng dụng được truy cập thông qua trình duyệt web.
Gilles 'SO- ngừng trở nên xấu xa'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.