php 实现webshell

  • Post author:
  • Post category:php


php实现webshell主要依靠swoole和ssh2扩展,前端展示页面使用xterm

废话不多说直接上代码



后端代码

<?php

/**
 * websocket服务器(使用swoole)
 * 使用ssh登录服务器
 */
class Ws{
    private $shell;
    private $connection;
    private $isConnection;

    private $ws;
    public function __construct(){
        //监听0.0.0.0:8081端口
        $this->ws = new Swoole\WebSocket\Server('0.0.0.0', 8081);

        $this->ws->on("open", [$this, "onOpen"]);
        $this->ws->on("message", [$this, "onMessage"]);
        $this->ws->on("close", [$this, "onClose"]);

        $this->ws->start();
    }

    //监听WebSocket连接打开事件
    public function onOpen($ws, $request){
        var_dump($request->fd, $request->server);
    }

    //监听WebSocket消息事件
    public function onMessage($ws, $frame){
        $data = json_decode($frame->data, true);
        switch(key($data)){
            case "data":    //输入命令
                fwrite($this->shell[$frame->fd], $data['data']);
                usleep(800);
                while($line = fgets($this->shell[$frame->fd])){
                    $ws->push($frame->fd, $line);
                }
            break;
            case "auth":    //登录
                if($this->loginSSH($data["auth"], $frame)){
                    $ws->push($frame->fd, "连接中...");
                    while($line = fgets($this->shell[$frame->fd])){
                        $ws->push($frame->fd, $line);
                    }
                } else {
                    $ws->push($frame->fd, "登录失败");
                }
            break;
            default:
                //清理空白行
                if($this->isConnection[$frame->fd]){
                    while($line = fgets($this->shell[$frame->fd])){
                        $ws->push($frame->fd, $line);
                    }
                }
            break;
        }
    }

    //监听WebSocket连接关闭事件
    public function onClose($ws, $fd){
        $this->isConnection[$fd] = false;
        echo "client-{$fd} is closed\n";
    }

    //ssh登录
    public function loginSSH($auth, $frame){
        //通过SSH连接服务器
        $this->connection[$frame->fd] = ssh2_connect($auth['server'], $auth['port']);
        //验证身份(登录)
        if(ssh2_auth_password($this->connection[$frame->fd], $auth['user'], $auth['password'])){
            //使用流的方式打开shell
            $this->shell[$frame->fd] = ssh2_shell($this->connection[$frame->fd], 'xterm', null, 80, 24, SSH2_TERM_UNIT_CHARS);
            sleep(1);   //延迟执行一秒等待服务器
            $this->isConnection[$frame->fd] = true;
            return true;
        } else {
            return false;
        }
    }
}

new ws();



前端页面代码

<!--使用"npm install xterm"安装xterm-->
<!doctype html>
  <html>
    <head>
      <link rel="stylesheet" href="node_modules/xterm/dist/xterm.css" />
      <script src="node_modules/xterm/dist/xterm.js"></script>
      <script src="node_modules/xterm/dist/addons/attach/attach.js"></script>
      <script src="node_modules/xterm/dist/addons/fit/fit.js"></script>
      <style>
      body {font-family: Arial, Helvetica, sans-serif;}

      input[type=text], input[type=password], input[type=number] {
          width: 100%;
          padding: 12px 20px;
          margin: 8px 0;
          display: inline-block;
          border: 1px solid #ccc;
          box-sizing: border-box;
      }

      button {
          background-color: #4CAF50;
          color: white;
          padding: 14px 20px;
          margin: 8px 0;
          border: none;
          cursor: pointer;
          width: 100%;
      }

      button:hover {
          opacity: 0.8;
      }

      .serverbox {
          padding: 16px;
          border: 3px solid #f1f1f1;
          width: 25%;
          position: absolute;
          top: 15%;
          left: 37%;
      }
      </style>
    </head>
    <body>

      <div id="terminal" style="width:100%; height:90vh;visibility:hidden"></div>
      
      <script>
        var resizeInterval;
        //连接websocket服务器
        var wSocket = new WebSocket("ws:服务器地址:8081");
        Terminal.applyAddon(attach);  // Apply the `attach` addon
        Terminal.applyAddon(fit);  //Apply the `fit` addon
        var term = new Terminal({
				  cols: 80,
				  rows: 24
        });
        term.open(document.getElementById('terminal'));

        function ConnectServer(){
          //ssh登录服务器需要的信息
          var server = "";      //服务器地址
          var port = 22;      //ssh端口
          var user = "";      //账号
          var password = "";      //密码
          
          document.getElementById("terminal").style.visibility="visible";
          var dataSend = {"auth":
                            {
                            "server":server, //document.getElementById("server").value,
                            "port":port, //document.getElementById("port").value,
                            "user":user, //document.getElementById("user").value,
                            "password":password, //document.getElementById("password").value
                            }
                          };
          wSocket.send(JSON.stringify(dataSend));

          term.fit();
          term.focus();
        }       

        wSocket.onopen = function (event) {
          console.log("打开连接");
          term.attach(wSocket,false,false);
          window.setInterval(function(){
            wSocket.send(JSON.stringify({"refresh":""}));
          }, 700);
        };

        wSocket.onerror = function (event){
          term.detach(wSocket);
          alert("Connection Closed");
        }        
        
        term.on('data', function (data) {
          var dataSend = {"data":data};
          wSocket.send(JSON.stringify(dataSend));
          //Xtermjs with attach dont print zero, so i force. Need to fix it :(
          if (data=="0"){
            term.write(data);
          }
        })
        
        //Execute resize with a timeout
        window.onresize = function() {
          clearTimeout(resizeInterval);
          resizeInterval = setTimeout(resize, 400);
        }
        // Recalculates the terminal Columns / Rows and sends new size to SSH server + xtermjs
        function resize() {
          if (term) {
            term.fit()
          }
        }

    window.onload=ConnectServer;
      </script>
    </body>
  </html>



最终效果



本功能主要参考以下实现


webssh



ssh2扩展



版权声明:本文为echo_heng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。