2008-10-09 38 views
66

Tôi đang viết một công cụ dòng lệnh để trợ giúp ứng dụng web của mình. Nó cần mật khẩu để kết nối với dịch vụ. Tôi muốn kịch bản để hiển thị một lời nhắc mật khẩu vì vậy tôi không phải vượt qua nó như là một đối số dòng lệnh.Dấu nhắc mật khẩu dòng lệnh bằng PHP

Thật dễ dàng, nhưng tôi muốn nó không lặp lại mật khẩu trên màn hình khi được nhập. Làm thế nào tôi có thể làm điều này với PHP?

Điểm thưởng để thực hiện trong PHP thuần túy (không system('stty')) và thay thế các ký tự bằng *.

EDIT:

Các kịch bản sẽ chạy trên một unix như hệ thống (Linux hoặc Mac). Kịch bản được viết bằng PHP, và rất có thể sẽ ở lại như thế.

Ngoài ra, cho các hồ sơ, cách stty để làm việc đó là:

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

tôi muốn không có system() cuộc gọi trong đó.

+0

Kịch bản dòng lệnh sẽ chạy trên hệ điều hành nào? Liệu kịch bản dòng lệnh có được viết bằng ngôn ngữ PHP hoặc ngôn ngữ kịch bản lệnh của hệ điều hành không? –

Trả lời

37

Tìm thấy trên 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; 
    } 
} 
+2

Không hoạt động trên Windows 7. Theo các diễn đàn trực tuyến khác nhau, sẽ không làm việc trên bất cứ điều gì khác hơn Windows XP và Máy chủ 2003. – Tgr

+0

Xem câu trả lời của tôi bên dưới (hoặc truy cập https://github.com/Seldaek/hidden-input trực tiếp) để biết giải pháp hoạt động từ XP lên đến 7, 32/64bit và không có dấu nhắc xấu xí xuất hiện. – Seldaek

+0

http://www.qxs.ch/2013/02/08/php-cli-password-prompts-on-windows-7/ – JMW

2

Tôi đoán rằng không có cách đơn giản để làm điều đó (thực sự tôi không thể nghĩ ra bất kỳ cách nào) mà không sử dụng stty-doo. Nếu bạn định chạy nó trên các cửa sổ, bạn có thể tạo một tập lệnh theo lô sẽ cung cấp thông tin đã nhập chưa được xử lý cho tập lệnh php của bạn.

@echo off 
cls 
SET /P uname=Enter Username: 
echo hP1X500P[PZBBBfh#b##[email protected]`$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 

dụ lấy từ http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/

+0

mẹo hay để tạo tệp COM chỉ văn bản (trông giống như kiểm tra chống vi-rút EICAR ;-)) không may điều này sẽ không hoạt động trong Windows 64 bit ... (không hỗ trợ COM 16 bit ... và tạo một tập tin EXE theo cách này ... chúc may mắn!) – Ale

9

Tùy thuộc vào môi trường của bạn (ví dụ, không phải trên Windows), bạn có thể sử dụng thư viện ncurses (đặc biệt là các ncurses_noecho() chức năng để ngăn chặn tiếng vang bàn phím và ncurses_getch() để đọc các đầu vào) để lấy mật khẩu mà không hiển thị nó trên màn hình.

1

Tại sao không sử dụng kết nối SSH? Bạn có thể tóm tắt các lệnh đi, chuyển hướng đầu vào/đầu ra và có toàn quyền kiểm soát.

Bạn có thể cung cấp cho ai đó một vỏ sạch thuần túy với ít quyền nhất thiết, và để mật khẩu chỉ được POST'ed cùng với SSH2 :: Connect() để mở trình bao.

Tôi đã tạo một lớp học tốt để làm việc với phần mở rộng php SSH2, có thể nó sẽ giúp bạn; (và nó cũng thực hiện chuyển file an toàn) ví dụ

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


} 

Cách sử dụng:

$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

Tôi gặp lỗi: Lỗi nghiêm trọng PHP: Lớp 'Cài đặt' không tìm thấy trong /home/tester/tools/SSH/conn_ssh3.php trên dòng 2, tôi đặt tên ssh2 lớp như Settings.php, và cũng đã cố gắng để thay đổi Settings :: Load() để SSH2 :: Load() – kamal

-1

theorically bạn có thể làm điều đó bằng stream_set_blocking(), nhưng hình như có một số lỗi PHP quản lý STDIN.

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

Hãy thử tự hỏi:

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');
11

Bạn có thể sử dụng tệp hiddeninput.exe để nhận thông tin ẩn thực sự mà không làm rò rỉ thông tin ở bất kỳ đâu trên màn hình.

<?php 

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

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

Nếu bạn loại bỏ tiếng vọng cuối cùng, mật khẩu sẽ không bao giờ xuất hiện, nhưng bạn có thể sử dụng mật khẩu đó để xác thực.

+1

Không hoạt động trên hộp linux của tôi vì lý do rõ ràng. –

1

Câu trả lời được chấp nhận không đủ tốt. Trước hết, giải pháp Windows không hoạt động trên Windows 7 trở lên. Giải pháp cho các hệ điều hành khác phụ thuộc vào Bash và bash được xây dựng trong 'đọc'. Tuy nhiên, có những hệ thống không sử dụng Bash (ví dụ OpenBSD) và điều này rõ ràng sẽ không hoạt động.

Trong số blog Tôi đã thảo luận giải pháp hoạt động trên hầu hết mọi hệ điều hành dựa trên Unix và Windows từ 95 đến 8. Giải pháp Windows sử dụng chương trình bên ngoài được viết bằng C trên API Win32 hàng đầu. Giải pháp cho các hệ điều hành khác sử dụng lệnh bên ngoài 'stty'. Tôi chưa thấy một hệ thống dựa trên Unix mà không có 'stty'

+1

Tôi nghĩ sẽ tốt hơn nếu bạn cung cấp một phiên bản ngắn gọn cho bài đăng của mình ở đây (có thể chỉ với các ví dụ cho mỗi cách tiếp cận), đơn giản là việc đăng liên kết là trái với ý tưởng ban đầu của trang web SE. – user907860

+0

Có thư viện chung cung cấp khả năng nhắc người dùng nhập mật khẩu, văn bản thuần túy thông thường và các tùy chọn trình đơn trên nền tảng không? – CMCDragonkai

+0

@CMCDragonkai Không, không có. Tính năng như vậy không được thực hiện trong PHP, do đó, nó không thể được thực hiện bằng cách sử dụng chỉ PHP. Có một phần mở rộng PHP ncurses nhưng nó không hoạt động trên Windows. –

2

Đây là giải pháp đơn giản nhất cho tất cả các nền tảng:

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

Sau đó tạo prompt_win.bat trong cùng thư mục:

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! 
+0

'rtrim' có thể tách các ký tự hợp lệ (tức là, bất kỳ khoảng trắng nào kết thúc chuỗi), nhưng bạn có thể bỏ qua nó và sử dụng' echo -n' để thay thế. – Synexis

1

Hoạt động trên mọi hệ thống cửa sổ, có hỗ trợ quyền hạn. (nguồn từ: 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"; 
0

Tôi đã định dạng lại JMW 's giải pháp 3 dòng vì vậy bạn chỉ có thể cắt và dán nó vào mã PHP hiện tại của bạn.

function getPassword() 
{ 
    $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]; 
    return $pwd; 
} 

Để sử dụng nó:

$usersPassword=getPassword(); 

Tôi đang trên Powershell V5.0 nhưng đường dẫn thư mục vẫn cho thấy là v1.0 nên chuỗi trích dẫn trong các cuộc gọi shell_exec nên OK.

Các vấn đề liên quan