系统管理与CGI脚本详解
1. 应用程序管理脚本
在系统管理中,我们常常需要对应用程序进行启动、停止、查看状态等操作。以下是一个简单的应用程序管理脚本示例,它可以帮助我们完成这些基本操作:
case "$1" in
start)
if [ ! -f $PIDFILE ]; then
exit 1
fi
$INSTDIR/$APP &
PID=$!
echo $PID > $PIDFILE
exit 0
;;
stop)
$0 status || exit 1
PID=`cat $PIDFILE 2>/dev/null`
if [ “$?” -eq “0” ]; then
kill -9 $PID && rm -f $PIDFILE || exit 1
else
exit 1
fi
exit 0
;;
status)
PID=`cat $PIDFILE 2>/dev/null`
if [ “$?” -ne “0” ]; then
exit 1
fi
if [ -f $PIDFILE ]; then
if [ “`ps -o comm= -p $PID`” != “$APP” ]; then
echo “Error: PID $PID is not $APP”
exit 1
fi
ps -p $PID > /dev/null 2>&1
exit $?
else
exit 1
fi
;;
restart)
$0 stop && $0 start
;;
force-reload)
$0 restart
;;
*) echo “Argument \”$1\” not implemented.”
exit 2
;;
esac
该脚本通过
case
语句实现了不同的操作:
-
启动(start)
:检查
$PIDFILE
是否存在,如果存在则启动应用程序,并将其进程ID(PID)写入
$PIDFILE
。
-
停止(stop)
:先检查应用程序状态,若运行则杀死进程并删除
$PIDFILE
。
-
状态(status)
:检查
$PIDFILE
是否存在,以及对应的进程是否为目标应用程序。
-
重启(restart)
:先停止应用程序,再启动它。
-
强制重新加载(force-reload)
:等同于重启操作。
以下是一些使用示例:
# 检查应用程序状态
/etc/init.d/myapp status
echo $?
# 启动应用程序
/etc/init.d/myapp start
cat /var/run/myapp.pid
# 重启应用程序
/etc/init.d/myapp restart
cat /var/run/myapp.pid
# 停止应用程序
/etc/init.d/myapp stop
cat /var/run/myapp.pid
当应用程序未运行时,
status
命令返回非零值。启动应用程序后,其PID会被写入
myapp.pid
文件。重启应用程序会生成新的PID,因为旧实例会被停止并启动新实例。停止应用程序会删除
myapp.pid
文件。如果应用程序未运行,
restart
命令不会启动它。
2. CGI脚本概述
CGI(Common Gateway Interface)是一种协议,定义了数据如何传递给Web服务器。常见的形式如
http://www.example.com/page?name=steve&shell=bash
,它不仅用于在URL中传递参数,还用于Web服务器处理表单数据。虽然CGI常与PHP等语言结合使用,但仅使用Web服务器和shell也可以完成这些任务。
在当今的互联网环境中,CGI脚本需要具备极高的健壮性和安全性。因为任何能够欺骗脚本执行异常操作的人,都可以使用运行脚本的用户账户权限在Web服务器上执行代码。像PHP这样的复杂系统虽然会增加一些额外的安全保护,但也会带来更多的冗余和隐藏底层细节。在调试复杂系统问题或在受信任的简单环境中,shell也可用于编写CGI脚本。
3. CGI技术相关概念
3.1 协议相关
- RFC 3875 :该文档详细记录了Common Gateway Interface协议。它允许Web服务器通过HTTP协议的GET和POST方法从浏览器接收额外数据。在REST架构中还存在DELETE和PUT方法,但在Web中较少使用。
-
GET方法
:这是最常见的一种方式,它将参数嵌入URL中,通过环境变量
QUERY_STRING传递给脚本。脚本需要根据自身需求解析该变量,标准格式是每个variable=value对用&分隔,并且会对特殊字符进行编码,例如空格会被替换为%20或+,冒号变为%3A,斜杠变为%2F等。 - POST方法 :POST方法将参数放在请求体中传递,可用于从浏览器向Web服务器发送文件。与GET不同,POST数据通过脚本的标准输入进行处理。
3.2 表单相关
表单在HTML中定义,包含
action
和
method
属性。
action
指定CGI脚本的URL,
method
指定使用GET还是POST方法将数据发送到Web服务器。HTML页面本身不一定是CGI页面,它可以是普通的HTML网页。
4. CGI脚本潜在风险
处理用户提交的数据时,安全是最大的问题。在测试本地完全受信任的系统时,使用shell作为调试工具可以节省时间,避免复杂的第三方软件带来的冗余。
然而,用户可以通过编辑浏览器地址栏中的URL来提取Web服务器的敏感数据,也可以使用telnet连接到Web服务器的80端口,直接向CGI脚本发送任意数据。管理员很难防范所有可能的攻击,因此理想情况下,CGI脚本应该明确知道期望的输入,并丢弃其他任何输入。但在实际应用中,这往往很难实现,所以所有输入数据都应被视为潜在的恶意数据。需要检查的内容包括:
-
$VARIABLE
-
${VARIABLE}
-
cmd
-
`cmd`
-
$(cmd)
-
cmd > file
(或
>>
)
-
cmd < file
(或
<<
,
<<-
, 或
<<<
)
5. CGI脚本结构设置
假设使用Apache Web服务器,需要在其配置文件中添加以下内容来启用CGI:
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
<Directory “/var/www/cgi-bin”>
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
上述配置定义了
/cgi-bin
别名,并为其指向的目录设置了
+ExecCGI
选项。之后,将
index.html
文件安装在Web服务器的文档目录中,将两个CGI文件安装在
cgi-bin
目录中。
为了使用
example.com
域名进行测试,可以在
/etc/hosts
文件中添加相应条目,并在Apache中创建虚拟主机以响应该域名。
6. CGI脚本的头部信息
CGI脚本必须输出HTML内容,且开头必须包含两行头部信息。第一行定义内容类型,第二行必须为空行,用于标记头部信息的结束和内容的开始。示例如下:
Content-type: text/html
在“Show the Header”部分,会发送此头部信息,同时还会发送初始的HTML代码来设置标题栏文本并显示H1标题。
7. GET方法示例
index.html
文件包含两个表单,第一个是GET表单,包含两个文本输入框(
one
和
two
)和三个复选框(
check1
、
check2
和
check3
)。表单定义如下:
<form action=”/cgi-bin/hello.cgi” method=”get”>
<tr><td colspan=2><h2>Submit Text (GET)</h2></td></tr>
<tr><th>First Text:</th>
<td><input type=text name=one></td></tr>
<tr><th>Second Text:</th>
<td><input type=text name=two></td></tr>
<tr><th>Check These</th>
<td>
1<input type=checkbox name=check1>
2<input type=checkbox name=check2>
3<input type=checkbox name=check3>
</td></tr>
<tr><td> </td><td>
<input type=submit value=”Submit Form”>
</td></tr>
</form>
hello.cgi
脚本用于解析
QUERY_STRING
变量,首先将
&
替换为换行符,然后使用
eval
命令执行这些行。例如,
one=hello+world&two=this+is+my+message&check1=on&check3=on
会被拆分为四行进行处理。注意,未选中的复选框不会被发送。脚本还会将
+
替换为空格以显示正确的消息。
以下是
hello.cgi
脚本的代码:
#!/bin/bash
echo “Content-type: text/html”
echo
cat - << EndOfHeaders
<html>
<head><title>Hello There!</title></head>
<body>
<h1>Hello There!</h1>
EndOfHeaders
echo “You said: $QUERY_STRING”
echo “<hr/>”
eval `echo ${QUERY_STRING} | tr ‘&’ ‘\n’`
echo “one is ${one}” | tr ‘+’ ‘ ‘
echo “<br/>”
echo “two is ${two}” | tr ‘+’ ‘ ‘
echo “<br/>”
for check in check1 check2 check3
do
if [ -z “${!check}” ]; then
echo “${check} is not set<br/>”
else
echo “${check} is set<br/>”
fi
done
echo “<hr/>”
eval `echo ${QUERY_STRING/’$’/’\\$’} | tr ‘&’ ‘\n’`
echo “one is ${one}” | tr ‘+’ ‘ ‘
echo “<br/>”
echo “two is ${two}” | tr ‘+’ ‘ ‘
echo “<hr/>”
cat - << EOF
</body>
</html>
EOF
这个脚本虽然简单,但直接将未处理的用户输入传递给
eval
是非常不安全的。例如,恶意用户可以通过编辑URL中的查询字符串来获取Web服务器的敏感信息,如
$DOCUMENT_ROOT
变量的值。为了提高安全性,脚本的第二部分将美元符号
$
替换为
\$
,但这并不能完全保证免受所有攻击。
8. POST方法示例
index.html
中的第二个表单是POST表单,用于上传文件。表单定义如下:
<form action=”/cgi-bin/upload.cgi?foo=bar”
method=”post” enctype=”multipart/form-data”>
<tr><td colspan=2><h2>File Upload (POST)</h2></td></tr>
<tr><th>File One:</th>
<td><input type=file name=fileone></td></tr>
<tr><th>File two:</th>
<td><input type=file name=filetwo></td></tr>
<tr><th>File Three:</th>
<td><input type=file name=filethree></td></tr>
<tr><td> </td><td>
<input type=submit value=”Upload Files”>
</td></tr>
</form>
POST数据通过浏览器发送到脚本的标准输入。虽然可以使用
method=”POST”
和
action=”upload.cgi?foo=bar”
的组合来同时传递GET请求,但这种方式并不常见,POST表单通常只使用POST元素并忽略
QUERY_STRING
。需要将
enctype
设置为
multipart/form-data
,以确保浏览器正确编码文件。
以下是
upload.cgi
脚本的代码:
#!/bin/bash
function readfiles
{
read disposition data name filename
read ct contenttype
read blankline
read previous_line
eval `echo $filename | tr -d ‘^M’`
echo “<hr/>”
echo “Processing file \”$filename\” ($contenttype)<br/>”
> uploads/$filename
while read content
do
contentvalue=${content%^M}
case $contentvalue in
$boundary)
# end of file.
# First, show the summary of the previous file
cd uploads
md5sum $filename
cd - > /dev/null
echo “<br/>”
# Now read in the headers of the next file
read disposition data name filename
read ct contenttype
read blankline
read previous_line
eval `echo $filename | tr -d ‘^M’`
if [ ! -z “$filename” ]; then
echo “<hr/>”
echo “Processing file \”$filename\” ($contenttype)<br/>”
> uploads/$filename
else
# That was the end of the input. No proper notification
# received (boundary--) but handle it gracefully.
echo “<hr/>”
return
fi
;;
${boundary}--)
# end of all input
cd uploads
md5sum $filename
cd - > /dev/null
echo “<hr/>”
return
;;
*)
echo “$previous_line” >> uploads/$filename # | tee -a uploads/$filename
previous_line=$content
;;
esac
done
}
# Show the Header
cat - << EndOfHeaders
Content-type: text/html
<html>
<head><title>Uploader</title></head>
<body>
<h1>File Uploads</h1>
EndOfHeaders
echo “Query String is $QUERY_STRING”
# Read the first line of input. This tells you the boundary
read BOUNDARY
boundary=${BOUNDARY%^M}
# Read and process the input
readfiles
# Use this instead for debugging and testing
# echo “<pre>”
# cat -
# echo “</pre>”
# Write the HTML footer
cat - << EOF
</body>
</html>
EOF
upload.cgi
脚本首先读取第一行以确定边界字符串,该字符串用于分隔不同的文件。然后,
readfiles
函数读取文件的头部信息并创建一个空白文件,接着逐行读取输入,根据不同的情况进行处理:
- 当找到边界字符串时,计算上一个文件的MD5校验和,并读取下一个文件的头部信息。
- 当找到边界字符串后跟
--
时,表示输入结束,计算最后一个文件的MD5校验和并返回。
- 在其他情况下,将上一行内容追加到当前文件中,并更新
$previous_line
变量。
通过这种方式,
upload.cgi
脚本可以正确处理多个文件的上传,并计算每个文件的MD5校验和。
综上所述,系统管理中的应用程序管理脚本可以帮助我们方便地对应用程序进行操作,而CGI脚本在Web开发中扮演着重要的角色,能够实现数据的传递和处理。但在使用CGI脚本时,必须高度重视安全问题,确保脚本能够抵御各种潜在的攻击。
系统管理与CGI脚本详解
9. CGI脚本操作流程总结
为了更清晰地理解CGI脚本的使用,下面总结一下相关的操作流程。
9.1 配置Apache服务器支持CGI
若使用Apache Web服务器,需在其配置文件中添加以下内容以启用CGI:
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
<Directory “/var/www/cgi-bin”>
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
操作步骤如下:
1. 打开Apache配置文件(通常位于
/etc/apache2/apache2.conf
或类似位置)。
2. 在配置文件中添加上述代码。
3. 保存配置文件并重启Apache服务器,可使用命令
sudo service apache2 restart
。
9.2 部署文件
将
index.html
文件放置在Web服务器的文档目录中,将
hello.cgi
和
upload.cgi
两个CGI文件放置在
/var/www/cgi-bin
目录下。
9.3 测试GET请求
-
打开浏览器,访问包含GET表单的
index.html页面。 -
在表单中输入数据并提交,观察
hello.cgi脚本的处理结果。 -
注意查看URL中的
QUERY_STRING,以及脚本对特殊字符的处理。
9.4 测试POST请求
-
同样打开
index.html页面,切换到POST表单。 - 选择要上传的文件并提交表单。
-
查看
upload.cgi脚本对文件的处理过程,包括边界字符串的识别、文件的写入和MD5校验和的计算。
10. CGI脚本安全防护建议
由于CGI脚本存在安全风险,以下是一些安全防护建议:
-
输入验证
:对用户输入的数据进行严格验证,只允许符合预期格式的数据通过。例如,对于数字输入,检查是否为有效的数字;对于字符串输入,检查是否包含非法字符。
-
过滤特殊字符
:对用户输入中的特殊字符进行过滤,如
$
、
;
、
|
等,防止恶意代码注入。可以使用正则表达式或特定的过滤函数来实现。
-
限制权限
:确保运行CGI脚本的用户账户具有最小的权限,避免使用具有高权限的账户运行脚本。例如,创建一个专门的低权限用户来运行CGI脚本。
-
更新和维护
:及时更新Web服务器和相关软件,修复已知的安全漏洞。定期检查和维护CGI脚本,确保其安全性。
11. 总结与展望
系统管理中的应用程序管理脚本为我们提供了便捷的应用程序操作方式,而CGI脚本则在Web开发中实现了数据的传递和处理。然而,安全问题始终是CGI脚本使用过程中的关键挑战。
在未来的Web开发中,随着技术的不断发展,CGI脚本可能会面临更多的安全威胁和性能挑战。因此,我们需要不断探索和采用新的安全技术和优化方法,以确保CGI脚本的安全性和性能。同时,结合现代的Web开发框架和工具,如Node.js、Python的Flask等,也可以为Web开发带来更多的便利和创新。
以下是一个简单的mermaid流程图,展示了
upload.cgi
脚本处理文件上传的主要流程:
graph TD;
A[开始] --> B[读取边界字符串];
B --> C[读取第一个文件头部信息];
C --> D[创建空白文件];
D --> E[逐行读取输入];
E --> F{是否为边界字符串};
F -- 是 --> G[计算上一个文件MD5校验和];
G --> H[读取下一个文件头部信息];
H --> D;
F -- 否 --> I{是否为边界字符串+--};
I -- 是 --> J[计算最后一个文件MD5校验和];
J --> K[结束];
I -- 否 --> L[将上一行内容追加到当前文件];
L --> E;
通过以上的介绍和分析,希望能帮助大家更好地理解系统管理中的应用程序管理脚本和CGI脚本的使用,同时提高对安全问题的认识和防范能力。在实际开发中,要根据具体的需求和场景,合理运用这些技术,并不断优化和改进,以实现更高效、安全的Web应用。
超级会员免费看
2902

被折叠的 条评论
为什么被折叠?



