CGI的运行原理

当用户在网页上发出一个请求,比如点击一个链接或提交一个表单,请求的URL会指向一个服务器上的CGI脚本。服务器会根据URL中的参数和请求方法,将一些信息传递给CGI脚本,比如查询字符串、内容长度、请求方法等,这些信息通常通过环境变量或命令行参数传递。服务器还会将请求的主体部分,比如表单数据,通过标准输入流传递给CGI脚本。CGI脚本会根据接收到的信息进行相应的处理,比如查询数据库、生成动态内容等,并将处理结果通过标准输出流返回给服务器。返回的结果必须包含一个合法的HTTP头部,指明内容类型、状态码等信息,然后是一个空行,再然后是实际的内容,比如HTML页面、图片数据等。服务器会将CGI脚本的输出作为响应发送给用户。

运用CGI的例子

一个简单的计数器脚本,它可以显示网页被访问的次数。这个脚本可以用C语言或Perl语言编写,并放在服务器的cgi-bin目录下。当用户请求这个脚本的URL时,服务器会执行这个脚本,并将其输出返回给用户。输出是一个包含计数器值的HTML页面,每次访问都会增加一。这个脚本需要一个文本文件来存储计数器值,并在每次执行时读取和更新这个文件。

CGI可能存在的安全漏洞

产生原因:由于用户输入没有被正确地解析或过滤,导致攻击者可以通过网页URL传递一些参数或命令,从而执行一些恶意的操作。列举如下:

  • 利用CGI脚本的权限,访问或修改服务器上的敏感文件或数据
  • 利用CGI脚本的内存问题,注入或执行一些恶意的代码
  • 利用CGI脚本的输入验证问题,传递一些非法或危险的数据,造成服务器的崩溃或拒绝服务。 为了避免这些漏洞,你在使用CGI的时候应该注意以下几点:
  • 限制CGI脚本的权限,不要让它们以root用户或其他高权限用户运行。
  • 管理好CGI脚本的内存分配和释放,避免内存泄露或溢出。
  • 对用户输入进行严格的验证和过滤,防止SQL注入、跨站脚本、命令执行等攻击。

CGI的优缺点

CGI的优点有:

  • CGI是一个非常明确和支持良好的标准,可以用任何语言和平台编写CGI程序,只要符合规范。
  • CGI可以实现网页和外部程序或数据库的交互,提供动态内容和功能。
  • CGI有很多现成的代码和框架,可以方便地实现一些常见的任务,比如计数器、表单处理、访问控制等。

CGI的缺点有:

  • CGI每次处理请求时都需要启动一个新的进程,占用服务器的内存和资源,效率低下。
  • CGI不能很好地在内存中缓存数据,每次都需要打开或关闭数据库连接,速度慢。
  • CGI需要对用户输入进行严格的验证和过滤,否则可能会遭受SQL注入、跨站脚本、命令执行等攻击。

CGI的横向对比

实现服务端和客户端交互的其他方式

  • Servlet:一种基于Java的技术,可以在服务器上运行Java程序,比CGI更快、更稳定、更安全。
  • ASP:一种基于Microsoft的技术,可以在服务器上运行VBScript或JScript程序,提供了丰富的组件和功能。
  • PHP:一种基于开源的技术,可以在服务器上运行PHP程序,支持多种数据库和平台,易于学习和使用。

CGI和其他技术的区别

  • CGI是一种协议,而不是一种语言,它可以用任何语言和平台实现,只要符合规范。其他技术,比如Servlet、ASP、PHP等,都是基于特定的语言或平台的技术,有自己的语法和特性。
  • CGI每次处理请求时都需要启动一个新的进程,占用服务器的内存和资源,效率低下。其他技术,比如Servlet、ASP、PHP等,都是在服务器上运行一个持久的程序,可以在内存中缓存数据和连接,速度快。
  • CGI需要对用户输入进行严格的验证和过滤,否则可能会遭受SQL注入、跨站脚本、命令执行等攻击。其他技术,比如Servlet、ASP、PHP等,都提供了一些内置的函数或组件,可以方便地处理用户输入和输出,提高安全性。
  • CGI有很多现成的代码和框架,可以方便地实现一些常见的任务,比如计数器、表单处理、访问控制等。其他技术,比如Servlet、ASP、PHP等,也有很多成熟的库和框架,可以提供更多的功能和扩展性。

CGI在现在的网页开发中还有一定的用处,但已经不是主流的技术了。

CGI的用处主要有以下几点:

  • CGI可以实现一些简单的动态内容和功能,比如计数器、表单处理、访问控制等,不需要安装或配置复杂的服务器或框架。
  • CGI可以用任何语言和平台实现,只要符合规范,可以充分利用现有的代码和资源,也可以学习一些新的语言或技术。
  • CGI可以作为一种学习和实验的工具,了解网页和服务器之间的交互原理和过程,以及一些安全和性能的问题和解决方法。

CGI已经不是主流的技术的原因主要有以下几点:

  • CGI每次处理请求时都需要启动一个新的进程,占用服务器的内存和资源,效率低下,不能满足高并发和高性能的需求。
  • CGI不能很好地在内存中缓存数据和连接,每次都需要打开或关闭数据库连接,速度慢,也容易出现错误或冲突。
  • CGI需要对用户输入进行严格的验证和过滤,否则可能会遭受SQL注入、跨站脚本、命令执行等攻击,这些攻击已经非常普遍和危险。
  • CGI有很多现成的代码和框架,但也有很多过时或不兼容的代码和框架,需要花费时间和精力去选择和维护。

CGI的框架

  • CGI::Framework:一个用Perl语言编写的轻量级的CGI框架,提供了一些基本的功能,如参数验证、会话管理、错误处理等。
  • Web.py:一个用Python语言编写的简单而强大的CGI框架,提供了一些高级的功能,如模板系统、数据库访问、缓存机制等。
  • Sinatra:一个用Ruby语言编写的极简主义的CGI框架,提供了一种优雅而灵活的方式来定义路由和处理请求。

CGI的实例

Web.py的例子,这是一个用Python语言编写的CGI框架。假设你想用这个框架来实现一个简单的留言板功能,你需要做以下几个步骤:

  • 在你的服务器上创建一个cgi-bin目录,这是存放CGI脚本的地方。
  • 在cgi-bin目录下创建一个guestbook.py文件,这是你的主要的CGI脚本,它的内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python
import web # import the web.py module

# define the URLs and their corresponding classes
urls = (
'/', 'show_messages', # show the messages on the guestbook
'/add', 'add_message', # add a new message to the guestbook
)

# define the template for rendering the HTML pages
render = web.template.render('templates/')

# define the class for showing the messages
class show_messages:
def GET(self): # define the GET method
messages = self.get_messages() # get the messages from the data file
return render.show(messages) # render the show.html template with the messages

def get_messages(self): # define a helper method to get the messages
messages = [] # create an empty list to store the messages
with open('data/guestbook.txt') as f: # open the data file
for line in f: # iterate over each line in the file
name, message, timestamp = line.strip().split('|') # split the line by '|'
messages.append((name, message, timestamp)) # append a tuple of name, message and timestamp to the list
return messages # return the list of messages

# define the class for adding a new message
class add_message:
def POST(self): # define the POST method
i = web.input() # get the user input from the web form
name = i.get('name') # get the name from the user input
message = i.get('message') # get the message from the user input
self.validate(name, message) # validate the name and message
self.add_message(name, message) # add the name and message to the data file
raise web.seeother('/') # redirect to the home page

def validate(self, name, message): # define a helper method to validate the name and message
if not name or not message: # if either name or message is empty
raise web.badrequest("Please enter your name and message") # raise a bad request error

def add_message(self, name, message): # define a helper method to add the name and message to the data file
import time # import the time module
timestamp = int(time.time()) # get the current timestamp as an integer
with open('data/guestbook.txt', 'a') as f: # open the data file in append mode
f.write(f'{name}|{message}|{timestamp}\n') # write a line with name, message and timestamp separated by '|'

# create a new web application with the URLs and classes
app = web.application(urls, globals())

# run the application if this file is executed as a script
if __name__ == '__main__':
app.run()
  • 在cgi-bin目录下创建一个templates文件夹,这是用来存放HTML模板的文件夹。
  • 在templates文件夹下创建一个base.html文件,这是用来定义HTML页面的基本结构的文件,它的内容如下:
1
2
3
4
5
6
7
8
9
10
11
$def with (title)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>$title</title>
</head>
<body>
$:content()
</body>
</html>
  • 在templates文件夹下创建一个add.html文件,这是用来显示添加留言成功的文件,它的内容如下:
1
2
3
4
5
6
7
8
9
$def with ()
$var title: Guestbook

$extends('base.html')

$def content():
<h1>Guestbook</h1>
<p>Your message has been added.</p>
<p><a href="/">Go back to the guestbook</a></p>
  • 在cgi-bin目录下创建一个data文件夹,这是用来存放留言数据的文件夹。
  • 在data文件夹下创建一个guestbook.txt文件,这是用来存放留言数据的文本文件,它的内容可以为空或者有一些示例数据,每一行代表一条留言,格式为:姓名|留言内容|时间戳。例如:
1
2
3
Alice|Hello world!|1639566000
Bob|Nice to meet you!|1639566600
Charlie|Happy holidays!|1639567200
  • 为了让你的CGI脚本可以被服务器执行,你需要给它赋予可执行权限,比如在Linux系统下,你可以使用chmod命令:
1
chmod +x guestbook.py

各种语言对CGI的实现

主要是遵循CGI的协议和标准,通过和服务器交换一些特定的环境变量和数据,来实现动态内容的生成和返回。比如说:

  • Java:可以使用Java Servlet技术来实现CGI,Servlet是一种运行在服务器上的Java程序,可以接收和处理客户端的请求,生成动态的HTML页面或其他类型的文档。Servlet可以使用Java的标准库或第三方的库来访问数据库或其他服务器。Servlet也可以使用JSP(Java Server Pages)技术来简化HTML页面的生成。
  • Python:可以使用Python的标准库中的cgi模块来实现CGI,cgi模块提供了一些类和函数来处理客户端的请求,获取表单数据,输出HTTP头部和内容等。Python也有很多第三方的框架或库来简化CGI开发,比如Web.py、Flask、Django等。
  • PHP:可以使用PHP的内置功能来实现CGI,PHP是一种专门用于网页开发的语言,它可以嵌入到HTML页面中,或者作为一个独立的脚本运行。PHP可以直接获取客户端的请求数据,输出HTTP头部和内容,以及访问数据库或其他服务器。PHP也有很多第三方的框架或库来简化CGI开发,比如Laravel、Symfony、CodeIgniter等。

CGI的漏洞

有一些关于CGI的CVE漏洞,比如: