PHP 中的命令行密码提示

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/187736/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:51:09  来源:igfitidea点击:

Command Line Password Prompt in PHP

phppasswords

提问by Gary Richardson

I'm writing a command line tool to help my web app. It needs a password to connect to the service. I'd like the script to show a password prompt so I don't have to pass it as a command line argument.

我正在编写一个命令行工具来帮助我的网络应用程序。它需要密码才能连接到服务。我希望脚本显示密码提示,因此我不必将其作为命令行参数传递。

That's easy enough, but I'd like it to not echo the password to the screen as it's typed. How can I do this with PHP?

这很容易,但我希望它不要在输入密码时将密码回显到屏幕上。我怎样才能用 PHP 做到这一点?

Bonus points for doing it in pure PHP (no system('stty')) and replacing the characters with *.

在纯 PHP 中执行此操作(否system('stty'))并将字符替换为*.

EDIT:

编辑:

The script will run on a unix like system (linux or mac). The script is written in PHP, and will most likely stay like that.

该脚本将在类 Unix 系统(linux 或 mac)上运行。该脚本是用 PHP 编写的,并且很可能会保持这样。

Also, for the record, the sttyway of doing it is:

另外,为了记录,stty这样做的方式是:

echo "Password: ";
system('stty -echo');
$password = trim(fgets(STDIN));
system('stty echo');
// add a new line since the users CR didn't echo
echo "\n";

I'd prefer to not have the system()calls in there.

我宁愿不要system()在那里打电话。

采纳答案by DaveHauenstein

Found on sitepoint.

sitepoint 上找到。

function prompt_silent($prompt = "Enter Password:") {
  if (preg_match('/^win/i', PHP_OS)) {
    $vbscript = sys_get_temp_dir() . 'prompt_password.vbs';
    file_put_contents(
      $vbscript, 'wscript.echo(InputBox("'
      . addslashes($prompt)
      . '", "", "password here"))');
    $command = "cscript //nologo " . escapeshellarg($vbscript);
    $password = rtrim(shell_exec($command));
    unlink($vbscript);
    return $password;
  } else {
    $command = "/usr/bin/env bash -c 'echo OK'";
    if (rtrim(shell_exec($command)) !== 'OK') {
      trigger_error("Can't invoke bash");
      return;
    }
    $command = "/usr/bin/env bash -c 'read -s -p \""
      . addslashes($prompt)
      . "\" mypassword && echo $mypassword'";
    $password = rtrim(shell_exec($command));
    echo "\n";
    return $password;
  }
}

回答by Seldaek

You can use my hiddeninput.exefile to get real hidden input without leaking the information anywhere on screen.

您可以使用我的hiddeninput.exe文件来获取真正的隐藏输入,而不会在屏幕上的任何地方泄露信息。

<?php

echo 'Enter password: ';
$password = exec('hiddeninput.exe');
echo PHP_EOL;

echo 'Password was: ' . $password . PHP_EOL;

If you remove the last echo, the password should never show up, but you can use that for validation obvoiusly.

如果您删除最后一个回显,密码将永远不会出现,但您可以使用它进行验证。

回答by Randy

Depending on your environment (i.e., not on Windows), you can use the ncurses library (specifically, the ncurses_noecho()function to stop keyboard echo and ncurses_getch()to read the input) to get the password without displaying it on screen.

根据您的环境(即,不是在 Windows 上),您可以使用 ncurses 库(特别是ncurses_noecho()函数来停止键盘回显和ncurses_getch()来读取输入)来获取密码而不在屏幕上显示它。

回答by Pitpat

The below method works under Linux CLI but not under Windows CLI or Apache. It also only works with chars in the standard Ascii table (It would not take much to make it compatible with extended char sets though).

以下方法适用于 Linux CLI,但不适用于 Windows CLI 或 Apache。它也只适用于标准 Ascii 表中的字符(尽管使它与扩展字符集兼容并不需要太多)。

I have put a bit of code in to protect against copy and paste passwords. If the bit between the two comments is removed then a password can be injected/pasted in.

我已经放入了一些代码来防止复制和粘贴密码。如果删除了两个注释之间的位,则可以注入/粘贴密码。

I hope this helps someone.

我希望这可以帮助别人。

<?php

    echo("Password: ");
    $strPassword=getObscuredText();
    echo("\n");
    echo("You entered: ".$strPassword."\n");

    function getObscuredText($strMaskChar='*')
    {
        if(!is_string($strMaskChar) || $strMaskChar=='')
        {
            $strMaskChar='*';
        }
        $strMaskChar=substr($strMaskChar,0,1);
        readline_callback_handler_install('', function(){});
        $strObscured='';
        while(true)
        {
            $strChar = stream_get_contents(STDIN, 1);
            $intCount=0;
// Protect against copy and paste passwords
// Comment \/\/\/ to remove password injection protection
            $arrRead = array(STDIN);
            $arrWrite = NULL;
            $arrExcept = NULL;
            while (stream_select($arrRead, $arrWrite, $arrExcept, 0,0) && in_array(STDIN, $arrRead))            
            {
                stream_get_contents(STDIN, 1);
                $intCount++;
            }
//        /\/\/\
// End of protection against copy and paste passwords
            if($strChar===chr(10))
            {
                break;
            }
            if ($intCount===0)
            {
                if(ord($strChar)===127)
                {
                    if(strlen($strObscured)>0)
                    {
                        $strObscured=substr($strObscured,0,strlen($strObscured)-1);
                        echo(chr(27).chr(91)."D"." ".chr(27).chr(91)."D");
                    }
                }
                elseif ($strChar>=' ')
                {
                    $strObscured.=$strChar;
                    echo($strMaskChar);
                    //echo(ord($strChar));
                }
            }
        }
        readline_callback_handler_remove();
        return($strObscured);
    }
?>

回答by mpyw

This is the easiest solution for all platforms:

这是适用于所有平台的最简单的解决方案:

function prompt($message = 'prompt: ', $hidden = false) {
    if (PHP_SAPI !== 'cli') {
        return false;
    }
    echo $message;
    $ret = 
        $hidden
        ? exec(
            PHP_OS === 'WINNT' || PHP_OS === 'WIN32'
            ? __DIR__ . '\prompt_win.bat'
            : 'read -s PW; echo $PW'
        )
        : rtrim(fgets(STDIN), PHP_EOL)
    ;
    if ($hidden) {
        echo PHP_EOL;
    }
    return $ret;
}

Then create prompt_win.batin the same directory:

然后prompt_win.bat在同一个目录下创建:

SetLocal DisableDelayedExpansion
Set "Line="
For /F %%# In ('"Prompt;$H & For %%# in (1) Do Rem"') Do (
    Set "BS=%%#"
)

:loop_start
    Set "Key="
    For /F "delims=" %%# In ('Xcopy /L /W "%~f0" "%~f0" 2^>Nul') Do (
        If Not Defined Key (
            Set "Key=%%#"
        )
    )
    Set "Key=%Key:~-1%"
    SetLocal EnableDelayedExpansion
    If Not Defined Key (
        Goto :loop_end
    )
    If %BS%==^%Key% (
        Set "Key="
        If Defined Line (
            Set "Line=!Line:~0,-1!"
        )
    )
    If Not Defined Line (
        EndLocal
        Set "Line=%Key%"
    ) Else (
        For /F "delims=" %%# In ("!Line!") Do (
            EndLocal
            Set "Line=%%#%Key%"
        )
    )
    Goto :loop_start
:loop_end

Echo;!Line!

回答by JMW

Works on every windows system, that has powershell support.(source from: http://www.qxs.ch/2013/02/08/php-cli-password-prompts-on-windows-7/)

适用于所有具有 powershell 支持的 Windows 系统。(来源:http: //www.qxs.ch/2013/02/08/php-cli-password-prompts-on-windows-7/

<?php
// please set the path to your powershell, here it is: C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe
$pwd=shell_exec('C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -Command "$Password=Read-Host -assecurestring \"Please enter your password\" ; $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) ; echo $PlainPassword;"');
$pwd=explode("\n", $pwd); $pwd=$pwd[0];
echo "You have entered the following password: $pwd\n";

回答by Gabriel Gilini

I guess that there is no simple way of doing it (actually I can't think of any way) without using stty -echo. If you intent running it on windows, you could create a batch script that would provide the unechoed typed info to your php script.

我想没有使用 stty -echo 就没有简单的方法(实际上我想不出任何方法)。如果您打算在 Windows 上运行它,您可以创建一个批处理脚本,为您的 php 脚本提供未回声的类型信息。

@echo off
cls
SET /P uname=Enter Username:
echo hP1X500P[PZBBBfh#b##fXf-V@`$fPf]f3/f1/5++u5>in.com
set /p password=Enter password :<nul
for /f “tokens=*” %%i in ('in.com') do set password=%%i
del in.com
echo.
c:\php\php.exe d:\php\test.php %uname% “%password%”
Pause

example taken from http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/

示例取自http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/

回答by SchizoDuckie

Why not use an SSH connection? You can abstract the commands away, redirect input/output and have full control.

为什么不使用 SSH 连接?您可以将命令抽象化,重定向输入/输出并拥有完全控制权。

You can provide someone with a pure clean shell with as little rights as neccesary, and let the password just be POST'ed along with to SSH2::Connect() to open the shell.

您可以为某人提供一个拥有尽可能少的权限的纯粹干净的外壳,并让密码与 SSH2::Connect() 一起发布以打开外壳。

I created a nice class to work with the php SSH2 extension, maybe it helps you; (and it also does secure file transfers)

我创建了一个很好的类来使用 php SSH2 扩展,也许对你有帮助;(而且它还可以安全地传输文件)

<?php

/**
 * SSH2
 * 
 * @package Pork
 * @author SchizoDuckie
 * @version 1.0
 * @access public
 */
class SSH2
{
    private $host;
    private $port;
    private $connection;
    private $timeout;
    private $debugMode;
    private $debugPointer;
    public $connected; 
    public $error;


    /**
     * SSH2::__construct()
     * 
     * @param mixed $host
     * @param integer $port
     * @param integer $timeout
     * @return
     */
    function __construct($host, $port=22, $timeout=10)
    {
        $this->host = $host;
        $this->port = $port;
        $this->timeout = 10;
        $this->error = 'not connected';
        $this->connection = false;
        $this->debugMode = Settings::Load()->->get('Debug', 'Debugmode');
        $this->debugPointer = ($this->debugMode) ? fopen('./logs/'.date('Y-m-d--H-i-s').'.log', 'w+') : false;
        $this->connected = false;

    }


    /**
     * SSH2::connect()
     * 
     * @param mixed $username
     * @param mixed $password
     * @return
     */
    function connect($username, $password)
    {
        $this->connection = ssh2_connect($this->host, $this->port);
        if (!$this->connection) return $this->error("Could not connect to {$this->host}:{$this->port}");
        $this->debug("Connected to {$this->host}:{$this->port}");
        $authenticated = ssh2_auth_password($this->connection, $username, $password);
        if(!$authenticated) return $this->error("Could not authenticate: {$username}, check your password");
        $this->debug("Authenticated successfully as {$username}");
        $this->connected = true;

        return true;
    }

    /**
     * SSH2::exec()
     *
     * @param mixed $command shell command to execute
     * @param bool $onAvailableFunction a function to handle any available data.
     * @param bool $blocking blocking or non-blocking mode. This 'hangs' php execution until the command has completed if you set it to true. If you just want to start an import and go on, use this icm onAvailableFunction and false
     * @return
     */
    function exec($command, $onAvailableFunction=false, $blocking=true)
    {
        $output = '';
        $stream = ssh2_exec($this->connection, $command);
        $this->debug("Exec: {$command}");
        if($onAvailableFunction !== false)
        {
            $lastReceived = time();
            $timeout =false;
            while (!feof($stream) && !$timeout)
            {
                $input = fgets($stream, 1024);
                if(strlen($input) >0)
                {
                    call_user_func($onAvailableFunction, $input);
                    $this->debug($input);
                    $lastReceived = time();
                }
                else
                {
                    if(time() - $lastReceived >= $this->timeout)
                    {
                        $timeout = true;
                        $this->error('Connection timed out');
                        return($this->error);
                    }
                }
            }
        }
        if($blocking === true && $onAvailableFunction === false)
        {
            stream_set_blocking($stream, true);
            $output = stream_get_contents($stream);
            $this->debug($output);
        }
        fclose($stream);
        return($output);
    }


    /**
     * SSH2::createDirectory()
     *
     * Creates a directory via sftp
     *
     * @param string $dirname
     * @return boolean success
     *  
     */
    function createDirectory($dirname)
    {
        $ftpconnection = ssh2_sftp ($this->connection);
        $dircreated = ssh2_sftp_mkdir($ftpconnection, $dirname, true);
        if(!$dircreated) 
        {
            $this->debug("Directory not created: ".$dirname);
        }
        return $dircreated;
    }

    public function listFiles($dirname)
    {
        $input = $this->exec(escapeshellcmd("ls  {$dirname}"));
        return(explode("\n", trim($input)));

    }

    public function sendFile($filename, $remotename)
    {
        $this->debug("sending {$filename} to {$remotename} ");
        if(file_exists($filename) && is_readable($filename))
        {
            $result = ssh2_scp_send($this->connection, $filename, $remotename, 0664);
        }
        else
        {
            $this->debug("Unable to read file : ".$filename);
            return false;
        }
        if(!$result) $this->debug("Failure uploading {$filename} to {$remotename}");
        return $result;
    }

    public function getFile($remotename, $localfile)
    {
        $this->debug("grabbing {$remotename} to {$localfile}");
        $result = ssh2_scp_recv($this->connection, $remotename, $localfile);

        if(!$result) $this->debug("Failure downloading {$remotename} to {$localfile}");
        return $result;
    }

    /**
     * SSH2::debug()
     * 
     * @param mixed $message
     * @return
     */
    function debug($message) 
    {
        if($this->debugMode)
        {
            fwrite($this->debugPointer, date('Y-m-d H:i:s')." : ".$message."\n");
        }
    }



    /**
     * SSH2::error()
     * 
     * @param mixed $errorMsg
     * @return
     */
    function error($errorMsg) 
    {
        $this->error = $errorMsg;
        $this->debug($errorMsg);
        return false;
    }   

    /**
     * SSH2::__destruct()
     * 
     * @return
     */
    function __destruct() 
    {
        if($this->connection){
            $this->connection = null;
        }
        if($this->debugMode && $this->debugPointer)
        {
            fclose($this->debugPointer);
        }
    }       


}

Usage example:

用法示例:

$settings = Settings::Load()->Get("SecureServer");
$ssh = new SSH2($settings['host']);
if( $ssh->connect($settings['username'], $settings['password']))
{
    echo $ssh->exec("ls -la ".$settings['path'], false, true);  
    flush();    
}

回答by Slipo

Theorically you can do it using stream_set_blocking(), but looks like there are some PHP bugs managing STDIN.

从理论上讲,您可以使用 stream_set_blocking() 来做到这一点,但看起来有一些 PHP 错误管理 STDIN。

Look: http://bugs.php.net/bug.php?id=34972http://bugs.php.net/bug.php?id=36030

看:http: //bugs.php.net/bug.php? id= 34972 http://bugs.php.net/bug.php?id=36030

Try yourself:

自己试试:

echo "Enter Password: ";
$stdin = fopen('php://stdin','r');
// Trying to disable stream blocking
stream_set_blocking($stdin, FALSE) or die ('Failed to disable stdin blocking');
// Trying to set stream timeout to 1sec
stream_set_timeout ($stdin, 1) or die ('Failed to enable stdin timeout');

回答by robert petranovic

The accepted answer is not good enough. First of all, the Windows solution doesn't work on Windows 7 and above. The solution for other OSs depends on Bash and bash built-in 'read'. However, there are systems which does not use Bash (eg. OpenBSD) and where this obviously won't work.

接受的答案还不够好。首先,Windows 解决方案不适用于 Windows 7 及更高版本。其他操作系统的解决方案取决于 Bash 和 bash 内置的“读取”。但是,有些系统不使用 Bash(例如 OpenBSD)并且这显然不起作用。

In this blogI've discussed solution which works on almost any Unix based OS and Windows from 95 to 8. The Windows solution uses external program written in C on top Win32 API. The solution for other OSs uses external command 'stty'. I have yet to see a Unix based system which does not have 'stty'

在这篇博客中,我讨论了适用于几乎所有基于 Unix 的操作系统和从 95 到 8 的 Windows 的解决方案。Windows 解决方案使用在 Win32 API 之上用 C 编写的外部程序。其他操作系统的解决方案使用外部命令“stty”。我还没有看到没有“stty”的基于 Unix 的系统