web-dev-qa-db-ja.com

PHPのコマンドラインパスワードプロンプト

Webアプリを支援するコマンドラインツールを作成しています。サービスに接続するにはパスワードが必要です。スクリプトにパスワードプロンプトを表示したいので、コマンドライン引数として渡す必要はありません。

それは十分簡単ですが、入力したパスワードを画面にエコーしないようにしたいと思います。 PHPでこれを行うにはどうすればよいですか?

純粋なPHP(system('stty')なし)でそれを実行し、文字を_*_に置き換えることによるボーナスポイント。

編集:

スクリプトは、システム(LinuxまたはMac)のようなUNIX上で実行されます。このスクリプトはPHPで記述されており、ほとんどの場合そのままです。

また、レコードの場合、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";
_

system()呼び出しをそこに入れたくないのですが。

70
Gary Richardson

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;
  }
}
39
DaveHauenstein

hiddeninput.exe ファイルを使用すると、画面上のどこにでも情報を漏らすことなく、実際の隠された入力を取得できます。

<?php

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

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

最後のエコーを削除すると、パスワードが表示されることはありませんが、検証のためにそれを使用できます。

9
Seldaek

環境に応じて(Windows以外)、ncursesライブラリを使用できます(具体的には、 ncurses_noecho() キーボードエコーを停止し、 ncurses_getch() を読み取ります入力)画面に表示せずにパスワードを取得します。

9
Randy

これは、すべてのプラットフォームで最も簡単なソリューションです。

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

次に、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!
2
mpyw

以下の方法はLinux CLIで機能しますが、Windows CLIまたはApacheでは機能しません。また、標準のAsciiテーブルの文字でのみ機能します(ただし、拡張文字セットとの互換性を確保するのにそれほど時間はかかりません)。

コピーアンドペーストパスワードから保護するためのコードを少し入れました。 2つのコメントの間のビットを削除すると、パスワードを挿入/貼り付けできます。

これが誰かの助けになることを願っています。

<?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);
    }
?>
1
Pitpat

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

http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/ からの例

1
Gabriel Gilini

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";
1
JMW

SSH接続を使用しないのはなぜですか?コマンドを抽象化して、入出力をリダイレクトし、完全に制御できます。

誰かに必要最低限​​の権限を持つ純粋できれいなシェルを提供し、SSH2 :: Connect()にパスワードをPOSTしてシェルを開くことができます。

PHPのSSH2拡張機能で動作するNiceクラスを作成しました。おそらく役立つでしょう。 (また、安全なファイル転送も行います)

<?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);
        }
    }       


}

使用例:

$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();    
}
0
SchizoDuckie

受け入れられた答えは十分ではありません。まず、WindowsソリューションはWindows 7以降では機能しません。他のOSのソリューションは、Bashおよびbashの組み込み「読み取り」に依存しています。ただし、Bashを使用しないシステム(OpenBSDなど)があり、これが明らかに機能しない場合があります。

これで blog 95から8までのほとんどすべてのUnixベースのOSおよびWindowsで動作するソリューションについて説明しました。WindowsソリューションはCで書かれた外部Win32 APIを使用します。他のOSのソリューションは、外部コマンド「stty」を使用します。 「stty」を持たないUnixベースのシステムはまだ見ていません

0

理論的にはstream_set_blocking()を使用して実行できますが、PHP STDINを管理するバグがあるようです。

見て: http://bugs.php.net/bug.php?id=34972http://bugs.php.net/bug.php?id=360

試してみてください:

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');
0
Slipo