CGI 1.1

不完全翻译,因为部分英文内容有点难以理解,水平有限。
这不是一个强制的规范,所以各种实现都不一定完全按照本规范。

摘要

The Common Gateway Interface (CGI) is a simple interface for running external programs, software or gateways under an information server in a platform-independent manner.
一般来说,支持的信息服务器(information servers)是 HTTP 服务器。

这个接口自 1993 年以来一直被 World-Wide Web 使用。这个规范定义了 CGI/1.1 接口的“当前实践(current practice)”参数,由美国国家超级计算应用中心开发并记录。这个文档也定义了 CGI/1.1 接口在 UNIX(R) 及其他类似系统上的使用。


1 简介

1.1 目的

CGI 允许 HTTP 服务器和 CGI script共同承担响应客户端请求的责任。客户端请求包含一个 URI(Uniform Resource Identifier),一个请求方法以及通过传输协议提供的关于请求的一些辅助信息。

CGI 定义了描述客户端请求的抽象变量,也叫做元变量(meta-variables)。同详细的程序员接口一起定义了 script和 HTTP 服务器之间的平台无关的接口。

服务器负责管理连接,数据传输以及客户端请求的网络事件。CGI script处理应用事件,比如数据连接和文档处理。

1.2 requirements

The key words ‘MUST’, ‘MUST NOT’, ‘REQUIRED’, ‘SHALL’, ‘SHALL NOT’, ‘SHOULD’, ‘SHOULD NOT’, ‘RECOMMENDED’, ‘MAY’ and ‘OPTIONAL’ in this document are to be interpreted as described in BCP 14, RFC 2119 [3].

An implementation is not compliant if it fails to satisfy one or more of the ‘must’ requirements for the protocols it implements. An implementation that satisfies all of the ‘must’ and all of the ‘should’ requirements for its features is said to be ‘unconditionally compliant’; one that satisfies all of the ‘must’ requirements but not all of the ‘should’ requirements for its features is said to be ‘conditionally compliant’.

1.3 规范

规范的主要部分没有定义所有 CGI 的函数和特性。下列词语用来描述未定义的特性:
system-defined
特性在不同系统间可能会不同,但在同样系统上的不同实现间必须是一致的。系统一般指的是一类操作系统。一些系统在文档的第七节定义。新系统可能会定义在新规范中,而不是修订此规范。

implemention-defined
不同实现间的特性可能不同。这样的实现必须写明其行为。

1.4 术语

这个规范使用了许多定义在 HTTP/1.1 中的术语;然而,下列术语有着不同的意义:

meta-variable
从服务器传到 script的携带有信息的命名变量。它不一定是操作系统环境中的变量,尽管这是最普遍的实现。

script
被服务器根据这个接口调用的软件。它不需要是一个独立的程序,可以是动态加载的或共享的库,甚至是服务器中的一个子线程。它可能是运行时解释的一些语句,就像术语 ‘script’ 的普遍意思,但这不是一个要求。在这个规范的上下文中,这个术语有更广泛的定义。

server
调用 script 来服务客户端请求的应用程序


2 注释惯例和一般语法

2.1 扩展的巴科斯范式(用来描述计算机语言语法的符号集)

All of the mechanisms specified in this document are described in
both prose and an augmented Backus-Naur Form (BNF) similar to that
used by RFC 822 [13]. Unless stated otherwise, the elements are
case-sensitive. This augmented BNF contains the following
constructs:

name = definition
The name of a rule and its definition are separated by the equals
character (‘=’). Whitespace is only significant in that
continuation lines of a definition are indented.

“literal”
Double quotation marks (“) surround literal text, except for a
literal quotation mark, which is surrounded by angle-brackets (‘<’
and ‘>’).

rule1 | rule2
Alternative rules are separated by a vertical bar (‘|’).

(rule1 rule2 rule3)
Elements enclosed in parentheses are treated as a single element.

*rule
A rule preceded by an asterisk (‘*’) may have zero or more
occurrences. The full form is ‘n*m rule’ indicating at least n
and at most m occurrences of the rule. n and m are optional
decimal values with default values of 0 and infinity respectively.

[rule]
An element enclosed in square brackets (‘[’ and ‘]’) is optional,
and is equivalent to ‘*1 rule’.

N rule
A rule preceded by a decimal number represents exactly N
occurrences of the rule. It is equivalent to ‘N*N rule’.

2.2 基本规则

This specification uses a BNF-like grammar defined in terms of
characters. Unlike many specifications which define the bytes
allowed by a protocol, here each literal in the grammar corresponds
to the character it represents. How these characters are represented
in terms of bits and bytes within a system are either system-defined
or specified in the particular context. The single exception is the
rule ‘OCTET’, defined below.

The following rules are used throughout this specification to
describe basic parsing constructs.

  alpha         = lowalpha | hialpha
  lowalpha      = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
                  "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
                  "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
                  "y" | "z"
  hialpha       = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" |
                  "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" |
                  "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" |
                  "Y" | "Z"

  digit         = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
                  "8" | "9"
  alphanum      = alpha | digit
  OCTET         = <any 8-bit byte>
  CHAR          = alpha | digit | separator | "!" | "#" | "$" |
                  "%" | "&" | "'" | "*" | "+" | "-" | "." | "`" |
                  "^" | "_" | "{" | "|" | "}" | "~" | CTL
  CTL           = <any control character>
  SP            = <space character>
  HT            = <horizontal tab character>
  NL            = <newline>
  LWSP          = SP | HT | NL
  separator     = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" |
                  "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" |
                  "}" | SP | HT
  token         = 1*<any CHAR except CTLs or separators>
  quoted-string = <"> *qdtext <">
  qdtext        = <any CHAR except <"> and CTLs but including LWSP>
  TEXT          = <any printable character>

Note that newline (NL) need not be a single control character, but
can be a sequence of control characters. A system MAY define TEXT to
be a larger set of characters than < any CHAR excluding CTLs but
including LWSP >.

2.3 URL 编码

一些变量和结构是 ‘URL-encoded’的。这种编码在 RFC 2396 第二节中描述。在一个 URL-encoded 字符串中,一个转义序列由百分号和之后的两个十六进制数组成,两个十六进制数组成了一个 octet。octet 表示了转义序列所表示的图形字符的 ASCII 值(对于非 ASCII 字符, 需要转换为 UTF-8 字节序, 然后每个字节按照上述方式表示)。一般来说,URI 语法中没有规定非 ASCII 编码所表示的字符集,因此 CGI 在一个 ad-hoc这个短语通常用来形容一些特殊的、不能用于其它方面的的,为一个特定的问题、任务而专门设定的解决方案。) 基础上处理这个问题。

一些不安全(保留的)字符编码后可能有不同的语义。需要根据上下文来确定哪些字符串是不安全的,权威解释请看 RFC 2396[2],以及 RFC 2732[7]。这些保留字符通常用于为语句提供句法结构 ,比如字段分隔符。在任何情况下,保留字符先被处理,然后结果数据被 URL-decoded。

为了编码一个字符串,所有保留和禁止的字符将被转义序列代替。然后,字符串可以被用于组装 URI。保留字符会随上下文改变,但总属于下面的集合:

reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" |
                 "," | "[" | "]"

在任何特定的上下文中,这些字符串中的某个子集会被保留;URL 编码时,这些字符中除子集外的字符禁止被编码。
其他描述 URI 语法的基本规则:

hex        = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b"
                   | "c" | "d" | "e" | "f"
escaped    = "%" hex hex
unreserved = alpha | digit | mark
mark       = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"

3 调用 script

3.1服务器的责任

服务器充当应用程序的网关。它接收客户端请求,选择 CGI script 来处理请求,将客户端请求转为一个 CGI 请求,执行 script ,把 CGI 响应转为客户端响应。当处理客户端请求时,服务器有责任实现协议或传输级别的认证和安全。服务器也可以以不透明的方式运行,修改请求或响应来提供额外的服务,比如媒体类型传输或协议减少(protocol reduction)。

服务器必须以本规范要求的方式来对客户端请求数据进行翻译和协议转换。而且,服务器和客户端之间必须符合相关网络协议的要求,即使 CGI script 没有符合这个规范的要求。

如果服务器正在对请求进行认证,它必须禁止执行 script, 除非请求通过了所有的访问控制。

3.2 选择 script

服务器根据客户端提供的通用 URI 来决定哪个 CGI script 会被执行。这个URI包含一个以“/”分隔的分层路径。对于任何特定的请求,服务器将用一个单独的脚本识别该路径的全部或前导部分,从而将脚本放置于路径层级中的一个特定的点。路径的剩余部分,如果有的话,是需要 script 解释的资源或子资源识别符。

script 能够通过 meta-variables 得到路径的划分信息。非层级的 URI 方案支持不在本规范范围内。

3.3 script-URI

客户端请求 URI 到 script 选择的映射,是由特定服务器的实现和配置来定义的。服务器可以允许通过一组不同的 URI 路径层级来确定 script。因此在处理和生成 meta-variable 时,允许通过组内其他 URI 来替换当前 URI。
服务器:
1.可以保留特定客户端请求中的 URI。或者
2.可以为每个 script 从一组可能的值中选取一个规范的 URI。或者
3.它可以从集合中实现任何其他的 URI 选择。

通过这样生成的 meta-variable,可以构建 ‘Script-URI’。必须具有这样的属性:如果客户端已经访问替换的 URI,那么该脚本将被执行,并且 SCRIPT_NAME,PATH_INFO 和 QUERY_STRING 三个 meta-variable 的值与替换前相同。Script-URI 与在 RFC 2396第三节中定义的结构一致,但是不允许对象参数和分段识别符。Script-URI 的不同组件由一些 meta-variable 定义:

 script-URI = <scheme> "://" <server-name> ":" <server-port>
                   <script-path> <extra-path> "?" <query-string>

<scheme>从 SERVER_PROTOCOL 中获得,<server-name><server-port><query-string>是相应 meta-variable 的值。<script-path><extra-path是通过对 SCRIPT_NAME 和 PATH_INFO 进行 URL-encoded 得到的(’;’,’=’,’?’是保留字符)。

从 4.1.5 节中可以获取关于 PATH_INFO 的更多信息。

scheme 和 protocol 不同,因为 scheme 除应用协议外还标识了访问方法。举例:对于使用 TLS(Transportransport Layer Security) 连接的资源,请求的 URI 的 scheme 是 https,但使用的是 http 协议。CGI/1.1 没有提供重构这个的通用方式,因此定义 Script-URI 时使用的是基本协议(protocol)。但是,脚本可以使用特定于方案的元变量来更好地推导出 URI scheme。
注意,这个定义也允许通过修改合适组件的方式来构建 URI,从而可以用任意允许的 path-info 或 query-string 的值来调用脚本。

3.4 执行

script 以系统定义的方式(system-defined)调用。除非另有规定,包含 script 的文件会被作为一个可执行程序调用。第四节有描述服务器如何准备 CGI 请求;CGI 请求包含 meta-variables(script 执行时立刻可用)和请求消息数据。请求消息数据不需要立刻可用;script 在服务器接收完所有数据前就可以执行。第五节和第六节描述了 script 返回的响应。

发生错误时,服务器可以在任何时候打断或终止 script 的执行而不需要警告。比如,当服务器和客户端之间传输失败时。因此,script 应该准备好处理异常的终止。


4 CGI 请求

请求的信息来自两个不同的源:1.请求的 meta-variable 2.任何关联的消息体

4.1 请求的 meta-variables

meta-variables 包含服务器传给 script 的关于请求的数据,script 可以通过系统定义的方式访问它们。meta-variable 的变量名不是大小写敏感的,所以变量名只有大小写不同的 meta-variable 不能共存。下面是 meta-variable 变量名的典型表示(大写加下划线)。特定的系统可以定义不同的表示方法。

 meta-variable-name = "AUTH_TYPE" | "CONTENT_LENGTH" |
                           "CONTENT_TYPE" | "GATEWAY_INTERFACE" |
                           "PATH_INFO" | "PATH_TRANSLATED" |
                           "QUERY_STRING" | "REMOTE_ADDR" |
                           "REMOTE_HOST" | "REMOTE_IDENT" |
                           "REMOTE_USER" | "REQUEST_METHOD" |
                           "SCRIPT_NAME" | "SERVER_NAME" |
                           "SERVER_PORT" | "SERVER_PROTOCOL" |
                           "SERVER_SOFTWARE" | scheme |
                           protocol-var-name | extension-var-name
      protocol-var-name  = ( protocol | scheme ) "_" var-name
      scheme             = alpha *( alpha | digit | "+" | "-" | "." )
      var-name           = token
      extension-var-name = token

有和 scheme 一样的变量名,也有以 scheme 和 protocol 开头的变量名(比如,HTTP_ACCEPT)。这些变量名的数量和意义可以改变。

服务器可以定义额外的实现定义(implementation defined)meta-variable,其名称应该以 ‘X_’开头。规范并没有区别 0 长度值(NULL)和缺省值。例如,script 并不能区分 ‘http://host/script’ 和 ‘http://host/script?’,这两种情况下,meta-variable QUERY_STRING 都是 NULL 值。

 meta-variable-value = "" | 1*<TEXT, CHAR or tokens of value>

如果一个可选的 meta-variable 的值为 NULL,它会被忽略(认为不存在)。meta-variable 的值必须是大小写敏感的。meta-variable 的字符表示是系统定义的;服务器必须将值转换成这种表示。

4.1.1 AUTH_TYPE

AUTH_TYPE 变量标识服务器用来认证用户的任何机制。它包含一个由客户端协议或服务器实现所定义的大小写不敏感的值。

对于 HTTP,如果客户端请求需要外部访问的授权,服务器必须将值设置为请求头中 Authorization 字段里 auth-scheme 对应的值。

AUTH_TYPE      = "" | auth-scheme
auth-scheme    = "Basic" | "Digest" | extension-auth
extension-auth = token

HTTP 访问授权方案在 RFC 2617 中描述。

4.1.2 CONTENT_LENGTH

CONTENT_LENGTH 变量包含请求中消息体的大小(十进制表示的 octet 的数量)。如果没有消息体,值为 NULL (或者未设置)。

CONTENT_LENGTH = "" | 1*digit

当且仅当请求有消息体时,服务器必须设置该变量。且该变量必须反映服务器移除所有传送编码(transfer coding,http 请求头中的意思:用于指示已经,可能或可能需要应用于实体主体的编码转换,以确保通过网络的“安全传输”,比如:chunked,compress。是站到站的,hop-by-hop)和内容编码(content coding,http 请求头中的意思:指示已经或可以应用于实体的编码转换。比如:gzip,compress。是端到端的,end-to-end。tansfer coding 的编码对象是已经 content coding 过的消息体)后的消息体长度。(那应该就是实际转码后看到的消息体的长度了

4.1.3 CONTENT_TYPE

如果请求包含消息体,CONTENT_TYPE 变量会被设置为消息体的 Internet Media Type

      CONTENT_TYPE = "" | media-type
      media-type   = type "/" subtype *( ";" parameter )
      type         = token
      subtype      = token
      parameter    = attribute "=" value
      attribute    = token
      value        = token | quoted-string

type,subtype 和 parameter attribute 不是大小写敏感的,parameter values 可以是大小写敏感的。media-type 和 其使用在 HTTP/1.1 第 3.7 节中描述。

该变量没有默认值。当且仅当该变量未设置时,script 可以尝试从接收到的数据中判断 media-type。如果仍然未知,script 可以假设一种类型-‘application/octet-stream’,或者返回一个错误(在 6.3.3 节中描述)。

每个 media-type 定义一个可选和强制参数的集合。这个集合可以包含一个大小写不敏感的字符集参数,该参数定义了消息体的字符编码集。如果字符集参数被忽略了,那么默认值按照下列顺序决定:
1. 对于一些 media-type,可以有一些系统定义的默认值。
2. ‘test’ 的默认值为 ‘ISO-8859-1’。
3. 在 media-type 规范中定义的默认值。
4. US-ASCII

如果请求头中包含 Content-Type 字段,服务器必须设置该变量。如果服务器收到一个带有附属实体的但没有 Content-Type 字段的请求,可以去尝试决定正确的内容类型,否则应该忽略该变量。

4.1.4 GATEWAY_INTERFACE

GATEWAY_INTERFACE 必须被设置为服务器和脚本之间交流的 CGI 信息。语法:

 GATEWAY_INTERFACE = "CGI" "/" 1*digit "." 1*digit
4.1.5 PATH_INFO

该变量指定了需要被 CGI script 解释的路径。变量标识了 CGI script 返回的资源或子资源,源自于 URI 层级路径的一部分( 跟在 script 识别自身的部分之后)。与 URI 路径不同,PATH_INFO 不是 URL-encoded 的,不能包含路径段参数(path-segment parameters,uri 段中表示参数而不是路径的部分)。’/’ 表示一个空路径段。

      PATH_INFO = "" | ( "/" path )
      path      = lsegment *( "/" lsegment )
      lsegment  = *lchar
      lchar     = <any TEXT or CTL except "/">

变量值是大小写敏感的,服务器必须保留 URI 中的大小写。服务器可以限制 PATH_INFO 中的值的范围,可以用返回错误的方式拒绝违反限制的请求。这可以包含任意的将编码后的 ‘/’ 解码到 PATH_INFO 的请求,因为这会让 script 接收到错误的信息(编码后的 ‘/’ 在 PATH_INFO 中仍应保持编码后的状态)。如何对待路径中的非 US-ASCII 字符,是 system-defined。

URL-encoded 的 PATH_INFO 是 Script-URI 的 <extra-path>部分,跟在 SCRIPT_NAME 部分之后。(参见 3.3 节)

4.1.6 PATH_TRANSLATED

PATH_TRANSLATED 变量由 PATH_INFO 值派生的,将其解析为一个本地 URI,执行合适的 virtual-to-physical 转换来匹配服务器文档库结构(document repository structure)。结果中允许的字符集是 system-defined。

PATH_TRANSLATED = *<any character>

这是将被请求访问的文件位置:<scheme> "://" <server-name> ":" <server-port> <extra-path>
<scheme>就是原始客户端请求的 scheme。extra-path是 URL-encoded 的 PATH_INFO,其中’;’,’=’,’?’是保留字符。举例,请求如下:

http://somehost.com/cgi-bin/somescript/this%2eis%2epath%3binfo

则其 PATH_INFO 的值:

/this.is.the.path;info

内部 URI 将会由 scheme,服务器位置和 URL-encoded 的 PATH_INFO 组成:

http://somehost.com/this.is.the.path%3binfo

这将被翻译成服务器文档库中的一个位置,可能是像这样的文件系统路径:

/usr/local/www/htdocs/this.is.the.path;info

PATH_TRANSLATED 就是翻译后的值。

服务器必须保留 extra-path 的大小写,除非文档库支持大小写不敏感的名称。 If the repository is only case-aware, case-preserving, or case-blind with regard to document names, the server is not required to preserve the case of the original segment through the translation.

服务器生成 PATH_TRANSLATED 的算法是 implement-defined,因此使用该变量的 CGI 脚本的可移植性会受到限制。

如果请求 uri 包含 path-info 部分,那么服务应该设置该变量。如果 PATH_INFO 为 NULL,则 PATH_TRANSLATED 变量必须设置为 NULL 或未设置。

4.1.7 QUERY_STRING

QUERY_STRING 变量包含一个 URL-encoded 的搜索或参数字符串;它提供给 CGI script 信息来影响或改进 script 返回的文档。

搜索字符串的语法在RFC 2396第三节中定义。QUERY_STRING 是大小写敏感的。

      QUERY_STRING = query-string
      query-string = *uric
      uric         = reserved | unreserved | escaped

解析和解码查询字符串时,解析细节、保留字符和非 US-ASCII 字符的支持依赖于上下文。比如,来自 HTML 文档的表单提交使用 application/x-www-form-urlencoded 编码,则 ‘+’,’&’ 和 ‘=’ 是保留字符,非 US-ASCII 字符可能被认为是 IOS-8859-1 编码的。

QUERY_STRING 值就是 Script-URI 的 <query-string>部分。

服务器必须设置该变量,如果 Script-URI 不包含查询部分,那么 QUERY_STRING 必须设置为空字符串(”“)。

4.1.8 REMOTE_ADDR

REMOTE_ADDR 变量必须设置为向服务器发送请求的客户端的网络地址。

      REMOTE_ADDR  = hostnumber
      hostnumber   = ipv4-address | ipv6-address
      ipv4-address = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
      ipv6-address = hexpart [ ":" ipv4-address ]
      hexpart      = hexseq | ( [ hexseq ] "::" [ hexseq ] )
      hexseq       = 1*4hex *( ":" 1*4hex 

IPv6 地址在RFC 3513中描述。

4.1.9 REMOTE_HOST

REMOTE_HOST 包含向服务器发送请求的客户端的全限定的域名,不存在则为 NULL。全限定域名的格式在RFC 1034的 3.5节和RFC 1123的 2.1 节中有描述。域名不是大小写敏感的。

      REMOTE_HOST   = "" | hostname | hostnumber
      hostname      = *( domainlabel "." ) toplabel [ "." ]
      domainlabel   = alphanum [ *alphahypdigit alphanum ]
      toplabel      = alpha [ *alphahypdigit alphanum ]
      alphahypdigit = alphanum | "-"

服务器应该设置这个变量。如果无法获得主机名,可以用 REMOTE_ADDR 的值代替。

4.1.10 REMOTE_INDENT

该变量可以用来提供关于连接的身份信息,通过对远程代理进行RFC 1413请求的方式。服务器可以选择不支持这个特性,或由于效率原因不请求数据,或者不返回可用的身份数据。

REMOTE_IDENT = *TEXT

返回的数据可以用于授权,但可信度应该是最低的。

4.1.11 REMOTE_USER

REMOTE_USER 变量表示客户端提供的身份信息字符串,用于用户授权的一部分。

REMOTE_USER = *TEXT

如果客户端请求要求 HTTP 授权(比如,AUTH_TYPE 变量设置为 ‘Basic’ 或 ‘Digest’),那么 REMOTE_USER 的值必须被设置为提供的 user-ID。

4.1.12 REQUEST_METHOD

该变量必须被设置为 4.3 节中描述的方法。

      REQUEST_METHOD   = method
      method           = "GET" | "POST" | "HEAD" | extension-method
      extension-method = "PUT" | "DELETE" | token

方法是大小写敏感的。

4.1.13 SCRIPT_NAME

SCRIPT_NAME 必须被设置为标识 CGI script 的 URI 路径(非 URL-encoded)。语法与 PATH_INFO 相同。

SCRIPT_NAME = "" | ( "/" path )

开头的 ‘/’ 不是路径的一部分,是可选的如果路径为 NULL,但是变量必须被设置。

SCRIPT_NAME 字符串形成 Script-URI 路径组件的某些主要部分,以 implementation-defined 的方式。SCRIPT_NAME 不包含 PATH_INFO 部分。

4.1.14 SERVER_NAME

该变量必须被设置为客户端请求指向的服务器 host 的名称,值是 hostname 或者 网络地址,且大小写不敏感。该变量组成了 Script-URI 的 <server-name> 部分。

      SERVER_NAME = server-name
      server-name = hostname | ipv4-address | ( "[" ipv6-address "]" )

一个已经部署的多个 HTTP 虚拟 host 共享一个 IP 地址的服务器可以有不止一个可能的值。这种情况下,需要根据请求头中的 host 信息来确定正确的值。

4.1.15 SERVER_PORT

该变量必须为客户端请求被接收的 TCP/IP 端口号。该变量组成了 Script-URI 的端口部分。

      SERVER_PORT = server-port
      server-port = 1*digit
4.1.16 SERVER_PROTOCOL

该变量必须被设置为 CGI 请求所使用的应用协议的名字和版本号。协议版本号在服务器和客户端交流过程中可以不同。

      SERVER_PROTOCOL   = HTTP-Version | "INCLUDED" | extension-version
      HTTP-Version      = "HTTP" "/" 1*digit "." 1*digit
      extension-version = protocol [ "/" 1*digit "." 1*digit ]
      protocol          = token

这里,协议定义了服务器与客户端之间传递信息的一些语法。值不是大小写敏感的,且一般为大写。该值与 Script-URI 中的 scheme 部分不同,scheme定义了客户端用来与服务器通信的整体访问机制。例如,使用协议 “HTTP” 到达脚本的请求可能使用了 “https” scheme。

‘include’ 是一个知名的 SERVER_PROTOCOL 值。该值表示当前文档是整个文档的一部分,而不是客户端请求的直接目标。script 应当认为这是个 HTTP/1.0 请求。

4.1.17 SERVER_SOFTWARE

该变量必须设置为产生 CGI 请求(并运行网关)的信息服务器软件的名称和版本,它应该与报告给客户端的服务器描述一致,如果有的话。(比如,nginx/1.46)

      SERVER_SOFTWARE = 1*( product | comment )
      product         = token [ "/" product-version ]
      product-version = token
      comment         = "(" *( ctext | comment ) ")"
      ctext           = <any TEXT excluding "(" and ")">
4.1.18 Protocol-Specific Meta-variables

服务器应该根据请求的 protocol 和 scheme 来设置 meta-variable。这些变量的解释依赖于 SERVER_PROTOCOL 的协议版本。当 scheme 的值和 protocol 不同时,服务器可以设置一个名为 scheme 的 meta-variable,该变量值为非 NULL,用来告诉 script 请求所用的 scheme。

以 HTTP_ 开头的 meta-variable 来自于请求的请求头,如果请求协议是 HTTP 的话。HTTP 请求头的名字会转为大写,所有的 ‘-’ 会转为 ‘‘,并会加上’HTTP’ 前缀。数据可以与客户端发送的一致,也可以以不改变语义的方式重写。如果多个请求头字段有相同的名称,服务器必须将他们重写为有相同语义的单个值。同样的,一个多行的请求头字段必须转为一个单行。如果有必要的话,服务器必须改变数据的表述(比如字符集)来符合 CGI meta-variable 的要求。

服务器并不要求对所有的请求头字段创建 meta-variable。尤其是,它应该移除任何包含授权信息的字段,比如 ‘Authorization’;或者在其他变量(例如“Content-Length”和“Content-Type”)中对脚本可用。服务器可以移除仅与客户端通信问题(如“Connection”)相关的标题字段。

4.2 请求消息体

script 访问请求数据的方式是 system-defined。除非另有定义,否则这将通过读取 ‘standard input’ 的文件描述符或文件句柄。

      Request-Data   = [ request-body ] [ extension-data ]
      request-body   = <CONTENT_LENGTH>OCTET
      extension-data = *OCTET

如果 CONTENT_LENGTH 非 NULL,请求包含请求体。服务器必须使 script 对这么多字节可读。当 CONTENT_LENGTH 个字节被读取后,服务器可以设置一个文件结束的标记,或者可以提供扩展数据。因此,script 禁止尝试读取超过 CONTENT_LENGTH 个字节,即使更多的数据是可用的。但是,它不一定要读取所有的数据。

对于非解析头文件(NPH)脚本(第5节),服务器应该尝试确保提供给脚本的数据与客户端提供的完全一致,并且服务器不会改变它。

由于请求体不支持传输编码,服务器必须从消息主体中删除这些编码,并重新计算 CONTENT_LENGTH。如果这不可能(比如,需要大容量缓存),服务器应当拒绝该请求。也可以从消息体中删除内容编码。

4.3 请求方法

请求方法,也是 REQUEST_METHOD 的值,标识了产生响应的过程中 script 使用的处理方法。script 作者能够选择实现最合适于特定应用的方法。如果 script 接收到了一个请求但不支持请求的方法,应当拒绝该请求并返回一个错误。

4.3.1 GET

GET 方法表示,script 应该基于 meta-variable 的值来产生一个文档。按照惯例,GET方法是“安全的”和“幂等的”,并且除了生成文档之外,不应该采取其他行动。

GET 方法的意义可以通过 protocol-specific meta-variable 来修改和改进。

4.3.2 POST

POST 方法用来请求 script 基于请求的消息体和 meta-variable 进行处理和产生一个文档。一个普遍的使用是在 HTML 中的表单提交,用来启动 script 的处理并产生一个永久的影响,比如数据库的修改。

script 在读取附带的消息体时,必须检查 CONTENT_LENGTH 值,在处理前应该检查 CONTENT_TYPE 的值。

4.3.3 HEAD

HEAD方法请求脚本执行处理以返回响应头字段,而不提供响应消息体。script 禁止对一个 HEAD 请求返回一个请求消息体。如果 script 返回了请求消息体,服务器在返回响应时必须去除该消息体。

4.3.4 protocol-specific 方法

script 可以实现任意 protocol-specific 方法,比如 HTTP 1/1 的 PUT 和 DELETE。此时,应当检查 SERVER_PROTOCOL 的值。

服务器可能会决定某些方法不适合或不允许使用 script,并且可能会处理方法本身或向客户端返回错误。

4.4 script 命令行

一些系统支持提供一个字符串数组给 CGI script 的方法。这仅在 ‘indexed’ HTTP查询中使用,该查询由具有不包含任何未编码的 “=” 字符的 URI 查询字符串的 ‘GET’ 或 ‘HEAD’ 请求标识。对于这样的一个请求,服务器应当把查询字符串看做搜索字符串,并解析成一些单词,使用如下规则:

      search-string = search-word *( "+" search-word )
      search-word   = 1*schar
      schar         = unreserved | escaped | xreserved
      xreserved     = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "," |
                      "$"

解析后,每个搜索词都是 URL-decoded,可以以 system-defined 的方式编码,之后被加到命令行参数列表。

如果服务器不能创建参数列表的任何部分,那么服务器禁止产生任何命令行信息。比如,参数数量超过了服务器或操作系统的限制,或其中某个单词不符合一个参数的表示。

script 应当检查 QUERY_STRING 是否包含一个未编码的 ‘=’,如果是,则不应该使用命令行参数。

5 NPH script

5.1 识别

服务器可以支持 NPH(Non-Parsed Header) script,对于这些 script 服务器不会对响应进行任何处理。

规范没有提供任何机制来仅仅根据 script 的输出来判断 script 是否是 NPH script。按照惯例,任何 script 只能产生一种输出(NPH or CGI),因此 script 本身可以被描述为 NPH script。支持 NPH 的服务器必须提供一种 implementation-defined 机制来识别 NPH script,可能基于 script 的名字或者位置。

5.2 NPH 响应

script 必须有一个 system-defined 方法来发送数据给服务器或客户端;script 必须总是返回一些数据。除非另有定义,否则这将与传统 CGI script 相同。

一般来说,NPH script 仅针对 HTTP 客户端请求进行定义。一个 (HTTP) NPH Script 必须返回完整的 HTTP 响应信息,这在 HTTP 规范的第 6 节有描述。script 必须使用 SERVER_PROTOCOL 来决定合适的响应格式。script 必须也考虑到请求中的任何通用的或 protocol-specific meta-variable。

服务器必须保证 script 发送给客户端的请求是没被修改的。这要求 script 在头字段中使用正确的字符集(US-ASCII and ISO 8859-1 for HTTP)。服务器应当尝试确保 script 输出直接发送给了客户端,用最小且传输不可见的内部缓存。

除非实现另有定义,script 禁止在响应中指示客户端能够在同一个连接中发送更多请求。

6 CGI 响应

6.1 响应处理

script 必须总是提供一个非空的响应,因此应有一个 system-defined 方法来发送响应数据给服务器。除非另有定义,否则将通过 ‘标准输出’ 文件描述符。

script 处理请求和准备响应时必须检查 REQUEST_METHOD。

服务器可以设置一个超时时间,该时间内必须从 script 中接收到数据,否则可以终止 script 进程。

6.2 响应类型

响应包含一个消息头和消息体,用空行分隔。消息头可以包含一个或多个头字段。消息体可以是 NULL。

generic-response = 1*header-field NL [ response-body ]

该脚本必须返回文档响应,本地重定向响应或客户端重定向(带有可选文档)响应之一。在下面的响应定义中,响应中标题字段的顺序并不重要(尽管在 BNF 中出现这种情况)。头字段定义在 6.3 节中。

      CGI-Response = document-response | local-redir-response |
                     client-redir-response | client-redirdoc-response
6.2.1 文档响应

CGI script 能够在文档响应中返回一个文档给用户,附带一个可选的错误码来指示响应的状态。

 document-response = Content-Type [ Status ] *other-field NL
                          response-body

script 必须返回一个 Content-Type 头字段。Status 头字段是可选的,不设置时默认为 200 ‘OK’。服务器必须对 script 的输出做任何合适的修改,来确保对客户端的响应符合响应协议版本。

6.2.2 本地重定向响应

对于本地资源,CGI script 能够在 Location 头字段中返回一个 URI 路径和查询字符串(local-pathquery)。这告诉服务器应该使用指定的路径重新处理请求。(本地重定向时,响应从 script 到了服务器,然后服务器处理后再次给 script 处理,没有返回到客户端

local-redir-response = local-Location NL

script 禁止返回其他头字段和消息体,返回的响应必须和包含该 URL 的请求所得到的响应一致。

6.2.3 客户端重定向响应

CGI script 可以在 Location 头字段中返回一个绝对的 URI 路径,来告诉客户端应该使用指定的 URI 重新处理请求。

client-redir-response = client-Location *extension-field NL

Status 头字段必须提供,且必须包含一个 302 ‘NOT FOUND’ 值,或者可以包含一个扩展码,即表示客户端重定向的另一个有效的状态码。服务器必须对 script 的输出做任何合适的修改,来确保对客户端的响应符合响应协议版本。

6.3 响应头字段

请求头字段可以是需要服务器解释的 CGI 或扩展头字段,或者是需要包含在返回给客户端的响应中的协议指定的头字段。必须至少提供一个 CGI 字段;每个 CGI 字段禁止在响应中出现超过一次。响应头字段语法:

      header-field    = CGI-field | other-field
      CGI-field       = Content-Type | Location | Status
      other-field     = protocol-field | extension-field
      protocol-field  = generic-field
      extension-field = generic-field
      generic-field   = field-name ":" [ field-value ] NL
      field-name      = token
      field-value     = *( field-content | LWSP )
      field-content   = *( token | separator | quoted-string )

字段名不是大小写敏感的。字段值为 NULL 等同于没有发送该字段。CGI 响应中的每个头字段必须在一个单独的行上;CGI/1.1 不支持连续行。在 ‘:’ 和 字段值之间允许有空格(不是 ‘:’ 和字段头之间),在字段值的 tokens 间也允许有空格。

Content-Type

Content-Type 响应字段设置了主体(entity body)的 Internet Media Type。

Content-Type = "Content-Type:" media-type NL

如果一个主体被返回,script 必须在响应中提供一个 Content-Type 字段。如果没有这么做,服务器不应该尝试决定正确的内容类型。字段值应该未被修改地发送到客户端,除非字符集参数变了。

除非它是系统定义的,否则如果协议是HTTP,客户端为文本媒体类型(text media-types)的默认字符集是 ISO-8859-1,其他则是 US-ASCII 字符集。因此,scirpt 应该包含一个字符集参数。在 HTTP/1.1 规范的 3.4.1 节中可以看到关于这个事务的讨论。

Location

Location 头字段用来告诉服务器 script 将要返回一个文档的引用而不是一个实际的文档(见 6.2.3 和 6.2.4)。可以是一个绝对 URI (可以带有段标识符),用来告诉客户端去获取被引用的文档;可以是本地 URI 路径(可以包含查询字符串),用来告诉服务器去获取被引用的文档,然后作为响应返回给客户端。

      Location        = local-Location | client-Location
      client-Location = "Location:" fragment-URI NL
      local-Location  = "Location:" local-pathquery NL
      fragment-URI    = absoluteURI [ "#" fragment ]
      fragment        = *uric
      local-pathquery = abs-path [ "?" query-string ]
      abs-path        = "/" path-segments
      path-segments   = segment *( "/" segment )
      segment         = *pchar
      pchar           = unreserved | escaped | extra
      extra           = ":" | "@" | "&" | "=" | "+" | "$" | ","

absoluteURI 的语法来自于规范 RFC 2396RFC 2732。一个有效的 absoluteURI 总是以 scheme 的名字加 ‘:’ 开头;scheme 的名字以一个字母开头,后面跟上字母数字,’+’,’-’ 或 ‘.’。本地 URI 路径和查询必须是一个绝对路径,不是相对路径或 NULL,因此必须以 ‘/’ 开头。

请求附带的任何消息体(比如 POST 请求)可能无法用于重定向目标的资源。

6.3.3 Status

Status 头字段包含一个三位整数的结果码,表示 script 尝试处理请求的成功的级别。

      Status         = "Status:" status-code SP reason-phrase NL
      status-code    = "200" | "302" | "400" | "501" | extension-code
      extension-code = 3digit
      reason-phrase  = *TEXT

状态码 200 ‘OK’ 表示成功,是文档响应的默认值。状态码 302 ‘FOUND’ 用来和 LOCATION 头字段及响应消息体一起使用。状态码 400 ‘Bad Request’ 可以对一个未知的请求格式使用,比如丢失 CONTENT_TYPE。状态码 501 ‘NOT Implemented’ 用于对不支持的 REQUEST_METHOD 使用。

其他有效的状态码在 HTTP 规范的 6.1.1 节及 IANA HTTP Status Code Registry 有提到,可以用来附加或代替上面列出的状态码。在使用 HTTP/1.1 状态码之前,服务器应该检查 SERVER_PROTOCOL 的值。script 可以用 405 ‘Method Not Allowed’ 来拒绝使用不支持方法的 HTTP/1.1 请求。

返回一个错误状态码不一定表示 script 本身出现了错误。举例,被服务器调用作为错误处理的 script 应该返回符合服务器错误状态的码。

原因短语是将错误返回给客户供人类理解的文本描述。

6.3.4 Protocal-specified 头字段

script 可以返回由 SERVER_PROTOCOL(HTTP/1.0 或 HTTP/1.1) 的规范定义的与响应信息有关的任意其他的头字段。服务器必须将头数据从 CGI 头语法翻译到 HTTP 的头语法,如果两者不同的话。举例,CGI 脚本使用的新行(比如 UNIX’s US-ASCII LF)的字符可能与 HTTP 使用的不同(US-ASCII CR followed by LF)。

脚本不能返回与客户端通信问题有关的和可能会影响服务器将响应发送到客户端的能力的任何头字段。服务器可以移除由客户端返回的任何这样的头字段。它应该解决脚本返回的头字段和它自己发送的头字段之间的任何冲突。

6.3.5 扩展头字段

可以有额外的 implementation-defined 的 CGI 头字段,字段名应该由 ‘X-CGI-’ 开头。服务器可以忽略或删除从 script 接收的以 ‘X-CGI-’ 开头的任何未识别的字段。

6.4 响应消息体

响应消息体是服务器返回给客户端的附带的文档。服务器必须读取由 script 提供的所有的数据,直到 script 通过 end-of-file 标识消息体的结束。

消息体应该未被修改的发送给客户端,除非是 HEAD 请求或任何传输编码,内容编码或字符集转换。

response-body = *OCTET

7 系统规范

7.1 AmigaDOS

  • Meta-Variables
    Meta-variables are passed to the script in identically named
    environment variables. These are accessed by the DOS library
    routine GetVar(). The flags argument SHOULD be 0. Case is
    ignored, but upper case is recommended for compatibility with
    case-sensitive systems.

  • The current working directory
    The current working directory for the script is set to the
    directory containing the script.

  • Character set
    The US-ASCII character set [9] is used for the definition of
    meta-variables, header fields and values; the newline (NL)
    sequence is LF; servers SHOULD also accept CR LF as a newline.

7.2 UNIX

对于 UNIX 兼容的操作系统:

  • Meta-variables
    以同名环境变量的方式传递给 script,通过 C 库的 getenv() 方法或 variable environ 访问。
  • 命令行
    通过 main() 函数的 argc,argv 访问。单词中任何在 Bourne shell 中 ‘active’ 的字符都需要一个反斜杠转义。
  • 当前工作目录
    应当被设置为包含 script 的目录。
  • 字符集
    US-ASCII 字符集除了 NUL,用来定义 meta-variables,头字段和 CHAR 值;TEXT 值使用 ISO-8859-1。PATH_TRANSLATED 能够包含任何 8 位字节,除了 NUL。新行用 LF 表示;服务器也应当支持用 CR LF 来表示新行。

7.3 EBCDIC/POSIX

对于使用 EBCDIC 字符集的 POSIX 兼容的操作系统:

  • Meta-variables
    以同名环境变量的方式传递给 script,通过 C 库的 getenv() 方法访问。
  • 命令行
    通过 main() 函数的 argc,argv 访问。单词中任何在 Bourne shell 中 ‘active’ 的字符都需要一个反斜杠转义。
  • 当前工作目录
    应当被设置为包含 script 的目录。
  • 字符集
    IBM1047字符集,除了 NUL,用来定义 meta-variables,头字段和值,TEXT 以及 PATH_TRANSLATED 的值。新行用 LF 表示;服务器也应当支持用 CR LF 来表示新行。
  • 默认媒体类型字符集
    text (以及其他 implementation-defined )媒体类型的默认字符集是 IBM1047。

8 实现

8.1 服务器建议

尽管服务器和 CGI 脚本在处理 URL 路径(客户端 URL 和 PATH_INFO 数据)时不需要一致,服务器作者可能希望能强制一致。所以服务器实现应该为下列情况指定它的行为:
- 定义对允许的路径段的任何限制,尤其对于是否允许非终止的 NULL 段。
- 定义 ‘.’ 或 ‘..’ 路径段的行为;即,是否被禁止,按照普通路径段对待或依据相对 URL 规范解释。
- 定义实现的任何限制,包括路径和搜索字符串长度的限制,以及服务器将解析的头字段数量的限制。

8.2 script 建议

如果 script 不打算处理 PATH_INFO 数据,那么如果 PATH_INFO 不是 NULL,应该用 404 NOT FOUND 拒绝请求。

如果一个表单的输出正在被处理,检查 CONTENT_TYPE 是 “application/x-www-form-urlencoded” 或 “multipart/form-data”。如果 CONTENT_TYPE 是空的, script 能够用 415 ‘Unsupported Media Type’ 拒绝请求(如果协议支持的话)。

当解析 PATH_INFO,PATH_TRANSLATED 以及 SCRIPT_NAME 时,script 应当小心空路径段 ‘//’,以及特殊路径段 ‘.’ 和 ‘..’。他们应当在系统调用前被删除,或者用 404 NOT FOUND 拒绝请求。

当返回头字段时,script 应当尽快发送 CGI 头字段,并在任何 HTTP 头字段前发送他们。这个可能有助于减少服务器的内存需求。

script 作者应当清楚,REMOTE_ADDR 和 REMOTE_HOST 可能不能标识请求的最终来源。他们识别客户端对服务器的即时请求;客户端可以是一个代理,网管或其他中介,用来代表真实的客户端源。

9 安全考虑

9.1 安全方法

正如 HTTP 规范的安全考虑所讨论的,已经建立了约定,即 GET 和 HEAD 方法应该是 ‘安全’ 和 ‘幂等’(重复请求与单个请求具有相同的效果) 。

9.2 头字段包含敏感信息

一些 HTTP 头字段可能包含敏感信息,服务器不应该将这些信息传递给 script,除非显示地配置要这么做。举例,服务器通过使用 Basic authentication scheme 来保护脚本,之后客户端会发送一个包含账号密码的请求头字段 Authorization。服务器验证该信息,因此在没有充分考虑前不应该将密码通过 HTTP_AUTHORIZATION meta-variable 传递。这也应该用于 Proxy-Authorization 头字段,以及相关的 HTTP_PROXY_AUTHORIZATION meta-variable。

9.3 数据隐私

请求中的机密数据应作为 POST 请求的一部分放入消息体中,而不要放在 URI 或消息头中。在某些系统上,用于将 meta-variables 传递给 script 的环境可能对其他 script 或用户可见。另外,许多现有的服务器,代理和客户端将永久记录 URI,它可能被第三方看到。

9.4 信息安全模型

对于使用 TLS 的客户端连接,安全模型适用于客户端和服务器之间,而不适用于客户端和 script 之间。处理 TLS 会话是服务器的责任,因此通过客户端身份验证的是服务器,而不是 CGI script。该规范没有提供 script 验证调用它的服务器的机制。 CGI 请求和响应消息没有强制的健全性。

9.5 script 对服务器的干扰

最普遍的 CGI 实现中, script 作为一个子进程调用,使用和服务器进程一样的用户和组。应当确保服务器进程不能被 script 干扰,包括服务器的配置,文档和日志文件。

如果 script 是通过调用链接到服务器软件的函数执行的(在编译时或运行时),应该注意保护服务器的核心内存,或者确保不被信任的代码不会被执行。

9.6 数据长度和缓冲区考虑

本规范没有对呈现给 script 的消息体长度的限制。script 不应该假定任何大小的静态分配的缓冲区都足以一次包含整个提交。使用没有溢出检查的固定长度的缓冲,可能会导致攻击者利用操作系统的 stack-smashing 和 stack-overflow 攻击。script 可能会将大量提交保存到磁盘或其他缓冲介质,但是快速连续的大量提交可能会导致拒绝服务。如果消息体的 CONTENT_LENGTH 大于资源允许值,script 应该用协议版本允许的错误状态响应;潜在适用的状态码包括 503 ‘Service Unavailable’ (HTTP/1.0 and HTTP/1.1),413 ‘Request Entity Too Large’ (HTTP/1.1),和 414 ‘Request-URI Too Large’ (HTTP/1.1)。

相似的考虑也应用在服务器处理来自 script 的 CGI 响应。对 script 返回的头或消息体长度没有限制;服务器不应该假设任何大小的静态分配的缓存能够容下整个响应。

9.7 无状态处理

Web 的无状态特性让 script 的执行和资源的获取独立于所有其他脚本,即使多个请求构成单个概念上的 Web 事务。因此,script 不应当对于提交请求的用户代理的上下文做任何假设。尤其是,对于客户端获取的表单和内容的数据,在允许被用于敏感目的前(比如作为其他应用,命令或操作系统服务的输入),script 应当检查这些数据,验证他们是否有效。这些敏感用途包括但不仅限于,系统调用的参数,数据库写入,动态评估源代码,订单输入或其他安全进程。保护应用程序免受无效输入,无论无效性是来自于用户错误,逻辑错误还是恶意行为。

参与多请求事务的 script 作者应该对验证状态信息特别谨慎;危险值替换被认为是安全的提交的部分,会导致不期望的结果。这种类型的颠覆发生在事务前一阶段的数据被改变,而这些数据并不打算着被客户控制(例如,隐藏的HTML表单元素,cookie,嵌入的 URL 等)。

9.8 相对路径

服务器应该小心请求 URI 中的 ‘..’ 路径段。在将它们分成 script-path 和 extra-path 之前,这些应该在请求 URI 中被删除或解析。或者,当使用 extra-path 来查找 PATH_TRANSLATED 时,应当小心避免解析的路径来自期望的路径层级之外。

9.9 Non-parsed Header 输出

如果脚本返回一个 Non-parsed Header 输出,并由客户端以其本地协议进行解释,那么该脚本必须解决与该协议有关的所有安全性考虑事项。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值