如何编写系统登录测试?


9

我已经编写了一个Python CGI脚本来调用bash命令,它需要测试主机上的成功登录。

如何为此编写测试?

例如,我可以创建一个bash脚本来针对主机上的注册用户测试给定的用户名和密码组合吗?


1
也许您可以看一下程序背后的代码login
凯文

与此问题无关,但我希望您正在加密到Web服务器的流量,以使用户的登录信息不会被窃听。
2012年

Answers:


8

使用PAM是最好的解决方案。您可以编写小型C代码,或安装python-pam软件包并使用python-pam软件包随附的python脚本。看到/usr/share/doc/python-pam/examples/pamtest.py


我尝试了PAM,但是没有用。但是我再试一次这个例子,它可以工作。
jcubic 2012年

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
2014年

5

测试用户是否可以登录的正确方法是实际以该用户身份登录。

因此,我建议您使用CGI脚本expect来运行su,传递密码并运行必须执行的命令。这是一个可以做到这一点的Expect脚本的草稿(警告:绝对未经测试,我对流利的期望并不熟练)。替换在用户名,密码和命令(其中我写的bobswordfishsomecommand); 确保引用正确。

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

如果您确实不想通过一层执行命令su(例如,由于必须由CGI进程本身执行此操作),则可以使用Expect运行命令true并检查返回状态是否为0。

另一种方法是通过Python的PAM绑定直接在您的应用程序中使用PAM


太棒了,这是没有根访问权限的唯一解决方案。
jcubic 2012年

su -c true bob && echo success很遗憾su不接受密码作为参数
jcubic 2012年

su从CGI脚本进行了测试,它需要一个终端才能工作。
jcubic 2012年

3
@jcubic在命令行参数中输入密码是一个非常愚蠢的主意,因为命令行参数在Unix系统上是公共的。删除密码将提供与在命令行中相同的安全级别。而且检查起来会容易得多:/bin/true足够了。
2015年

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 2012年

我测试了一下,但不起作用
jcubic 2012年

3
馊主意!您假设您的程序是以root身份或至少以shadowgroup 身份运行的,因此强烈不建议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();

是的,好的,但是问题是关于用Python编写的CGI脚本。
吉尔斯(Gillles)“所以-别再邪恶了”

1

如果您具有root用户访问权限,并且正在使用md5密码,并且只需要比较密码,则可以使用perl Crypt :: PasswdMD5模块。从/ etc / shadow中获取MD5哈希,剥离$ 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 2012年

2
馊主意!您假设您的程序是以root身份或至少以shadowgroup 身份运行的,因此强烈不建议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身份或至少以shadowgroup 身份运行的,因此强烈不建议CGI使用该程序:您需要另一层特权升级。并且您假设一种特定的密码哈希算法(openssl支持一种)和密码存储位置(/etc/shadow与NIS或LDAP相反)可能不是该特定用户实际使用的密码。使用PAM,它知道它的工作。
吉尔(Gilles)'所以

是的,我知道,但是认为没有根就不可能。其他所有解决方案也都使用root,当然您也可以使用root。
jcubic 2012年

0

由于您提到您正在python中使用CGI,因此假设您将Apache用作httpd服务器可能是适当的。如果是这样,则将程序的身份验证过程留给Apache,并仅允许经过身份验证的人员执行您的cgi脚本/程序。

有很多模块可以在Apache上为您执行身份验证,这实际上取决于您要寻找哪种身份验证机制。您在问题中引用的方式似乎与基于/ etc / passwd,shadow文件的本地主机帐户身份验证有关。我对此快速搜索的模块是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

另一种方法是使用Apache的SuEXEc模块,该模块代表经过身份验证的用户执行进程(cgi程序)。


此CGI脚本是通过Ajax调用的JSON-RPC服务,我需要返回令牌的方法login,如果登录成功,则应返回令牌。因此,基本上每个用户都需要能够执行该脚本。
jcubic 2012年

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 2014年

-3

如果需要脚本登录到主机,最好的办法是在主机之间配置ssh密钥。

链接:http//pkeck.myweb.uga.edu/ssh/

我几乎从页面上取消了这个


首先,在两台UNIX机器hurly和burly上安装OpenSSH。据我所知,默认情况下,使用DSA密钥和SSH2效果最佳。我见过的所有其他HOWTO似乎都处理RSA密钥和SSH1,并且指令不足为奇地无法与SSH2一起使用。在每台计算机上,键入ssh somemachine.example.com并使用您的常规密码进行连接。这将在您的主目录中创建一个带有适当权限的.ssh目录。在您希望您的秘密密钥存在的主计算机上(假设您很生气),键入

ssh-keygen -t dsa

这将提示您输入密码。如果这是您的主要身份密钥,请确保使用良好的密码短语。如果此方法正常,您将在.ssh目录中得到两个名为id_dsa和id_dsa.pub的文件。注意:提示输入密码短语时,可以仅按Enter键,这将使一个没有密码短语的键成为可能。这是用于身份密钥的Bad Idea™,所以不要这样做!请参见下面的无密码密钥使用。

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 shell。通过这个新的外壳,您应该能够:

ssh burly

您应该可以登录


虽然这是事实,但它似乎与问题无关,该问题与通过Web浏览器访问的应用程序有关。
吉尔斯(Gillles)“所以-别再作恶了”
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.