Perl初心者の部屋  目次  イントロ  モジュール  CGIとWWWサーバ  セキュリティ
                       .d8888b.    .d8888b.   8888888
                      d88P  Y88b  d88P  Y88b    888
                      888    888  888    888    888
                      888         888           888
                      888         888  88888    888
                      888    888  888    888    888
                      Y88b  d88P  Y88b  d88P    888
                       "Y8888P"    "Y8888P88  8888888

                       PCP => Perl CGI Program (ming)

                                Version 1.0

Shishir Gundavaram <shishir@ora.com>
Tom Christiansen <tchrist@perl.com>

4.0 - 特定のプログラミングの質問

Q4.1: 利用者に、あるフォームに記入してもらって、私宛にメールしてもらいたいのです。  どうしたらこれができるでしょうか?  その方法を示す例が何かありますか?

Q4.2: フォームメールスクリプトは複雑にみえます。 どうしてmailto:URLを使って、利用者が書き込んでくれた情報を私宛に単に メールするだけではいけないのでしょうか?

Q4.3: MacやMSーDOS、Windows、NTのような非UNIXプラットホームからPCPを行うには どうすればよいでしょうか?  私のPCPはこれら全ての環境の間で移植できるのでしょうか?  それは透過的にできるのでしょうか ? 私はUNIXサーバにアカウントがあるのですが、Windows/Macシステムで仕事をして います。私自身のシステムでCGIスクリプトをテストするにはどうすれば いいのでしょうか?

Q4.4: PCPの中では、STDERRとSTDINとSTDOUTはどこに接続されるのですか?

Q4.5: アクセスカウンタスクリプトを書くにはどうしたらいいですか?

Q4.6: Perlの置換を使って文書からHTMLタグを全部取り去るにはどうしたら いいでしょうか?

Q4.7: どんなユーザ/ホスト/ブラウザが私のプログラムを呼び出しているのか 表示するにはどうしたらいいですか?

Q4.8: 人々は私のPCPが読めますか?  そうならば、その人達が私のコードがどのように機能しているか知ってしまうという セキュリティの問題はありませんか?  どうやってそれを隠せばよいでしょうか?

Q4.9: 全Perlライブラリを私のhtdocsディレクトリにコピーしなければならない のでしょうか?

Q4.10: どうして人々にパスワードや社会保証番号やクレジットカード番号を入力させる べきではないのでしょうか? TYPE="password"とはそのためにあるのでは ないのですか?

Q4.11: ネットスケープ対それ以外の世界用に別々のページを生成するには どうしたらいいでしょうか?

Q4.12: どうしてsystem ()の出力が正しい順序で出てこないのでしょうか?

Q4.13: ネットスケープはJavaをサポートしていると聞いています。 もうPerlの代わりにJavaを使わなければならないということなのですか?  そうすべきなのでしょうか?

Q4.14: どうしたら私の環境変数にアクセスできますか?  時々異なっているのはどうしてですか?

Q4.15: 私の出力がむちゃくちゃになっているのはどうしてでしょうか?  ("if b < a" のようなものがめちゃめちゃです)

Q4.16: 私のPCPはコマンドラインから実行するときは動くのですが、 ブラウザからだと動きません。どうしてでしょうか?

Q4.17: 私のPCPはうまく動いているのですが、出力ファイルに書き込めません。 どうしてでしょうか?

Q4.18: 状態を維持する、あるいはいくつかのエントリポイントのあるフォームを作るには どうしたらいいでしょうか?

Q4.19: webブラウザから走らせずにPCPをデバッグするにはどうしたらいいでしょうか?

Q4.20: <FORM>タグを使わないでPCPをcallするには どうしたらいいのでしょうか?

Q4.21: 何も記入しないでフォームをcallするのをやめてもらうにはどうしたら いいでしょうか? 人々はなぜこれをし続けるのでしょうか?

Q4.22: server response codesとは何ですか? またどんな意味があるのですか?

Q4.23: なぜ

print "Location: http://host/page.html\n"
はうまく機能しないのでしょうか?  1回目だけちゃんと動き、その後は間違ってリダイレクトされるのは どうしてでしょうか?

Q4.24: 私のHTMLページの一番下の所に自動的に

"Last updated: ..."
行を加えたいのですが、どうすればいいのでしょうか?  あるいは、SSIページでしたこれはできないのでしょうか?  CGIスクリプトの日付はどうやったらわかりますか?

Q4.25: 簡単なタスクだけどPCPだとひどく複雑になってしまうのでシェルの方が いいというのはどんな時でしょうか?  難しい事に対して十分力を発揮できない場合はどんな時ですか?  Cについてはどうですか?


4.0 - 特定のプログラミングの質問


Q4.1: 利用者に、あるフォームに記入してもらって、私宛にメールしてもらいたいのです。  どうしたらこれができるでしょうか?  その方法を示す例が何かありますか?

実のところ、これを行うのは非常に簡単なプロセスです。 あなたのCGIスクリプトは2つのタスクを遂行できなくてはなりません。:

あなたがCGI::* モジュールを使っていると仮定しましょう。 以下にsendmailを取り扱う方法を示します。:

$cgi_form = new CGI::Form;

$from     = $cgi_form->param('from');
$name     = $cgi_form->param('name');
$to       = $cgi_form->param('to');
$subject  = $cgi_form->param('subject');
$message  = $cgi_form->param('message');

open (SENDMAIL, "| /usr/bin/sendmail -t -n");
print SENDMAIL <<End_of_Mail;
From: $from <$name>
To: $to
Reply-To: $from
Subject: $subject

$message
End_of_Mail
あなたが留意すべき点は一つで、"Reply-To:"ヘッダを忘れないことです。 サーバはユーザ"nobody"で走っていますから、メールヘッダはグシャグシャに なっているかもしれません(特に人々がそれに返事をしようとする時は)。 "Reply-To:"フィールドがそれを解決します。

次のようなフォーマットでメールを使用するメールゲートウェイが ずいぶんあちこちにあります。:


open (MAIL, "| mail -s 'Subject' $to");

                                   ^
                                   |
                                   +-- セキュリティホールの可能性!!!!

シェルのメタキャラクタ用の$to変数をチェックしなければ、 ひどい頭痛に悩まされることになるでしょう! 例えば、ある悪意のある利用者が次のように入力した場合、:

; rm -fr / ;

あなたは自分の両手に大きな問題を抱えることとなるでしょう。


Q4.2: フォームメールスクリプトは複雑にみえます。 どうしてmailto:URLを使って、利用者が書き込んでくれた情報を私宛に単に メールするだけではいけないのでしょうか?

残念なことに、mailto:が全てのブラウザでサポートされているわけではないのです。 もしこれがあなたの文書にあるのでしたら、それは制限要素となり、 この機能をサポートしていないブラウザを使う人々は、あなたにメールを送る ことができなくなってしまうでしょう。


Q4.3: MacやMSーDOS、Windows、NTのような非UNIXプラットホームからPCPを行うには どうすればよいでしょうか?  私のPCPはこれら全ての環境の間で移植できるのでしょうか?  それは透過的にできるのでしょうか ? 私はUNIXサーバにアカウントがあるのですが、Windows/Macシステムで仕事をして います。私自身のシステムでCGIスクリプトをテストするにはどうすれば いいのでしょうか?

Perlは上記に述べられたプラットホーム全てに移植されてきました。 その結果、あなたのPCPプログラムは問題なく移植できるはずです。 もしあなたがUNIX側にある様々な外部プログラムとのインターフェースを とっている場合は、おそらくうまく移植できないでしょう。 しかし、データを操作したり、ファイルをオープンして読み込んだりしている だけなら、何の問題もないはずです。


Q4.4: PCPの中では、STDERRとSTDINとSTDOUTはどこに接続されるのですか?

CGI環境では、STDERRはサーバのエラーログファイルを指し示します。 デバッグメッセージを出力するようにしておいて、後でログファイルを確認する ようにすれば、これをアドバンテージとすることができます。

STDIN と STDOUTはブラウザを指し示します。実際には、STDINは実はクライアント (あるいはブラウザ)のリクエストと情報を解釈するサーバを指し示しており、 スクリプトへリクエストと情報を送ります。

エラーを捕捉するために、スクリプトの頭の方(有効なHTTPヘッダを出力した後) でSTDERRをSTDOUTに"dupe"(結び付ける)ことができます。

open (STDERR, ">&STDOUT");
これはSTDOUT(例:ブラウザ)へエラーメッセージを全部リダイレクトします。


Q4.5: アクセスカウンタスクリプトを書くにはどうしたらいいですか?

カウンタスクリプトは大変な人気となってきました。 カウンタの背後にあるアイデアは非常に単純です。:

以下は簡単なカウンタスクリプトです。:

#!/usr/local/bin/perl

$counter = "/home/shishir/counter.dat";
print "Content-type: text/plain", "\n\n";

open (FILE, $counter) || die "Cannot read from the counter file.\n";
flock (FILE, 2);
$visitors = <FILE>;
flock (FILE, 8);
close (FILE);

open (FILE, ">" . $counter) || die "Cannot write to counter file.\n";
flock (FILE, 2);
print FILE $visitors;
flock (FILE, 8);
close (FILE);
[訳注:Perl for Win32 build110ではflock()はサポートされていませんので 例外が発生します。]

これでSSI (Server Side Includes)を使って、あなたのHTML文書中に カウンタを表示することができます。

あなたは

<!--#exec cgi="/cgi-bin/counter.pl-->
番目の訪問者です。


Q4.6: Perlの置換を使って文書からHTMLタグを全部取り去るにはどうしたら いいでしょうか?

以下はHTMLタグを取り去る簡単な正規表現です。:

$line =~ s/<(([^ >]|\n)*)>//g;

あるいは、HTMLタグの中のある文字群を"回避させる"こともでき、 それは以下のように表されます。:

$line =~ s/<(([^>]|\n)*)>/<$1>/g;

さらに詳細な情報は、Tomの striphtml programを 参照して下さい。これは、彼の tour of perl5 regexps.にも含まれています。


Q4.7: どんなユーザ/ホスト/ブラウザが私のプログラムを呼び出しているのか 表示するにはどうしたらいいですか?

ユーザのブラウザを決定するには環境変数 HTTP_USER_AGENT を使用すれば よいでしょう。

[ From WWW FAQ ]

5つの重要な環境変数があなたのCGIスクリプトで利用できるので、 エンドユーザを認証する際に助けとなるでしょう。

[ WWW FAQ からの情報の終わり]


Q4.8: 人々は私のPCPが読めますか?  そうならば、その人達が私のコードがどのように機能しているか知ってしまうという セキュリティの問題はありませんか?  どうやってそれを隠せばよいでしょうか?

あなたがサーバの設定をしているのであれば、特定のディレクトリ(例:"cgi-bin" ) にある全てのファイルやある拡張子(例: ".pl"、".tcl"、".sh" その他) を持つファイルがCGIプログラムであり、サーバがこれらのプログラムを 実行することはわかります。 ユーザがスクリプト自体を見る方法はありません。

一方、自分のスクリプトを(例えば、それを文書ルートディレクトリに 置いたりして)一般の人に見せることを許可する場合、 プログラムの中にセキュリティホールがないのであれば、たいていの場合 セキュリティ上問題にはなりません。 そのプログラムがセキュリティホールを含んでおり、ユーザにそれを見せた 場合は、その問題は食い物にされてしまうでしょう。


Q4.9: 全Perlライブラリを私のhtdocsディレクトリにコピーしなければならない のでしょうか?

いいえ、サーバがchrootされた環境で実行されていない限り、CGIスクリプト はサーバと文書のルートディレクトリの外側 (outside the server and documents root directory)のファイルに アクセスすることが可能です。
[訳注:documents root directoryとはいわゆる/cgi-binを指すのではないかと..(^^;)


Q4.10: どうして人々にパスワードや社会保証番号やクレジットカード番号を入力させる べきではないのでしょうか? TYPE="password"とはそのためにあるのでは ないのですか?

いけません! フォームのインターフェースは"password"フィールドを持つことを許していますが、 何か高い機密事項のために使用されるべきものではありません。 その主な理由は、("password"フィールドを含んでいる)すべてのフォームデータは ブラウザから Webサーバに暗号化されたデータとしてではなく、 平文として送られるからです。

セキュアな情報がどうしても欲しいということなら、 Netscape's Commerce Serverのようなセキュアサーバを使用する 必要があります。


Q4.11: ネットスケープ対それ以外の世界用に別々のページを生成するには どうしたらいいでしょうか?

環境変数HTTP_USER_AGENTを使うことで、あなたのスクリプトがネットスケープに アクセスされているかどうかをCGIスクリプトに確認させることができます。 以下はそのサンプルです。:

$browser = $ENV{'HTTP_USER_AGENT'};

if ($browser =~ /Mozilla/) {
    #
    # Netscape
    #
} else {
    #
    # Non Netscape
    #
}

Q4.12: どうしてsystem ()の出力が正しい順序で出てこないのでしょうか?

標準出力がバッファされる方法を処理しなければなりません。 出力を正しい順序で表示するためには、$|変数を使ってバッファリングを オフにする必要があります。:

$| = 1;


Q4.13: ネットスケープはJavaをサポートしていると聞いています。 もうPerlの代わりにJavaを使わなければならないということなのですか?  そうすべきなのでしょうか?

いえいえ。JavaのコンセプトはCGIのそれとは全く異なっています。 CGIはサーバ側での実行に言及し、一方Javaはクライアント側の実行に 言及します。Javaを使うことによって改善することの出来る (アニメーションのような)ことは確かにあります。 しかしながら、サーバ側のアプリケーションを開発するため、 Perlを継続して使うことができます。

さらに詳細な情報については、下記で見ることが出来るでしょう。:


Q4.14: どうしたら私の環境変数にアクセスできますか?  時々異なっているのはどうしてですか?

%ENV連想配列を通じて環境変数にアクセスすることが可能です。 以下は(ソートした)環境変数を全部ダンプアウトする簡単なスクリプトです。:

#!/usr/local/bin/perl

print "Content-type: text/plain", "\n\n";
	
foreach $key (sort keys %ENV) {
    print $key, " = ", $ENV{$key}, "\n";
}

exit (0);

残念なことに、全てのブラウザが同じ環境変数を設定するわけではありません。 たとえば、HTTP_REFERERは全てのブラウザが設定しているわけではありません。


Q4.15: 私の出力がむちゃくちゃになっているのはどうしてでしょうか?  ("if b < a" のようなものがめちゃめちゃです)

もしあなたがHTMLのMIME contentタイプを送っているなら、 "<"、"&" や ">"あるいはブラウザがそれはHTMLだと思うような 特定の文字を"エスケープ"しなければなりません。

次のようなコンストラクトを使って文字をエスケープしなければなりません。

&#ASCII Code;

以下は簡単な例ですが、コマンドラインから実行でき、 非英数字用にASCIIコードを与えるものです。:

#!/usr/local/bin/perl

print "Please enter a string: ";

chop ($string = <STDIN>);
$string =~ s/([^\w\s])/sprintf ("&#%d;", ord ($1))/ge;

print "The escaped string is: $string\n";

exit (0);

Q4.16: 私のPCPはコマンドラインから実行するときは動くのですが、 ブラウザからだと動きません。どうしてでしょうか?

この大半は多分パーミッション(権限)問題によるものです。 おそらくあなたのサーバは"nobody"、"www"あるいは必要最小限の特権を持った プロセスとして実行されています。[訳注:このユーザ名は UNIX HTTPサーバに多い。] 結論としては、ちゃんとした権限[訳注:実行等]がない限り、あなたのスクリプトを 実行することはできないでしょう。


Q4.17: 私のPCPはうまく動いているのですが、出力ファイルに書き込めません。 どうしてでしょうか?

もう一度、これはパーミッション(権限)を処理しなければなりません! サーバはちゃんとした権限がなければ、特定のディレクトリにあるファイルへ の書込はできません。

openコマンドからのエラー状況を確認するのを忘れないことです。:

print "Content-type: text/plain\n\n";

.
.
.

open (FILE, ">" . "/some/dir/some.file") ||
    print "Cannot write to the data file!";  #[訳注:標準エラーはファイル
                       #のため標準出力にprint]
.
.
.

Q4.18: 状態を維持する、あるいはいくつかのエントリポイントのあるフォームを作るには どうしたらいいでしょうか?

複数のエントリポイント間の状態を保つ CGI::MiniSvr モジュールを使えばよいでしょう。

あるいは、クェリー、外部パス名として、または隠しフィールドとして、 お互いにユニークなセッションIDを受け渡す一連の動的文書を生成しても よいでしょう。
[訳注:よくわかりませんでした。(^^;]


Q4.19: webブラウザから走らせずにPCPをデバッグするにはどうしたらいいでしょうか?

CGIスクリプトをデバッグするのは困難です。 マニュアルで環境変数を設定すればサーバをエミュレートすることは できるでしょう。:

setenv HTTP_USER_AGENT "Mozilla/2.0b6"       (csh)

あるいは

export HTTP_USER_AGENT = "Mozilla/2.0b6"     (ksh, bash)
ファイルにデータを置き、それをプログラムにpipeすることによって、 POSTリクエストをエミュレートすることができます。:
cat data.file | some_program.pl

スクリプトをデバッグするのに CGI.pm (あるいは the CGI::* modules) を 使うこともできます。 次のような簡単なスクリプトがあり、受け渡すキーと値のペアを全部 printしているとします。

#!/usr/local/bin/perl5

use CGI;

$cgi = new CGI;

print $cgi->header;
print $cgi->start_html("Simple CGI.pm Program");
print "<H1>Simple CGI.pm Program</H1>\n";
print "<HR >";
print "Here is a list of the values you passed: ";
print $cgi->dump;

exit (0);
このスクリプトは、コマンドラインからか、あるいは標準出力、テキストファイル からなのか、また、GET、POSTあるいはISINDEXとして値を受け渡しているのか 全く気にかけません。デバッグ目的のためには、コマンドラインからいくつか 値を渡してやりましょう。:
% simple.cgi first=shishir last=gundavaram document='CGI\ FAQ'

あるいは

% simple.cgi "first=shishir&last=gundavaram&document='CGI\ FAQ'"
2番目の例の中で、あなたは文字全部を""で囲まなければなりません。 さもないと、シェルが"&"文字を見たとき混乱してしまうでしょう。 さて、以下は標準出力からのデバッグのやり方です。:
% simple.cgi
(標準入力を待つ)
first=shishir
last=gundavaram
document=CGI\ FAQ
^D
もちろん、以下のように、ファイルを使ってフォームデータを蓄積しておき、 入力をリダイレクトすることもできます。:
% simple.cgi < form.data

また、まもなくリリース予定のCGI Lintを利用することもできます。これは 同じ事をやってくれるでしょう。それに、潜在的なセキュリティ問題や open ()におけるエラーや無効なHTTPヘッダのチェックもしてくれます。


Q4.20: <FORM>タグを使わないでPCPをcallするには どうしたらいいのでしょうか?

単にそのCGIプログラムへのURLをオープンするだけで、 それをcallすることができます。:

http://some.machine/cgi-bin/your_program.pl

また、以下のように文書の中にリンクを持つこともできます。

<A HREF="http://some.machine/cgi-bin/your_program.pl">
Click here to access my CGI program</A>


Q4.21: 何も記入しないでフォームをcallするのをやめてもらうにはどうしたら いいでしょうか? 人々はなぜこれをし続けるのでしょうか?

何も記入しないでフォームをcallする理由は、彼らはフォームへのURLをセーブし、 それから再びフォームをcallするのですが、それはただの空のGET (and not a POST or a filled-out GET)となるのです。 The reason they call a form with nothing filled in is that they save the URL to the form, which they then call again, which is just an empty GET (and not a POST or a filled-out GET).

しかしながら、全フィールドからの情報を確認し、そのうちどれかが 空だった場合、"no response"を戻すことが可能です。 以下はその一例です。(あなたのフォームの情報が廉そう配列 %formに 含まれているとします。):

$error = 0;

foreach $value (values %form) {
     $value =~ s/\s//g;
     $error = 1 unless ($value);
}

if ($error) {
    print "Content-type: text/plain\n";
    print "Status: 204 No Response\n\n";
    print "You should only see this message if your browser does";
    print "not support the status code 204\n";
} else {
    #
    # Process Data Here
    #
}

Q4.22: server response codesとは何ですか? またどんな意味があるのですか?

CGIプログラムはサーバに特定のレスポンスコードを送信でき、それは 順番にブラウザに送られてきます。 例えば、あなたが "No Response"(ブラウザが新しいページをロードしないという 意味)が欲しい場合、204 というレスポンスコード(上記参照)を送信することが 必要です。


Q4.23: なぜ
print "Location: http://host/page.html\n"
はうまく機能しないのでしょうか?  1回目だけちゃんと動き、その後は間違ってリダイレクトされるのはどうしてでしょうか?

CGIプログラミングではロケーションヘッダは一つしか送ることが できません。 また、サーバにリダイレクションを実行させたい場合、MIME content タイプを 送ることはできません。 例えば、下記は有効ではありません。 まっ、いくつかのサーバでは機能するかもしれませんが。:

#!/usr/local/bin/perl

.
.
.
print "Content-type: text/plain\n"
print "Location: http://some.machine/some.doc\n\n"";

Q4.24: 私のHTMLページの一番下の所に自動的に

"Last updated: ..."
行を加えたいのですが、どうすればいいのでしょうか?  あるいは、SSIページではこれはできないのでしょうか?  CGIスクリプトの日付はどうやったらわかりますか?

CGIを使って動的に文書を生成しているのなら、かなり簡単にタイム スタンプを挿入できます。以下は一例です。(Perl 5のみ)

$last_updated = localtime (time);
print "Last updated: $last_updated\n";
あるいは:
require "ctime.pl";

$last_updated = &ctime (time);
print "Last updated: $last_updated\n";
あるいは これでも:
chop ($date = `/usr/local/bin/date`);
print "Last updated: $last_updated\n";
以下のようにSSIを使っても完遂できます。:
<--#echo var="LAST_MODIFIED"-->


Q4.25: 簡単なタスクだけどPCPだとひどく複雑になってしまうのでシェルの方が いいというのはどんな時でしょうか?  難しい事に対して十分力を発揮できない場合はどんな時ですか?  Cについてはどうですか?

それぞれの言語はそれなりの優位性、非優位性を持っています。 やろうとしていることによるんです:という話は何回も耳にしたと思います。 1時間に数千回もアクセスされるようなCGIを書くつもりなら、CかC++を 使って書くべきでしょう。(実装と同じ位)早い解決を求めているのなら Perlが進むべき道です!

一般的には、どんなタイプのCGIプログラミングでも、シェルを使うのは よした方がいいです。セキュリティ問題に対する潜在的な脅威を秘めている からというのがその理由です。


This document, and all its parts, are Copyright (c) 1996, Shishir Gundavaram and Tom Christiansen. All rights reservered. Permisson to distribute this collection, in part or full, via electronic means (emailed, posted or archived) or printed copy are granted providing that no charges are involved, reasonable attempt is made to use the most current version, and all credits and copyright notices are retained. Requests for other distribution rights, including incorporation in commercial products, such as books, magazine articles, or CD-ROMs should be made to either of the authors.

ご意見、ご要望は、 電子メールまたは 投稿にお願い致します。

ホームページへ戻る。