logo NodeSeekbeta

Pure Shell HTTP Server

  1. 这是一个纯 shell 实现的 HTTP 服务器,能够使用 GET/POST 请求读写 Working directory 下的文件。
  2. 可以通过 PORT 和 KEY 环境变量设置监听的端口和写入操作的 API Key 。如果没有指定,那么会自动生成一个 UUID 作为 API Key ,并且在控制台打印出来。写入操作应该设置 Authorization 头为: APIKey 你的 API 密钥。
  3. 对于中文或者没有正确 URL Encode 的请求支持应该不太好。
  4. 如果想要结束服务器,请在服务器目录下运行 kill -9 $(cat server.pid)。
  5. 虽然做了一些安全措施,但是 shell 写的东西总归有些危险,不建议在有个人数据且暴露公网的设备上长期运行。
  6. 当然,这个服务器很简陋,也不支持多进程、多连接、可能有 race condition 问题,但是 just for fun ,更多的是作为一个「 shell 也可以写 HTTP 服务器」的概念验证。如果对代码有什么建议也欢迎提出。
#!/bin/bash
function server {
   read il
   echo "recv_il: $il" >&2
   method=$( echo "$il" | cut -d" " -f1)
   path=$(   echo "$il" | cut -d" " -f2)
   proto=$(  echo "$il" | cut -d" " -f3)
   echo "method: $method" >&2
   echo "path:   $path"   >&2
   echo "proto:  $proto"  >&2

   declare -A hdr
   while read line; do
   	sline=`echo $line | tr -d '[\r\n]'`
   	[ -z "$sline" ] && break
   	echo "recv_hdr: $line" >&2
   	hdr_k=$(echo $line | cut -d":" -f1)
   	hdr_v=$(echo $line | cut -d":" -f2- | cut -c2- | tr -d '[\r\n]')
   	hdr[$hdr_k]="$hdr_v"
   done
   echo "recv_hdr_end" >&2

   relpath=$(realpath "$(pwd)$path")
   if [[ "$relpath" != "$(pwd)"* ]]; then
   	echo "possible path traversal attack: $relpath" >&2
   	echo "pwd:     $(pwd)"   >&2
   	echo "relpath: $relpath" >&2
   	echo -ne "HTTP/1.1 403 Forbidden\r\n"
   	echo -ne "\r\n"
   	echo -ne "possible path traversal attack: $relpath"
   	exit 0
   fi
   echo "relpath: $relpath" >&2
   if [ $method = "GET" ]; then
   	if [ ! -f "$relpath" ]; then
   		echo -ne "HTTP/1.1 404 Not Found\r\n"
   		echo -ne "\r\n"
   		echo -ne "not found: $relpath"
   		exit 0
   	fi
   	echo -ne "HTTP/1.1 200\r\n"
   	echo -ne "\r\n"
   	cat  "$relpath"
   	exit 0
   fi

   if [ $method != "POST" ]; then
   	echo -ne "HTTP/1.1 405 Method Not Allowed\r\n"
   	echo -ne "\r\n"
   	exit 0
   fi

   if [ "${hdr[Authorization]}" != "APIKey $KEY" ]; then
   	echo -ne "HTTP/1.1 401 Unauthorized\r\n"
   	echo -ne "\r\n"
   	exit 0
   fi

   body_file=$(mktemp)
   body_len=0
   if [ ! -z "${hdr[Content-Length]}" ]; then
   	echo "hdr_cl > body_len"   >&2
   	body_len="${hdr[Content-Length]}"
   fi
   echo "body_len:  $body_len"    >&2
   echo "body_file: $body_file"   >&2
   dd of=$body_file bs=1 count=$body_len

   mkdir -p $(dirname "$relpath") >&2
   cp -v $body_file "$relpath"    >&2

   echo -ne "HTTP/1.1 201 Created\r\n"
   echo -ne "\r\n"
   echo -ne "created: $relpath\r\n"
   rm -rvf $body_file             >&2
}

if [ -z "$PORT" ]; then
   PORT=3000
fi

if [ -z "$KEY" ]; then
   KEY=$(uuidgen)
fi

if [ "$EXEC" = "server" ]; then
   server
   exit 0
fi

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
PID=$$
PIDFILE="$SCRIPT_DIR/server.pid"

if [[ -f "$PIDFILE" && -d "/proc/$(cat $PIDFILE)" ]]; then
   echo "one instance is running, refuse to start another"
   exit 1
fi

if [ -f "server.sh" ]; then
   echo "DO NOT START SERVER WHEN CURRENT WORKING DIRECTORY IS SAME AS SCRIPT DIRECTORY"
   echo "THIS MAY CAUSE UNEXPECTED OVERWRITING SERVER AND RCE"
   echo "EXITING"
   exit 1
fi

echo "PID=$PID"
echo $PID > $PIDFILE
echo "PIDFILE=$PIDFILE"
echo "KEY=$KEY"

while true; do
   nc -vlp $PORT -c "EXEC=server KEY=$KEY $0"
done

(好像 NS 不支持代码高亮?

  • nc命令不是系统自带,需要安装,换socat还支持端口复用

    不过都需要安装包了,直接安装其他的更好的http server不好吗 xhj002

  • #!/bin/bash
    function server {
       read il
       echo "recv_il: $il" >&2
       method=$( echo "$il" | cut -d" " -f1)
       path=$(   echo "$il" | cut -d" " -f2)
       proto=$(  echo "$il" | cut -d" " -f3)
       echo "method: $method" >&2
       echo "path:   $path"   >&2
       echo "proto:  $proto"  >&2
    
       declare -A hdr
       while read line; do
       	sline=`echo $line | tr -d '[\r\n]'`
       	[ -z "$sline" ] && break
       	echo "recv_hdr: $line" >&2
       	hdr_k=$(echo $line | cut -d":" -f1)
       	hdr_v=$(echo $line | cut -d":" -f2- | cut -c2- | tr -d '[\r\n]')
       	hdr[$hdr_k]="$hdr_v"
       done
       echo "recv_hdr_end" >&2
    
       relpath=$(realpath "$(pwd)$path")
       if [[ "$relpath" != "$(pwd)"* ]]; then
       	echo "possible path traversal attack: $relpath" >&2
       	echo "pwd:     $(pwd)"   >&2
       	echo "relpath: $relpath" >&2
       	echo -ne "HTTP/1.1 403 Forbidden\r\n"
       	echo -ne "\r\n"
       	echo -ne "possible path traversal attack: $relpath"
       	exit 0
       fi
       echo "relpath: $relpath" >&2
       if [ $method = "GET" ]; then
       	if [ ! -f "$relpath" ]; then
       		echo -ne "HTTP/1.1 404 Not Found\r\n"
       		echo -ne "\r\n"
       		echo -ne "not found: $relpath"
       		exit 0
       	fi
       	echo -ne "HTTP/1.1 200\r\n"
       	echo -ne "\r\n"
       	cat  "$relpath"
       	exit 0
       fi
    
       if [ $method != "POST" ]; then
       	echo -ne "HTTP/1.1 405 Method Not Allowed\r\n"
       	echo -ne "\r\n"
       	exit 0
       fi
    
       if [ "${hdr[Authorization]}" != "APIKey $KEY" ]; then
       	echo -ne "HTTP/1.1 401 Unauthorized\r\n"
       	echo -ne "\r\n"
       	exit 0
       fi
    
       body_file=$(mktemp)
       body_len=0
       if [ ! -z "${hdr[Content-Length]}" ]; then
       	echo "hdr_cl > body_len"   >&2
       	body_len="${hdr[Content-Length]}"
       fi
       echo "body_len:  $body_len"    >&2
       echo "body_file: $body_file"   >&2
       dd of=$body_file bs=1 count=$body_len
    
       mkdir -p $(dirname "$relpath") >&2
       cp -v $body_file "$relpath"    >&2
    
       echo -ne "HTTP/1.1 201 Created\r\n"
       echo -ne "\r\n"
       echo -ne "created: $relpath\r\n"
       rm -rvf $body_file             >&2
    }
    
    if [ -z "$PORT" ]; then
       PORT=3000
    fi
    
    if [ -z "$KEY" ]; then
       KEY=$(uuidgen)
    fi
    
    if [ "$EXEC" = "server" ]; then
       server
       exit 0
    fi
    
    SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
    PID=$$
    PIDFILE="$SCRIPT_DIR/server.pid"
    
    if [[ -f "$PIDFILE" && -d "/proc/$(cat $PIDFILE)" ]]; then
       echo "one instance is running, refuse to start another"
       exit 1
    fi
    
    if [ -f "server.sh" ]; then
       echo "DO NOT START SERVER WHEN CURRENT WORKING DIRECTORY IS SAME AS SCRIPT DIRECTORY"
       echo "THIS MAY CAUSE UNEXPECTED OVERWRITING SERVER AND RCE"
       echo "EXITING"
       exit 1
    fi
    
    echo "PID=$PID"
    echo $PID > $PIDFILE
    echo "PIDFILE=$PIDFILE"
    echo "KEY=$KEY"
    
    while true; do
       nc -vlp $PORT -c "EXEC=server KEY=$KEY $0"
    done
    

    亮不了一点...

  • 牛逼 xhj003

  • 大佬 运行这个需要root权限吗?

  • @josephcz #4 发布于15/01/2025, 14:22:48
    @peterbosnia #3 1024以下端口需要,高位端口不需要

    有在Termux啥的跑过不 万一能的话那真的厉害了

  • 帮顶

  • @九尾妖姬 #7 哈哈,just for fun,只是觉得这样做一个 HTTP Server 很有趣

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有72267位seeker

🎉欢迎新用户🎉