CGIの仕組みとcgi-binディレクトリの役割

Web黎明期に登場したCGI(Common Gateway Interface)は、HTTPサーバーが外部プログラムと連携して動的コンテンツを生成するための規約である。cgi-binという特殊ディレクトリは、この規約を実現するための舞台として長年使われてきた。以下では、CGIの動作フローから実装例、そして現代の代替技術までを俯瞰する。

CGIが動くまでの流れ

  1. ブラウザがURL(例: /cgi-bin/weather.cgi?city=tokyo)へリクエスト
  2. サーバーがパスや拡張子からCGI呼び出しを判定
  3. 新規プロセスを起動し、環境変数と標準入力でHTTP情報を渡す
  4. プログラムがビジネスロジックを実行し、標準出力へHTTPレスポンスを書き出す
  5. サーバーがその出力をそのままブラウザへ返却

環境変数早見表

変数名内容例
REQUEST_METHODGET, POST, PUT …
QUERY_STRINGcity=tokyo&day=3
CONTENT_LENGTH17(POSTボディのバイト数)
HTTP_USER_AGENTMozilla/5.0 …

最小構成のCGIスクリプト(Bash版)

#!/bin/bash
echo "Content-Type: text/plain"
echo
echo "サーバー時刻: $(date)"
echo "リクエストURI: $REQUEST_URI"

上記を /usr/lib/cgi-bin/time.sh として配置し、chmod 755 しておくと、http://example.com/cgi-bin/time.sh で動作を確認できる。

フォームデータを扱うPythonスクリプト

#!/usr/bin/env python3
import cgi, html

form = cgi.FieldStorage()
nickname = html.escape(form.getfirst("nick", "名無し"))
message  = html.escape(form.getfirst("msg", ""))

print("""Content-Type: text/html; charset=utf-8


<title>チャット風CGI</title>
<h1>{0} さんの投稿</h1>
<p>{1}</p>
<form method="post">
  名前: <input name="nick" value="{0}"><br>
  本文: <textarea name="msg"></textarea><br>
  <button>送信</button>
</form>""".format(nickname, message))

セキュリティチェックリスト

  • 外部コマンド実行はsubprocess.run(..., shell=False)で引数リスト化
  • PATH環境変数を明示的に上書きして意図しないバイナリが呼ばれないようにする
  • アップロードされたファイルは/tmp以外の専用ディレクトリへ保存し、拡張子とMIMEタイプの両方を検証
  • エラー詳細はsyslogへ、ブラウザには汎用メッセージのみ返す

CGIからの進化形

FastCGI
ワーカープロセスを常駐させ、起動オーバーヘッドを削減
WSGI
Pythonにおける標準的なサーバー/アプリ間インターフェース
Servlet
Java EEが提供するマルチスレッド型コンポーネントモデル
PSGI
Perl版WSGIに相当する仕様
Rack
Rubyにおけるミドルウェアスタック標準

例えば、同じPythonコードでもWSGI版に書き換えると

def app(environ, start_response):
    body = b"<h1>Hello WSGI</h1>"
    start_response("200 OK", [("Content-Type", "text/html")])
    return [body]

というシンプルな関数で済み、uWSGIやGunicornなどの高機能サーバーに載せるだけで高速化が図れる。

CGIは現在ではレガシーとされることも多いが、HTTPリクエストをプロセスへマッピングするという基本思想はFastCGIやWSGIにも受け継がれている。その意味で、cgi-binディレクトリはWebアーキテクチャの礎を学ぶための格好の教材である。

タグ: CGI cgi-bin FastCGI WSGI HTTPサーバー

5月16日 07:09 投稿