Apache服务器下Bash CGI脚本文件上传失败问题求助
问题背景
处理文件传输的POST请求时,Apache服务器上的Bash CGI脚本无法完成文件上传。定位到/dev/stdin未正常输出二进制流,使用轻量级CMS Lichen时也出现该问题。以下是简化后的错误场景及排查过程。
简化场景
HTML上传表单
<form action="http://guestserver/cgi-bin/upload.cgi" method="post" enctype="multipart/form-data"> <p><input type="file" name="filename" id="file"></p> <p><input type="submit" value="Upload"></p> </form>
CGI脚本(upload.cgi)
#!/bin/sh # Exit immediately if a command exits with a non-zero status. set -e # replace spaces with underscores # which is done by tr "thisGetsReplaced" "byThis" ; # -s means squeeze repeated occurence # echo $PATH_INFO | # gets filename and passes output to next (by '|') sanitized=$(echo $PATH_INFO | tr -s ' ' '_') # move one dir up and look if file exists there if [ -f ..$sanitized ]; then cat /dev/stdin > /dev/null echo 'Status: 409 Conflict' echo 'Content-Type: text/plain' echo '' echo 'File already exists.' exit 0 fi # Actual file write mkdir -p ..$(dirname $sanitized) cat /dev/stdin > ..$sanitized # line that throws error # I guess if file write at this point was successful # it exits with something non-zero # so the script is STOPPED echo 'Status: 204 No Content' echo "X-File-Name: $(basename $sanitized)" echo "X-File-Path: $(dirname $sanitized)" echo ''
发现$PATH_INFO不包含上传文件名,导致上传失败。生产环境中虽能创建正确文件名,但文件为空,疑惑该现象的原因。
测试验证
编写test.cgi验证数据传输及PATH_INFO为空的问题:
#!/bin/sh echo "Content-Type: text/html" echo "<html><head></head><body>" echo SERVER_SOFTWARE = $SERVER_SOFTWARE echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE echo SERVER_PROTOCOL = $SERVER_PROTOCOL echo SERVER_PORT = $SERVER_PORT echo REQUEST_METHOD = $REQUEST_METHOD echo HTTP_ACCEPT = $HTTP_ACCEPT echo PATH_INFO = $PATH_INFO dd count=1 bs=$CONTENT_LENGTH # prints file content echo "</body></html>"
执行后输出:
SERVER_SOFTWARE = Apache/2.4.55 (Unix)
GATEWAY_INTERFACE = CGI/1.1
SERVER_PROTOCOL = HTTP/1.1
SERVER_PORT = 80
REQUEST_METHOD = POST
HTTP_ACCEPT =
PATH_INFO =
------WebKitFormBoundaryMNBsYvUe3DbH9tpE Content-Disposition: form-data; name="filename"; filename="uploaded file.jpg" Content-Type: image/jpeg ÿØÿàJFIFÿÛC %# , #&')*)-0-(0%()(ÿÀ,àÿÄÿÄ; !"#$312%4CB“5ADQRcdƒabe„”•ÿÚ?•ñ£Ö˜þFßE%ò}õ[/ì³è1Æ'¬YÇçªÙæÞõÑTÚuJn4îÝ)ÎV“¦9îª ©“1í”»ge¢R…Z¿MÑŽ¼ÜÃÛ—d´¯±¦#ø4¦‚ðœDÐŽæ…c4û°e¥4ê×1žOO qu»Ö:ûïAB¬?ÙܶbZÎf³ª‹¹yçDÖÒáSªµù¦
最后一段为dd命令输出的文件内容(已截断),证明文件数据已传输,但未被服务器保存。
错误日志分析
Apache错误信息:Premature end of script headers: upload.cgi
/var/log/httpd/error_log日志:
dirname: missing operand
Try 'dirname --help' for more information.
/srv/http/cgi-bin/upload.cgi: line 20: ..: Is a directory
[Mon Mar 06 12:29:04.166828 2023] [cgid:error] [pid 297:tid 140340891719360] [client 192.168.56.1:63168] Premature end of script headers: upload.cgi, referer: http://localhost:5500/
错误指向脚本第19行mkdir -p ..$(dirname $sanitized),因$sanitized由空的$PATH_INFO生成,导致dirname缺少操作数执行失败。
需求:解决该问题,实现文件正确上传至服务器。
内容的提问来源于stack exchange,提问作者proto

