2

几种语言的 CGI 编程

 3 years ago
source link: https://phuker.github.io/cgi-programming.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

为了了解 PHP、JSP、ASP 出现之前人们写网站的方法,洒家研究了一波 CGI,使用 C、Python、batch、shell script 语言写了几个简单的网页。

CGI 即通用网关接口,指 Web 服务器调用编程语言编写的程序的一个接口。洒家用的是 Apache 的 CGI,QUERY_STRINGREMOTE_ADDRREQUEST_URI 等参数是通过环境变量传递给 CGI 程序的,请求主体(POST 数据)作为 CGI 程序的标准输入(stdin),而 CGI 程序的标准输出(stdout)作为 HTTP 响应的部分(注:标准错误输出 stderr 会出现在错误日志中)。

系统和软件环境配置

WAMP Apache 2.4.18 64 位,Ubuntu Server 16.04 64 位。

需要开启 Apache 的 cgi_module

sudo a2enmod cgi
sudo service apache2 restart

对于 Linux,cgi-bin 的目录在 /etc/apache2/conf-enabled/serve-cgi-bin.conf 中规定,使用浏览器访问这个目录的 alias:/cgi-bin/。对于 Windows 下的 WAMP 套件,对应目录默认在安装目录内(例如 C:/wamp64/cgi-bin/)。

CGI programming 1

由于洒家对 Linux 不熟悉,踩到了几个坑

  1. 对于 Ubuntu 中 shell script,开头的 #!/bin/sh#!/bin/bash 是不同的。Ubuntu 的 /bin/sh/bin/dashDebian Almquist shell)的链接。对于 echo -e 'Content-Type: abc\n' 的执行结果不同。
  2. HTTP 响应头和响应主体之间要有一个换行符,否则无法分清响应主体和响应头部。
  3. Linux 上的程序要加可执行文件权限。否则报 500 错误 Permission denied: exec of '/usr/lib/cgi-bin/env_var.sh' failed: /usr/lib/cgi-bin/env_var.sh
  4. shell script 执行字符串要用 eval,否则不能把引号中带空格的字符串识别为同一个参数。

参考

用 C、Python、Shell Script、batch(批处理)写的几个小程序

编程语言 Perl 是一个广泛被用来编写 CGI 程序的语言,但 CGI 的一个目的是要独立于任何语言的。Web 服务器无须在这个问题上对语言有任何了解。事实上,CGI 程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。除 Perl 外,像 Unix shell script, Python, Ruby, PHP, Tcl, C/C++,和 Visual Basic 都可以用来编写 CGI 程序。

洒家看到这段话的时候想,哇塞,还可以用 C 语言写网站!洒家顿时感到了历史的气息。

查看所有的环境变量

在 Linux 下可以用 env 命令,列出所有的环境变量。注意,HTTP 响应头和响应主体之间要有一个换行符,否则无法分清响应主体和响应头部,报错:malformed header from script 'env_var.sh': Bad header: SERVER_SIGNATURE=<address>Apac

使用 shell script 脚本:

#!/bin/bash
echo -e "Content-Type: text/html\n"
env
HTTP/1.1 200 OK
Date: Sun, 23 Oct 2016 13:10:01 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Author: http://www.cnblogs.com/go2bed/
Vary: Accept-Encoding
Content-Length: 1180
Connection: close
Content-Type: text/html

SERVER_SIGNATURE=<address>Apache/2.4.18 (Ubuntu) Server at 192.168.245.136 Port 80</address>

HTTP_USER_AGENT=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.7 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.7
SERVER_PORT=80
HTTP_HOST=192.168.245.136
(省略)

Hello World

Linux/Windows,C 语言,请求的时候要请求编译好的二进制程序。

#include <stdio.h>

int main()
{
        printf("Content-Type: text/html\n\n");
        printf("Hello World!\n");
        return 0;
}

CGI programming 2

一个 say hello 的动态网页

Linux/Windows,Python,头部需要加 Python 可执行文件的位置。对于 Windows,则可能是 #!C:/Python27/python.exe

#!/usr/local/bin/python
import os
import urllib
import sys
import cgi
print 'Content-Type: text/html\n'

postData = sys.stdin.read()
if postData != '':
    items = postData.split('&')
    for item in items:
        key,value = urllib.splitvalue(item)
        if key == 'name':
            print '<h1>Hello, %s</h1>' % (cgi.escape(urllib.unquote_plus(value)),)

print '''<form action="" method="POST">
<input name="name" type="text" placeholder="Your name" />
<input type="submit" />
</form>'''
print os.environ['QUERY_STRING']

效果如下图。此程序读取 POST 数据(stdin),Tom 未在 URL 中出现。

CGI programming 3

另一个 say hello 的动态网页

Windows/Linux,C 语言。此处没有做 URL decode。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    int i;
    char * query;
    query = getenv("QUERY_STRING");
    printf("Content-type:text/html\n\n");
    if(getenv("QUERY_STRING") != NULL && strlen(getenv("QUERY_STRING")) > 5)
    {
        printf("<h1>Hello %s</h1><br />\n", query + 5);
    }
    printf("<form action=\"\" method=\"GET\"><input type=\"text\" name=\"name\" autofocus/><input type=\"submit\"/></form>");

    return 0;
}

CGI programming 4

执行任意命令的 Webshell,及其过程与经验

Linux,shell script

#!/bin/bash

echo -e 'Content-Type: text/html\n'

echo '<h1>I am using cgi (shell script)</h1>'
echo '<form action="" method="GET">'
echo '<input type="text" name="cmd" autofocus />'
echo '<input type="submit" />'
echo '</form>'


echo -e '\n<pre>\n'
#echo ${QUERY_STRING}
cmd=${QUERY_STRING#'cmd='}
#echo ${cmd}
cmd=${cmd:-'ping -c 2 baidu.com'}
cmd=$(echo ${cmd}| python -c 'import sys;import urllib;sys.stdout.write(urllib.unquote_plus(sys.stdin.read()))')
echo ${cmd}
echo '<hr />'
eval ${cmd} 2>&1
#ping -c 2 baidu.com
echo -e '\n</pre>\n'

CGI programming 5

由于洒家对 shell script 这门精妙的语言不甚了解,以为在 shell script 中执行一个命令(字符串)直接写上即可,一开始上文加粗的字体写的命令是 ${cmd} 2>&1。这一个脚本在命令执行的时候有一些奇怪的地方。例如执行 python -c 'import this' 时,会报错:

  File "", line 1
    'import
          ^
SyntaxError: EOL while scanning string literal

CGI programming 6

这个错误输出和下面的情况的输出相同:

user@localhost:/usr/lib/cgi-bin$ cmd=python\ -c\ \'import\ this\'
user@localhost:/usr/lib/cgi-bin$ echo $cmd
python -c 'import this'
user@localhost:/usr/lib/cgi-bin$ $cmd
  File "<string>", line 1
    'import
          ^
SyntaxError: EOL while scanning string literal
user@localhost:/usr/lib/cgi-bin$ eval $cmd
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.

经过一番研究之后洒家恍然大悟,这个报错表示,Python 收到的参数(sys.argv[2])为 'import,单引号的作用不是命令行中参数的边界,而是直接作为参数的一部分传进了 Python。也就是说,直接执行 ${cmd} 相当于 Python 中的

subprocess.call(["python","-c","'import","this'"])

一个字符串变量直接执行会有问题,正确的做法要用 eval 命令“扫描两次”,才能正确解析。使用 eval ${cmd} 使 shell 重新扫描命令,把 importthis 看做同一个参数,相当于 subprocess.call(["python","-c","import this"])

总而言之,这次的错误类似于这种情况:

user@localhost:/usr/lib/cgi-bin$ pipe="|"
user@localhost:/usr/lib/cgi-bin$ ls $pipe grep sh
ls: cannot access '|': No such file or directory
ls: cannot access 'grep': No such file or directory
ls: cannot access 'sh': No such file or directory
user@localhost:/usr/lib/cgi-bin$ eval ls $pipe grep sh
env_var.sh
test.sh

使用 Windows batch script(批处理)写的一个功能有限的 webshell

批处理,这个的坑也有点多,而且洒家也不太熟悉,只能写这么多了。

@echo off
echo Content-Type: text/html; charset=GBK
echo.

echo ^<h1^>batch webshell^</h1^>
echo ^<form action="" method="GET"^>
echo ^<input type="text" name="cmd" autofocus /^>
echo ^<input type="submit" /^>
echo ^</form^>

echo ^<pre^>
set ccmmdd=%QUERY_STRING:~4%
set ccmmdd=%ccmmdd:+= %
set ccmmdd=%ccmmdd:^%20= %
echo ^<textarea style="width:100%%;height:60%%;"^>
%ccmmdd%
echo ^</textarea^>
echo ^</pre^>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK