programing

PHP의 명령줄 암호 프롬프트

firstcheck 2022. 11. 7. 21:44
반응형

PHP의 명령줄 암호 프롬프트

웹 앱에 도움이 되는 명령줄 도구를 쓰고 있습니다.서비스에 연결하려면 암호가 필요합니다.명령줄 인수로 전달할 필요가 없도록 스크립트에 비밀번호 프롬프트가 표시되도록 합니다.

그건 쉬운데, 비밀번호가 입력된 대로 화면에 반영되지 않았으면 좋겠어요.PHP로 어떻게 하면 좋을까요?

순수 PHP로 하면 보너스 포인트 (아니요)system('stty')를 사용하여 문자를 치환합니다.*.

편집:

이 스크립트는 Unix와 같은 시스템(Linux 또는 Mac)에서 실행됩니다.스크립트는 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()전화 왔어요.

사이트 포인트에 있습니다.

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

환경에 따라서는(즉, Windows가 아닌) ncurses 라이브러리(구체적으로는 키보드 에코를 정지하기 위한 ncurses_noecho() 함수, 입력을 읽기 위한 ncurses_getch() 함수)를 사용하여 비밀번호를 화면에 표시하지 않고 얻을 수 있습니다.

hiddenput.exe 파일을 사용하면 화면 어디에도 정보가 유출되지 않고 실제 숨겨진 입력을 얻을 수 있습니다.

<?php

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

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

마지막 에코를 삭제하면 패스워드는 표시되지 않지만 검증에 사용할 수 있습니다.

다음 방법은 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);
    }
?>

이것은 모든 플랫폼에서 가장 쉬운 솔루션입니다.

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!

stty - echo를 사용하지 않고 간단하게 할 수 있는 방법은 없다고 생각합니다.만약 당신이 그것을 윈도우에서 실행하려고 한다면, 당신은 당신의 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/에서 인용한 예

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

받아들여진 답변은 충분하지 않다.우선, Windows 솔루션은 Windows 7 이상에서는 동작하지 않습니다.다른 OS의 솔루션은 Bash와 bash가 내장된 '읽기'에 따라 달라집니다.단, Bash를 사용하지 않는 시스템이 있습니다(예:OpenBSD)와 이것이 확실히 기능하지 않는 부분.

블로그에서는 95부터8까지의 거의 모든 Unix 기반의 OS와 Windows에서 동작하는 솔루션에 대해 설명했습니다.Windows 솔루션은 최상위 Win32 API에서 C로 작성된 외부 프로그램을 사용합니다.다른 OS용 솔루션에서는 외부 명령어 'stty'를 사용합니다.아직 'stty'가 없는 Unix 기반 시스템을 보지 못했습니다.

SSH 접속을 사용하는 이유는 무엇입니까?명령어를 추상화하고 입력/출력을 리다이렉트하여 완전한 제어를 할 수 있습니다.

necesary만큼 적은 권한을 가진 깨끗한 셸을 누군가에게 제공하고 SSH2::Connect()와 함께 비밀번호를 POST하여 셸을 열 수 있습니다.

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


}

사용 예:

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

이론적으로는 stream_set_blocking()을 사용하여 실행할 수 있지만 STDIN을 관리하는 PHP 버그가 있는 것 같습니다.

http://bugs.php.net/bug.php?id=34972 http://bugs.php.net/bug.php?id=36030 를 참조해 주세요.

직접 사용해 보세요.

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');
system('stty -echo');

현재 단말 에코를 디세블로 합니다.

system('stty echo');

을 사용하다fgets.

언급URL : https://stackoverflow.com/questions/187736/command-line-password-prompt-in-php

반응형