Studying HTTP

Help

CGI - Common Gateway Interface

この頁では、Webリソースを動的に生成し、Web上で双方向のやり取りを可能にする技術であり、Webの誕生期から現在まで広く使用されているCGIについて、仕様書を中心に解説します。

CGI とは

CGI(Common Gateway Interface)とは、WWWサーバが、WWWクライアントからの要求に応じて、外部プログラムを起動するための仕組みの事を言います。 たとえば、Webページへの訪問者数を表示するためのアクセスカウンタや、訪問者にメッセージを記入してもらうための掲示板などで、現在も広く使用されている技術です。 CGIの現在のバージョンは“CGI/1.1”で、The Common Gateway Interface (CGI) Version 1.1 という文書で規定されています。

Common Gateway Interface (CGI) は、プラットフォームに依存しない方法で情報サーバのもとで、外部プログラム、ソフトウェア、ゲートウェイを動かするための単純なインタフェースである。 現在、サポートされている情報サーバは、HTTP サーバである。

このインタフェースは、1993 年以来 World Wide Web (WWW) にて使用されている。 この仕様書は、the U.S. National Centre for Supercomputing Applications にて開発、文書化された 'CGI/1.1' インタフェースの '現在の慣習' のパラメータを定義するものである。 また、この文書は UNIX ® や、他の同様のシステム上での CGI/1.1 インタフェースの使用方法も定義する。

CGIが動作する状況をまとめると以下のようになります。

  1. WWWサーバがユーザエージェントからHTTPリクエストメッセージを受け取る
  2. WWWサーバは外部のプログラムに処理を依頼する
  3. CGIプログラムが実行される
  4. プログラムがWWWサーバに実行結果を返す
  5. WWWサーバがステータスライン等をつけて、HTTPレスポンスメッセージを送り返す
  6. ユーザエージェントが処理結果を受け取る

CGIでは、WWWサーバが外部プログラムに処理を依頼しますが、ここで「外部プログラム」とは「WWWサーバ以外のソフトウェア」を意味します。 すなわち、「CGIに対応したWWWサーバ」とは、「外部プログラムへ処理を依頼するための仕組みを持っているWWW サーバ」であると言えます。 CGIプログラムの処理結果がWWWサーバの認識可能な形式になっていれば、WWWサーバがHTTPヘッダを付加して、ユーザエージェントに返される事になります。

本来、WWWサーバとは「自身が保持している文書をただ送り返すためのもの」なのですが、CGIを使う事によって、外部プログラムの処理結果に基づいて動的なリソースを生成し、送信できるようになります。 言い換えれば、CGIはCGIプログラムが制作したリソースをそのまま、既にサーバ上に存在するリソースと同じようにクライアントに返すためのものであるのです。

したがって、例えば二つの異なるWWWサーバがあった場合、以下の条件を満たせば全く同じCGIプログラムを動作させることができます。

  1. どちらのWWWサーバもCGIに対応している
  2. どちらのコンピュータもそのCGIを動作させるプログラムを理解できる

CGI で使用される外部変数

まず、外部変数という用語の意味について、RFC 3875 の section 1.4 をご覧下さい。

'外部変数 {meta-variable}'
サーバからスクリプトへ情報を運ぶ名前付パラメータ。 多くの一般的な実装ではオペレーティングシステムの環境における変数であるが、そうである必要はない。

CGI 外部変数は、一般に環境変数という用語で知られます。 しかし、環境変数とは、一般にOS のシェルなどに設定されている、システムの属性を記録している変数の事を指します。 CGI に対応しているほとんど全てのWWW サーバは、CGI プログラムに要求があると、適当な環境変数を設定してそのプログラムを起動していますが、仕様的にはそうである必要はありません。 そのため、このサイトでは「外部変数」で用語を統一します。

それでは、外部変数について規定している、RFC 3875 の section 4 をご覧下さい。

外部変数は、サーバからスクリプトへ渡されるリクエストについてのデータを含み、システム的に規定される方法をもってスクリプトによってアクセスされる。 外部変数は、大文字・小文字を区別しない名前によって識別される; すなわち、二つの異なる変数においてその名前の大文字・小文字のみが異なるものはありえない。 ここでは、大文字と下線 ("_") の正式な表現を使用してそれらを示す。 特定のシステムでは、異なる表現を定義する事ができる。

 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

外部変数は、大文字・小文字を区別しません。 meta-variable-name は、仕様的には token なので、あらゆる文字を取り得ますが、慣習的に大文字と下線 ("_") のみが使われています。

scheme と同じ名前の外部変数、そしてプロトコルあるいは scheme の名前で始まる名前 (例えば、HTTP_ACCEPT) も定義される。 このような変数の数と意味は、この仕様書とは無関係に変化するであろう。 (section 4.18 も参照)

サーバは、実装的に規定される拡張外部変数を追加的に設定する事ができるが、その名前は先頭に "X_" をつけるべきである

例えば、アクセスプロトコルとして HTTP を利用した場合、「HTTP という外部変数」及び「HTTP_ で始まる外部変数」が設定される可能性があります。 但し、どんな外部変数が設定されるかは、RFC 3875 の範囲を超えます。

また、サーバは「X_ で始まる外部変数」を自由に設定できます。

この仕様書は、長さゼロ (NULL) の値と値なしを区別しない。 例えば、二つのリクエスト http://host/script と http://host/script? は共に QUERY_STRING 外部変数が NULL になるため、スクリプトはこれらを区別する事ができない。

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

省略可能な外部変数は、その値が NULL の場合、省略して (設定しないでおいて) もよい。 外部変数の値は、他に言及されていなければ、大文字・小文字を区別するとみなされなければならない。 外部変数における文字の表現はシステム的に規定される; すなわち、サーバはそれらの値をその対応する表現に変換しなければならない

外部変数において、「NULL (長さ 0 の値) という値を持つ」と「そもそも値が設定されていない」は同じ意味となります。 外部変数名は大文字・小文字を区別しませんが、外部変数の値は大文字・小文字を区別するとみなされなければなりません。 外部変数の値に非 ASCII コードが使用されていた場合の文字セットの解釈は、システム依存となります。

AUTH_TYPE

AUTH_TYPE 変数は、ユーザを認証するためにサーバによって使用されるメカニズムを識別する。 ここには、クライアントのプロトコルやサーバの実装によって定義される大文字・小文字を区別しない値を含む。

HTTP において、クライアントのリクエストが外部のアクセスについての認証を要求する場合、サーバはこの変数の値をリクエストの Authorization ヘッダフィールド内の 'auth-scheme' トークンから設定しなければならない

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

このセッションで使用される HTTP アクセス認証メカニズムを示します。 ここで取り得る値として、現時点でHTTP アクセス認証メカニズムについて最新版の RFC である RFC2617 より、あらかじめ BasicDigest が示されていますが、将来的にこれ以外のアクセス認証メカニズムが開発されればこの限りではありません。

CONTENT_LENGTH

CONTENT_LENGTH 変数は、リクエストが message-body を持つ場合、オクテットの十進数でそのサイズを含む。 データが付加されていなければ、 NULL (あるいは未設定) である。

 CONTENT_LENGTH = "" | 1*digit

サーバは、リクエストが message-body エンティティが伴っている場合にのみ、この外部変数を設定しなければならない。 CONTENT_LENGTH 値は、サーバが任意の転送コーディングや内容コーディングを取り除いた後、message-body の長さを反映しなければならない。

リクエストに同封されるメッセージボディの長さです。 メッセージボディを持つリクエストの場合はこの外部変数が設定されなければなりませんが、メッセージボディを持たないリクエストの場合は、この値が 0 と与えられるか、あるいはそもそも設定されません。

メッセージボディに内容コーディング転送コーディングが適用されていた場合は、これを取り除いた後の長さ、すなわちエンティティボディの長さを設定しなければなりません。

CONTENT_TYPE

リクエストが message-body を含む場合、CONTENT_TYPE 変数は message-body のインターネットメディアタイプ [6] を設定する。

 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 属性名は大文字・小文字を区別しない。 parameter の値は、大文字・小文字を区別するかもしれない。 HTTP におけるメディアタイプとその使用法は、HTTP/1.1 仕様書 [4] の section 3.7 にて記述される。

この変数のための既定値はない。 この変数が設定されていない場合にのみ、スクリプトは受信されるデータのメディアタイプを決定しようとしてもよい。 タイプが未知のままの場合、スクリプトは application/octet-stream というタイプだとみなしてもよいし、あるいは (section 6.3.3 にて記述されるように) エラーをもってそのリクエストを拒否してもよい

リクエストにメッセージボディが同封されていた場合のメディアタイプです。 ここで取り得る値は、下記のいずれかとなります。

但し、リクエストヘッダ中に Content-Type が無い時点でエラー (415 Unsupported Media Type) としてもかまいません。

各メディアタイプは、任意なパラメータと必須なパラメータの集合を定義する。 これは、message-body をコード化する文字セットを定義している、大文字・小文字を区別しない値を持つ charset パラメータを含むかもしれない。 charset パラメータが省略される場合、既定値は最初に以下の規則のいずれかを適用する事によって生成されるべきである:

  1. いくつかのメディアタイプでは、システム的に規定される既定の文字セットが存在するかもしれない
  2. "text" というメディアタイプの既定値は、ISO-8859-1 [4] である。
  3. そのメディアタイプの仕様書にて定義される既定値。
  4. 既定値は US-ASCII である。

サーバは、HTTP Content-Type フィールドがクライアントリクエストヘッダ内に存在する場合は、この外部変数を設定しなければならない。 サーバは、エンティティが付加されており Content-Type ヘッダフィールドのないリクエストを受信した場合は、正しい内容タイプを決定しようとしてもよいが、そうでなければこの外部変数は省略すべきである。

メディアタイプは大文字・小文字は区別しませんが、; 以降のパラメータでは大文字・小文字を区別するかもしれません。 パラメータには、しばしば、メッセージボディで使用されている文字セットを示すための charset が使用されます。 charset が省略される場合、文字セットの解釈は実装依存となり、いわゆる「文字化け」の原因となりうるので、文字セットがわかっている場合はなるべく charset も指定するようにしましょう。

GATEWAY_INTERFACE

GATEWAY_INTERFACE 変数は、スクリプトを介して通信するサーバがによって使用されている CGI の能力{dialect} が設定されなければならない。 構文は:

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

メジャー番号とマイナー番号は分割された整数値として扱われるので、それぞれが複数桁になりうる事に注意せよ。 従って、CGI/2.4 は CGI/2.13 よりも下のバージョンで、さらにそれらは CGI/12.3 よりも下となる。 先行するゼロはスクリプトによって無視されなければならないし、またサーバによって生成されてはならない

この文書は、CGI インタフェースのバージョン 1.1 を定義する。

このサーバが従う CGI 仕様のバージョンです。 この仕様書を満たすような CGI アプリケーションは、この値として CGI/1.1 を返すでしょう。

PATH_INFO

PATH_INFO 変数は、CGI スクリプトによって解釈されるパスを規定する。 これは CGI スクリプトによって返されるリソースあるいはリソースの一部を識別し、URI のパス階層のうちのスクリプト自身を識別する部分の後の部分に由来する。 URI のパスとは異なり、PATH_INFO は URL エンコードされず、パス要素{segment} の引数を含む事はできない。 "/" という PATH_INFO は、単一の空のパス要素{segment} を表す。

 PATH_INFO = "" | ( "/" path )
 path      = lsegment *( "/" lsegment )
 lsegment  = *lchar
 lchar     = <"/" を除く任意の TEXT か CTL>

値は、大文字・小文字を区別するとみなされ、サーバはリクエスト URI にあるようにパス経路の大文字・小文字を保持しなければならない。 サーバは、PATH_INFO についてどんな値が許されるについての制限を課してもよいし、好ましくないとみなす任意の値に遭遇した場合エラーをもってそのリクエストを拒否してもよい。 ここには、それがスクリプトへの情報の喪失を表すので、PATH_INFO 内でデコードされると "/" となるような任意のリクエストを含んでもよい。 同様に、パス内の非 US-ASCII 文字の扱いはシステム的に規定される。

URL エンコードされた PATH_INFO 文字列は、そのパスの SCRIPY_NAME 部分に続く Script-URI (section 3.3 参照) の更なるパス構成要素を形成する。

URI によって与えられるリソースを検索する際には、先頭一致 (与えられた文字を先頭から順に見て一致するかどうかを決定) によって検索されます。 この際、 URI の先頭部分に一致するリソースが発見されても、更にその後に URI が続く場合、残りの部分が PATH_INFO として返される事になります。

例えば、http://www.studyinghttp.net/cgi-bin/abc.cgi/123/XYZ という URI が与えられたとします。 この時、サーバ内に http://www.studyinghttp.net/cgi-bin/abc.cgi というリソースが存在すれば、 このリソースに対し、その際の PATH_INFO には /123/XYZ が与えられる事になります。

PATH_TRANSLATED

PATH_TRANSLATED 変数は、PATH_INFO 値を取得し、それ自身をローカル URI として構文解析し、サーバの文書レポジトリ構造にマッピングする事で適切に仮想的なものから物理的なものへの変換を実行することで得られる。 その結果で許される文字集合は、システム的に規定される。

 PATH_TRANSLATED = *<any character>

これは、以下についてのリクエストによってアクセスされるファイルの場所となり、

 <scheme> "://" <server-name> ":" <server-port> <extra-path>

ここで、<scheme> は元々のクライアントリクエストにおける scheme、<extra-path> は ";", "=", "?" 予約文字を含む、PATH_INFO を URI エンコードしたものである。 例えば、以下のようなリクエスト:

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

は、PATH_INFO の値は以下のようなものになる。

 /this.is.the.path;info

内部的 URI は、schema, サーバの位置、URL エンコードされた PATH_INFO から構築される:

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

そして、これがサーバの文書レポジトリ内の位置に変換され、おそらく以下のようなファイルシステムパスとなる:

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

PATH_TRANSLATED の値は、この変換の結果である。

この値は、妥当なレポジトリ内の場所へとマッピングされるかどうかとは無関係にこの方法によって得られる。 サーバは、使用しているレポジトリが名前の大文字・小文字を区別しないものでなければ、extra-path 要素の大文字・小文字を保持しなければならない。 レポジトリが文書名に関してのみ大文字・小文字を区別する、保持する、あるいは問わないというものであれば、サーバは変換を通じて元の要素の大文字・小文字を保持する必要はない。

サーバが PATH_TRANSLATED を得るために使用する変換アルゴリズムは、実装的に規定される; この変数を使用する CGI スクリプトは、可搬性{portability} が制限される恐れがある。

サーバは、リクエスト URI が path-info 構成要素を含む場合、この外部変数を設定すべきである。 PATH_INFO が NULL の場合、PATH_TRANSLATED 変数も NULL に設定 (あるいは未設定と) しなければならない

PATH_TRANSLATED は、「もし、PATH_INFO がサーバ上の文書へのパスを表しているのだとしたら、おそらくこのパスになるであろう」という予測の元に決定されます。 実際に、この場所にファイルがあるかどうかは問いませんし、そもそもその場所を決めるためのアルゴリズムは実装依存です。

QUERY_STRING

QUERY_STRING 変数は、URL エンコードされた検索用、あるいはパラメータ用文字列を含む; これは CGI スクリプトによって返される文書に作用する、あるいは変更するための情報を提供する。

検索文字列のための URL 構文は RFC 2396 [2] の section 3 にて記述される。 QUERY_STRING 値は、大文字・小文字を区別する。

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

問い合わせ文字列を解析し、デコードする際、その解析の詳細や予約文字、非 US-ASCII 文字についてのサポートは、状況に依存する。 例えば、HTML 文書からのフォーム提出 [18] は、application/x-www-form-urlencoded 符号化を使用し、その場合は文字 "+", "&", "=" が予約されており、非 US-ASCII 文字については ISO 8859-1 エンコーディングが使用されているであろう。

QUERY_STRING 値は、Script-URI の query-string 部を提供する。 (section 3.3 参照)。

サーバはこの変数を設定しなければならない; もし Script-URI が問い合わせ構成要素を含んでいなければ、QUERY_STRING は空文字列 ("") として定義されなければならない

URI 中の "?" 以下の文字列です。 但し、この中に非 ASCII コードが使用されていた場合の文字セットの解釈は、システム依存となります。

この変数は常にセットされます。 "?" 以下が無い場合でも、「空文字列」として定義されます。

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 [15] にて記述されている。

リクエストを作成するリモートホストの IP アドレスです。

REMOTE_HOST

REMOTE_HOST 変数は、もしそれが存在すれば、そのリクエストをサーバに送信しているクライアントの完全修飾ドメイン名を、なければ NULL を含む。 完全修飾ドメイン名は、RFC 1034 [17] の section 3.5 や RFC 1123 [12] の section 2.1 に記述されるような形式を取る。 ドメイン名は、大文字・小文字を区別しない。

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

サーバはこの変数を設定すべきである。 もしパフォーマンスやその他の理由のために hostname が利用できなければ、サーバは REMOTE_ADDR の値で代用してもよい

リクエストを作成するリモートホスト名です。 大文字・小文字は区別されません。

サーバはこの情報を設定すべきですが、逆引き (IP アドレスからホスト名を調べる事) は負荷がかかるためにこれを禁じているサーバもあります。 その場合は、REMOTE_ADDR をセットするか、あるいは何もセットしないままにしておくべきです。

REMOTE_IDENT

REMOTE_IDENT 変数は、遠隔エージェントに対する RFC 1413 [20] リクエストによる接続について報告される識別情報があれば、これを提供するために使用する事ができる。 サーバは、この機能を提供しない、効率上の理由のためデータを要求しない、あるいは利用可能な識別データを返さない、のいずれかを選択する事ができる。

 REMOTE_IDENT = *TEXT

返されるデータは認証目的のために使用する事ができるが、その信頼度は最小限であるべきである。

クライアントが RFC 1413 互換デーモンをサポートしている場合、サーバがこの変数を利用可能にしていれば、この変数にはサーバが得たクライアントのユーザ名がセットされるでしょう。

但し、これを利用可能にした場合、全てのリクエストに対してルックアップが行なわれますので、深刻な遅延を起こすかもしれない事に注意して下さい。 また、この変数の使用はログ取得のみに制限されるべきであり、またその信頼性も決して高くはないという事に注意して下さい。 ここで得られた情報は簡単なユーザ追跡に使う以外は、まったく信頼するべきではありません。

REMOTE_USER

REMOTE_USER 変数は、ユーザ認証の一部としてクライアントによって供給されるユーザ識別用文字列を提供する。

 REMOTE_USER = *TEXT

クライアントリクエストが、HTTP 認証 [5] を要求した (例えば AUTH_TYPE 外部変数が "Basic" や "Digest" と設定されている) 場合は、REMOTE_USER 外部変数の値は供給されるユーザ ID に設定されなければならない

サーバが HTTP 認証をサポートしている場合のユーザ名となります。 例えば、Authorization: aC1oYXNoOkFCQzEyeHl6 というヘッダが送られたとしたら、REMOTE_USER には h-hash という値が入るでしょう。

REQUEST_METHOD

外部変数 REQUEST_METHOD は、section 4.3 にて記述されるように、リクエストを処理するためにスクリプトによって使用されるべきメソッドが設定されなければならない

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

メソッドは、大文字・小文字を区別する。 HTTP メソッドは、HTTP/1.0 仕様書 [1] section 5.1.1 や HTTP/1.1 仕様書 [4] section 5.1.1 にて記述されている。

このリクエストで使用されたリクエストメソッドがセットされます。

SCRIPT_NAME

SCRIPT_NAME 変数は、(スクリプトの出力ではなく) CGI スクリプトを識別できる (URL エンコードされていない) URL パスが設定されなければならない。 構文は PATH_INFO (section 4.1.5) と同じである。

 SCRIPT_NAME = "" | ( "/" path )

先頭の "/" はパスの一部ではない。 パスが NULL ならばこれは省略可能である; しかし、その場合でさえも、変数は設定されなければならない

SCRIPT_NAME 文字列は、実装的に規定されるある種の方法に由来する Script-URI のパス構成要素の先頭部分を形成する。 PATH_INFO 部分 (section 4.1.5 参照) は、SCRIPT_NAME 値には含まれない。

URI のパスにあたる部分がここに入ります。 主に、スクリプトが自己を参照する際に使用します。

SERVER_NAME

SERVER_NAME 変数は、クライアントリクエストが向けられるサーバのホスト名が設定されなければならない。 この値は、大文字・小文字を区別しないホスト名、あるいはネットワークアドレスである。 これは、Script-URI のホスト部分を形成する。

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

複数の HTTP バーチャルホストが同じ IP 番地を共有していれば、設置されるサーバはこの変数の値を複数持ち得る。 その場合、サーバは正しいバーチャルホストを選択するためにリクエストの Host ヘッダフィールドの内容を使用する。

URI のホスト名 (あるいはアドレス) にあたる部分がここに入ります。 主に、スクリプトが自己を参照する際に使用します。

SERVER_PORT

SERVER_PORT 変数は、クライアントから受信されたこのリクエストにおける TCP/IP ポート番号が設定されなければならない。 この値は、Script-URI のポート部分に使用される。

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

この変数は、たとえそのポートがその scheme についての既定のポートであり、URI から省略できる場合であっても、設定されなければならない事に注意せよ。

クライアントからサーバに向けて、そのリクエストが送信された時のポート番号です。 これは URI 中に含まれる値か、あるいはプロトコル既定の値 (HTTP の場合は 80) となります。

SERVER_PROTOCOL

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

ここで、'protocol' は、サーバとスクリプトの間で受け渡しされる情報 ('プロトコル的に規定される' 特性) のいくつかの構文を定義する。 これは、大文字・小文字を区別せず、通常は大文字で表される。 プロトコルは、スクリプト URI の scheme 部分とは異なり、サーバと通信するためにクライアントによって使用されるアクセスメカニズム全体を定義する。 例えば、"HTTP" のプロトコルをもってスクリプトに到達するリクエストは、"https" scheme を使用しているかもしれない。

サーバが使用できる SERVER_PROTOCOL についてよく知られている値はの1つは "INCLUDED" で、 これは 現在の文書がクライアントのリクエストの直接の対象ではなく、複合文書の一部として含まれているという事を示している。 スクリプトは、これを HTTP/1.0 リクエストとして扱うべきである。

このリクエストで使用されたプロトコルがセットされます。 通常は、リクエストで使用された HTTP バージョンが入ると考えてよいでしょう。

SERVER_SOFTWARE

SERVER_SOFTWARE 外部変数は、CGI リクエストを作成している (またゲートウェイを運用している) サーバソフトウェアの名前とバージョンの情報が設定されなければならない。 これは、クライアントに報告されるサーバの説明がある場合、それと同じであるべきである

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

サーバで使用されているソフトウェアの名前です。 多くの場合、Server ヘッダと同じ値になります。

プロトコル特有な外部変数

サーバは、そのリクエストについてのプロトコルや scheme に特有の外部変数を設定すべきである。 プロトコル特有の変数の解釈は、SERVER_PROTOCOL 内のプロトコルバージョンに依存する。 サーバは、その scheme がそのプロトコルと同じでなければ、scheme の名前を持つ外部変数に NULL でない値を設定してもよい。 このような変数の有無は、そのリクエストでどの scheme が使用されているかをスクリプトに示す。

サーバは、標準の外部変数以外にも、リクエストに応じてサーバ独自の外部変数をセットする事が可能です。 特に URI 中の scheme と、使用されているプロトコルが一致しない場合は、scheme の名前を持つ外部変数を設定する事ができます。 例えば、https という scheme を使用した場合、あるサーバソフトウェアでは HTTPSON という値が設定されます。

使用されているプロトコルが HTTP の場合、"HTTP_" で始まる名前を持つ外部変数はクライアントのリクエストヘッダフィールドから読み込んだ値を含む。 外部変数名は、HTTP ヘッダフィールド名が大文字に変換され、存在する全ての "-" を "_" に置換し、先頭に "HTTP_" が与えられたものとなる。 ヘッダデータは、クライアントによって送られる通りに表す事もできるし、あるいはその意味を変えないように書き換える事もできる。 同じ名前のフィールド名を持つ複数のヘッダフィールドを受信した場合、サーバは同じ意味を持つ単一の値に書き換えなければならない。 同様に、複数行にわたるヘッダフィールドは、単一の行に結合されなければならない。 サーバは、必要であれば、データの表現 (例えば、文字セット) を CGI 外部変数にとって適切なように変更しなければならない

サーバは、それが受信した全てのヘッダフィールドについての外部変数を作成する必要はない。 特に、例えば 'Authorization' のような、認証情報を転送する任意のヘッダフィールドや、あるいは、例えば 'Content-Length' や 'Content-Type' のような、他の変数からスクリプトにて利用可能なものは取り除くすべきである。 サーバは、'Connection' のような、クライアント側との通信上の点にのみ関係するようなヘッダフィールドは取り除いてもよい

使用されるプロトコルが通常の HTTP (すなわち scheme が http) である場合、クライアントから受信されたヘッダ名の前に HTTP_ が追加された外部変数が作成されます。 この時、ヘッダ名中の全ての -_ に変換されます。 これらの値の解釈は、HTTP 仕様書中の HTTP ヘッダ にそれぞれ準じると考えられるべきでしょう。

但し、セキュリティの観点から Authorization のような値を外部変数として渡す事は避けるべきです。 また、Content-TypeContent-Length のような、サーバによって既に処理されたヘッダや、その他のものでもサーバが望む任意のヘッダは全て取り除く事ができます。

NPH スクリプト

NPH スクリプトについては、RFC 3875 の section 5.1 をご覧下さい。

サーバは、NPH (Non-Parsed Header) スクリプトをサポートする事ができる; これらは、サーバが負うレスポンス処理についての全ての責任を渡されたスクリプトである。

この仕様書では、NPH スクリプトがその出力データのみに基づいて識別されるようなメカニズムは提供しない。 慣例により、任意のスクリプトは一つの種類 (NPH か CGI) の出力のみしか提供できないので、そのスクリプト自身は 'NPH スクリプト' と表現される。 NPH をサポートするサーバは、おそらくスクリプトの名前や位置に基づいた、NPH スクリプトを識別するための実装的に規定されるメカニズムを提供しなければならない

通常、CGI では、スクリプトの処理結果はサーバに渡され、HTTP レスポンスとしての体裁を整えられた後、クライアントに返されます。 しかし、NPH スクリプトを使用すると、スクリプトの出力結果は、サーバに介されずに直接クライアントに返されるようになります。 これによって、例えば「HTTP サーバを用いて、HTTP 以外のプロトコルを利用したレスポンスを返す」等という事ができるようになります。

サーバは、スクリプトの出力がクライアントに修正されずに送られるという事を保証しなければならない。 これは、スクリプトがヘッダフィールドにおいて正しい文字セット (HTTP においては US-ASCII [9] や ISO 8859-1 [10]) を使用する事を要求しているのであるという事に注意せよ。 サーバは、内部的に最小限のバッファリングとなるように、また転送部分ではバッファリングのないように、スクリプトの出力がクライアントに直接送られるという事を保証しようとすべきである

スクリプトが non-parsed header 出力、すなわちその本来のプロトコル内でクライアントによって解釈されるものを返す場合、スクリプトはそのプロトコルに関連する全てのセキュリティについての考察について取り組まなければならない。

NPH スクリプトでは、サーバはスクリプトの出力に干渉しません。 従って、スクリプトの作者はそのレスポンスで扱うプロトコルについてはあらゆる危険性について考慮しなければなりません。

なお、一部では「NPH スクリプトを使用すると、レスポンスを返す速度が早くなる」等と言われる事がありますが、レスポンスの返答速度は、一般にサーバ内処理速度よりも、通信路上の状況 (すなわち、TCP 以下の層) に依存する事が多く、このような事は言い切れません。

CGI スクリプトとセキュリティ

CGI スクリプトは、しばしばセキュリティホールを産み出す原因となります。 CGI に対応したサーバを開発する人はもちろん、CGI スクリプトの作者も、サーバ開発者と同じくらいの注意を払いながら作成をしなければなりません。 十分に注意を払っていないスクリプトが利用された場合、システム内を探査されたり、サーバログを読まれたり、パスワードファイルをメール送信されたり等、様々に悪用されてしまう可能性があります。

スクリプト作成者が注意すべき点はいくつかありますが、以下については特に重要ですので、絶対にこのような事は行わないで下さい。

ユーザからの入力を信用しない

外部に公開されるスクリプトを作成する場合、ユーザから入力される情報は信用してはいけません。 RFC 3875 の section 8.2 では、CGI スクリプト作成時に推奨される事が記述されています。

スクリプトが PATH_INFO データを処理するつもりでない場合、PATH_INFO が NULL でなければ、404 Not Found をもってそのリクエストを拒否すべきである。

フォームの出力が処理される場合は、CONTENT_TYPE が "application/x-www-form-urlencoded" [18] か "multipart/form-data" [16] である事をチェックせよ。 CONTENT_TYPE が空白の場合、そのプロトコルによってサポートされていれば、スクリプトは 415 'Unsupported Media Type' エラーをもってそのリクエストを拒否できる。

スクリプトは、PATH_INFO, PATH_TRANSLATED, SCRIPT_NAME を解析する時、空のパス要素 ("//") や特別なパス要素 ("." や "..") に注意すべきである。 OS システム呼出しにて使用する前にパスからこれらを取り除くか、404 'Not Found' をもってそのリクエストを拒否すべきである。

CGI スクリプトによってアクセスされるディレクトリやファイルは、限定されなければなりません。 この点に注意しないと、CGI に十分な権限が与えられている場合、/bin/passwd 等のサーバ上の機密ファイルにアクセスされ、重大な問題を引き起こす可能性があります。

CGI スクリプトの中で、ユーザからの入力に従ってファイルにアクセスする場合は、必ずその整合性をチェックしなければなりません。 open FILE, "data/$CGI{name}.dat" のような事をしてはいけません。

サーバの情報を与えすぎない

現在、Web サーバを走らせている OS の殆どは UNIX 互換のものと言っていいと思います。 RFC 3875 の section 7.2 では、UNIX 互換 OS における CGI のシステム仕様について記述しています。

UNIX 互換の OS については、以下が定義される:

外部変数
外部変数は、同一に名付けられた環境変数にてスクリプトに渡される。 これらへは、C ライブラリルーチン getenv() やそれに関する変数によってアクセスされる。

一般に、環境変数とは「OSのシェルなどに設定されている、システムの属性を記録している変数」のことを指します。 UNIX互換OSでは、その環境変数としてCGIで使用される外部変数を設定します。 まれに、「HTTP_で始まるCGI外部変数」のことを「環境変数」と呼んでいる人がいますが、これは正確ではありません。

また、「HTTP_で始まるCGI外部変数」をユーザに見せたいがために、サーバOSの環境変数のすべてを開示してしまっているCGIスクリプトがありますが、これはサーバのセキュリティを下げる非常に危険な行為です。 例えば、DOCUMENT_ROOTという環境変数は、サーバ上の配置構成を知らしめる一端を担いますし、PATHという環境変数によって、実行可能なプログラムのヒントを与える可能性があります。 従って、「HTTP_で始まるCGI外部変数」を見せたい場合は、それ以外の環境変数は見えないようにしなければなりません

(※)CGIで使用される外部変数(環境変数)を表示するためのCGIスクリプトのサンプルについては、env_with_cgi_pm.cgiをご覧ください。

機密性の高い情報を漏洩させない

基本的に、SSL/TLS 等を利用していない通信は全て傍受されているとみなしてもよいのですが、それは極端としても、第三者がパスワードや個人情報等の入力情報を簡単に入手できないように、スクリプトの作者は注意しなければなりません。

リクエスト内の機密データは POST リクエストの一部として message-body 内に置かれるべきであり、URI やメッセージヘッダ内に置かれるべきではない。 システムによっては、スクリプトに外部変数を渡す環境が他のスクリプトやユーザから見えるかもしれない。 加えて、多くの既存のサーバやプロクシ、クライアントは、第三者から見られる所に URI を永続的に記録しているであろう。

クライアントから情報を受け取る際に、もし GET メソッドを利用してしまうと、仕様上そのメッセージボディは URI 中に露出される事になってしまいます。 そのような場合、サーバや中継するプロクシ上のログにその URI が記録される事になります。 サーバやプロクシの管理者は、アクセスログを個人情報に準じるものとみなし、第三者が閲覧可能な場所に放置しておくべきはありませんが、仮にそれが見られた場合、そこから個人情報が漏洩する可能性があります。 同様に、メッセージヘッダや Cookies によってそれらを扱う事もすべきではありません (Cookies は、基本的に HTTP ヘッダの一種です)。 従って、機密的な情報は、全て POST メソッドを利用し、簡単にログに残らないようにしなければなりません

(※) 「こうすれば、ログに残らない」と言っているわけではありません。 「一般に、POST の message-body はログにとらないから、そうしておけば危険性が減る」と言っているに過ぎません。 よりリスクを下げたければ、SSL/TLS 等を利用した暗号化通信を行わなければなりません。

参照文献

Webリソース
The Common Gateway Interface (CGI) Version 1.1
Hypertext Transfer Protocol -- HTTP/1.1
ナウでヤングなレンタルサーバー!ロリポップ!

Copyright © 1999-2010 H-Hash, All Rights Reserved. Valid HTML 4.01 Strict 正当なCSSです!