D2MatE Top
this page
python CGIサーバ開発
CGI (Common Gateway Interface) は、標準的なWebサーバから直接起動することで、外部プログラム(クライアント)との間で情報をやり取りするためのインターフェース/プログラムです。Webサーバが実行を許可しているディレクトリ・条件であれば、
- URLからquery
parameterを解析し、HTTP出力するプログラムを作成する
(例えばCGI.pyというファイル名とする)
- CGI.py を該当ディレクトリ (例えば public_html/cgi-bin)
にコピーし、適切に owner (例えば apache) と permission
(例えば rwxr-x---) を設定する
- https://server/cgi-bin/CGI.py?query_parameters でアクセス
という簡単な手順でデプロイできること、CGIプログラムも一般的なCLIと大きく変わらない
(入力をquery
parameterで取得する、出力にHTMLヘッダーとHTMLタグをつけるように変更)
ため、CLIプログラム開発の経験があれば、プログラム開発も簡単です。
一方で、最近では多くのWebフレームワークが普及し、CGIの使用は推奨されなくなりました。pythonでは、version
3.13においてcgiモジュールが廃止されました。
大きな理由は次の通りです。
詳細
- セキュリティの問題: CGIプログラムは構造を簡単にできるため、逆にセキュリティが甘くなる危険がある
=>
Webフレームワークではセキュリティ対策がされ、コミュニティ・ユーザにより広く検証されている
- 機能の問題:
CGIを開発する際は、基本的にサーバサイドのみで動かすことを想定しています。
一方で、ユーザフレンドリでインタラクティブなWebアプリの開発をするには、クライアントサイドとの連携を独自に実装する必要があります。
=>
Webフレームワークでは、URLによるrequestを対応する関数に接続する機能
(routing)や、サーバサイドとクライアントサイドの連携をはかる機構が埋め込まれています
しかしながら、先端Webフレームワークは標準的なWebサーバでは動かず、pythonのFlask/Dash/FastAPI/DjangoなどはWSGIと呼ばれる仕組みのサーバシステムが必要であり、Javascript/Node.jsではプログラムをdaemon化する必要があり、デプロイとメンテナンスに若干の手間が追加されます;。
CGIであっても、適切にセキュリティ機能を実装することで上記のセキュリティの問題をクリアできますし、その開発過程を知ることにより、Webアプリのセキュリティを上げるのに重要な知識と経験が得られます。ただし、それでも、CGIには以下のようなデメリットもあります。
詳細
- 基本的にページ遷移型のアプリになる。
SPA (Single Page Application)
を開発する際には、クライアント側で必要な機能を全て処理できるHTMLを送出しないといけない
- クライアント側でのインタラクティブなUIも、クライアントサイド
(Javascriptなど) で実装する必要がある
- CGIの場合は、ページ遷移ごとにプログラムが再起動されるため、オーバーヘッドが大きく、毎起動時にセキュリティチェックをする必要があります;。
一方、毎回再起動するためにプログラムが初期化され、リソースリークなどによる致命的エラーが出にくくなります
(逆に言えば、バグが見つけづらくなる)。
オーバーヘッドの問題については、FastCGI化してデプロイすることで解決できます
本ページでは、Flask、Javascript的な記法が可能で、ユーザ認証、ファイルアクセス制限などのsecurity機能を実装したフレームワークを作ります。
また、JSONの受け渡しによるREST
APIサーバとして働く python CGIプログラムの開発例を掲載します。
セキュリティを確保するための重要項目
Webアプリに必要な対策です。
- アクセス制限:
ユーザ認証、IPアドレス制限、ディレクトリ/ファイル制限、動作制限
ユーザ認証:
アカウント名とパスワード、場合によっては多重認証による認証
IPアドレス制限:
クライアントのIPアドレスを取得して確認
ディレクトリ/ファイル制限:
requestされたパスが許容されているかどうかの確認と、応答を返す最終段階での再確認
動作制限:
requestに応答する関数で必ず認証・制限確認を行う
- サニタイズ (消毒、インジェクション対策): 入力データの無害化。HTMLタグの無効化、不正なパスの無効化、危険なパス
(../など) の無効化
- セキュリティヘッダー: HTTPヘッダーを設定して、クリックジャッキングやXSS攻撃を防ぐ。
例えば、Content-Security-Policy
ヘッダーや X-Frame-Options ヘッダーを使用する。
- ロギングとモニタリング: 異常なアクセスや不正なアクティビティを検出するために、ログを収集、重要な関数へのアクセスはリアルタイムモニタリング
(管理者へのメール送出など) する。
以下はサーバ側の対策です。
- サーバによるアクセス制限:
HTTPでアクセスする必要があるファイルと、不要なファイルの分離。
HTTPでアクセスさせてはいけないファイルは、DocumentRoot (public_htmlなど)
とは別のディレクトリツリーに置き、適切なpermissionをかけます
HTTPでアクセスするファイルの場合も、必要に応じて、.htaccess/.htpasswdやWebサーバの設定によりアクセス制限を掛けます
- HTTPSの導入: 通信内容を暗号化するため、SSL/TLS証明書を使用してHTTPSを導入し、データの盗聴を防ぎます。
セキュリティ対策HTTPヘッダー
ページのリソースの読み込みやスクリプトの実行を制御します
Content-Security-Policy (CSP):
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; object-src 'none'; frame-ancestors 'none';
- default-src 'self':
デフォルトのリソースソースを自身のドメインに制限する。
- script-src 'self' https://trusted.cdn.com:
スクリプトの読み込みを自身のドメインと信頼できるCDNからのみ許可する。
- object-src 'none':
オブジェクト、プラグインをすべて禁止する。
- frame-ancestors 'none':
他のページがこのページをフレーム内に埋め込むことを禁止する。
X-Frame-Options
クリックジャッキング攻撃を防ぐために、ページが他のサイトでフレームに埋め込まれるのを制御する
- X-Frame-Options: DENY
他のすべてのページからのフレーム内埋め込みを禁止する。
- X-Frame-Options: SAMEORIGIN
同じオリジン (ドメイン)
内のページのみフレーム内に埋め込みを許可する。
- X-Frame-Options: ALLOW-FROM https://trusted.domain.com
指定された信頼できるドメインからのフレーム内埋め込みを許可する
(一部のブラウザでのみサポート)。
Webアプリに必要な機能: requestの取得と応答
現在ではREST APIの枠組みを使うことが一般的です
REST APIの基本的な特徴:
- ステートレス:
各リクエストは、他のリクエストと独立して処理される。
- クライアント・サーバアーキテクチャ:
クライアントとサーバは独立して開発・運用される。
- リソースベース: URIを使ってリソースを識別し、HTTPメソッド(GET、POST、PUT、DELETEなど)を利用して操作する。
以下のプログラム例 API_test.py では、一般的なJSONを用いたAPIを実装します。
生成AI (MS365 Copilot) による Flask-like CGIフレームワークの開発例
- tkCGIApplication_Copilot..docx
Copilotの画面からコピペしているので、入力プロンプトのプログラムコードの改行が削除されています。
生成AIで「以下はpythonプログラムですが改行が削除されています。改行とインデントを入れて整形してください」
で修復できます
(関数間に外部コードがある場合などは、インデントを間違ると思います)。
以下で掲載しているtkCGIApplication.pyは、この例からかなりの修正を行っています
Flask-like CGI Framework tkCGIApplication.py
Javascript-like HTML library
tkHTMLDocument.py
以下の機能を実装しています。
Flask-like機能
- ルーティング:
@app.route(entry_point)
route関数が文字列あるいはtkHTMLElementを返すと、ブラウザに表示されます
- ロギング:
app.setup_logging()
, @app.logging()
- 実行:
app.run()
関数
・ カスタムエラーハンドラー
注: Flaskのapp.run()はHTTPサーバを起動しますが、tkCGIApplication.pyではCGIプログラムのroutingを実行します。
- print()関数のredirect
- app.init_html()を実行
- 初期設定、query parameterの読み込み等
- route関数呼び出し
- app.end_html()処理を行い終了
- 出力文字コードの固定: POSTあるいはGETの場合は、
sys.stdout.reconfigure(encoding='utf-8')
を実行
- template機能:
app.render_template()
- 開発用httpサーバ: 以下のプログラムで起動
その他機能
- query parameterの取得:
app.get_params()
POST, GET, 起動時引数に対応
サニタイズ: app.sanitize()
, app.desanitize()
- ユーザ認証 (アカウント名、パスワード、IPアドレス):
@app.validate_account
- ディレクトリ、ファイル名でのアクセス制限
- セキュリティ対策HTTPヘッダー
tkHTMLApplicationのコンストラクタのsecury_level
で制御。
デフォルトは5で、同一オリジンのリソース (CGI, CSSその他)
しか使えない、iframeで取り込めない設定。
- アクセス通知メール送信機能:
@app.email()
- print()関数のカスタマイズ
- HTML初期化・終了処理:
app.init_html()
、app.end_html()
- Javascript-like HTML出力関数: tkHTMLDocment.pyで実装
ファイル
API_test.py動作試験 プログラム
以下は古い参考プログラム
CGIアプリの開発手順
デバッグ情報の見方
- pythonのTraceback: サーバコンソール
- プログラムからのlogginメッセージ: log ファイル
- print()出力: 実行コンソールあるいはWebブラウザ
ただし、事前にapp.init_html()を実行し、<body>タグまで出力しておく必要がある。
import cgitb
cgitb.enable()
を実行すれば、pythonのTracebackメッセージをブラウザに出力できる。
しかし、cgi、cgitbモジュールはpython3.13で廃止されたため、代替としてtracebackを
使うことができる。
import traceback
def handle_exception(exc_type, exc_value, exc_tb):
"""例外をHTML形式で出力"""
print("Content-Type: text/html\n")
print("<html><body>")
print("<h2>Error:</h2>")
print("<pre>")
traceback.print_exception(exc_type, exc_value, exc_tb, file=sys.stdout)
print("</pre>")
print("</body></html>")
# エラーハンドラを設定
sys.excepthook = handle_exception
開発・デバッグ手順
- プログラム hogehoge.py を作成
- CLI として hogehoge.py を起動してデバッグ
> python hogehoge.py
- hogehoge.py に query parameter
を起動時引数として渡して動作検証
> python hogehoge.py action=getFiles user=user password=pw
- 疑似POST動作で動作検証
環境変数を設定して疑似POST関数を作って hogehoge.py
を起動
(API_test.py動作試験 プログラム (API_teest.pyを CLI
として起動) を参照)
> python query2cli.py
-
>
python http_server_me.py
HTTPサーバを起動
- hogehoge.pyをCGIとして呼び出すCLIプログラムで動作検証
(API_test.py動作試験 プログラム (API_teest.pyを CGI
として利用) を参照)
> python query2cgi.py
- CGIはプログラム起動時のオーバヘッドが大きいため、パフォーマンスが必要な場合は
FastCGI を使ってWSGIアプリ化する