筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2007/No9.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2007/
http://www.coins.tsukuba.ac.jp/~yas/
size_t strlcpy(char *dst, const char *src, size_t size);第3引数には、コピー先 dst の大きさを与える。strlen(src) のように、コピー 元 src から計算してはならない。
char buf[BUFFERSIZE]; /*正*/ strlcpy( buf,p,sizeof(buf) ); /*正*/ strlcpy( buf,p,BUFFERSIZE ); /*正*/ snprintf( buf,BUFFERSIZE,"%s",p ); /*誤*/ strlcpy( buf,p,strlen(p) );snprintf() を正しく使うこと。
int snprintf(char *str, size_t size, char *,...);第2引数には、str の大きさを与える。strlen() などで、コピー元から計算し てはならない。
char buf[BUFFERSIZE];
/*正*/ snprintf( buf,sizeof(buf),"%s/public_html/%s",home,file );
/*正*/ snprintf( buf,BUFFERSIZE, "%s/public_html/%s",home,file );
/*誤*/ snprintf( buf,CONST+strlen(home)+strlen(file),BUFFERSIZE,
"%s/public_html/%s",home,file );
再提出時には、差分を強調して書く。レポートには、例題で示した関数を含め
ない方が読みやすい。
Gateway とは、WWW で使われている HTTP というプロトコルへの入り口という 意味である。 プログラムの実行結果は、普通は、HTML にすることが多いが、普通のテキス トであることもイメージであることもある。
CGI の利用例
情報学類には、2つのサーバ・プロセスが動作している。
CGI になるファイル名のパタンの例(設定により、これ以外も可能)。
/cgi-bin/name /dir/name.cgi
1: /*
2: cgi-hello.c --簡単な CGI のプログラム
3: ~yas/syspro/www/cgi-hello.c
4: Created on: 2002/06/23 18:21:34
5: */
6:
7: #include <stdlib.h> /* exit() */
8: #include <stdio.h> /* printf() */
9:
10: extern void print_header();
11: extern void print_content();
12:
13: main()
14: {
15: print_header();
16: print_content();
17: }
18:
19: void print_header()
20: {
21: printf("Content-Type: text/html\n");
22: printf("\n");
23: }
24:
25: void print_content()
26: {
27: printf("<HTML><HEAD></HEAD><BODY>\n");
28: printf("hello.\n");
29: printf("</BODY></HTML>\n");
30: }
CGI のプログラムは、Apache の場合、標準ではHTTP のヘッダ(一番最初の
「HTTP/1.0 200 OK」 ではなく、2行目以降)を標準出力に出力する所から始
まる。Content-Type: 行は、必ず付ける。空行がヘッダと本分の区切りである。
本文には、この場合は、HTML の文書を置いている。
実行例:
% mkdir ~/public_html/syspro-cgi-examples/CGI のプログラムの場合、拡張子を .cgi にする。 CGI のプログラムを、シェルから実行することもできる。 引数の与え方については、後述する。% cd ~/public_html/syspro-cgi-examples/
% cp ~yas/syspro/www/cgi-hello.c .
% cc cgi-hello.c -o cgi-hello.cgi
% ./cgi-hello.cgi
Content-Type: text/html <HTML><HEAD></HEAD><BODY> hello. </BODY></HTML> %
![]()
WWW ブラウザでは、次の URL を開く。
http://www.coins.tsukuba.ac.jp/~syspro/2007/No9_files/cgi-hello.cgi
telnet では、次のようにして実行する。
% telnet www.coins.tsukuba.ac.jp 80GET の代りに POST でもよい。Trying 130.158.86.207... Connected to www.coins.tsukuba.ac.jp. Escape character is '^]'. GET /~syspro/2007/No9_files/cgi-hello.cgi HTTP/1.0
![]()
HTTP/1.1 200 OK Date: Tue, 12 Jun 2007 10:50:16 GMT Server: Apache/2.0.55 (Unix) PHP/4.4.2 Connection: close Content-Type: text/html <HTML><HEAD></HEAD><BODY> hello. </BODY></HTML> Connection closed by foreign host. %
![]()
% telnet www.coins.tsukuba.ac.jp 80Trying 130.158.86.207... Connected to www.coins.tsukuba.ac.jp. Escape character is '^]'. POST /~syspro/2007/No9_files/cgi-hello.cgi HTTP/1.0
![]()
HTTP/1.1 200 OK Date: Tue, 12 Jun 2007 10:51:47 GMT Server: Apache/2.0.55 (Unix) PHP/4.4.2 Connection: close Content-Type: text/html <HTML><HEAD></HEAD><BODY> hello. </BODY></HTML> Connection closed by foreign host. %
![]()
Status:という行を含めることで、ステータ
ス行を記述することもできる。Status:がない場合、Location:
というヘッダがあれば、302 Found (redirect)、それ以外の場合は、200
OKが返される。それ以外のヘッダも、Apache が自動的に生成して返す。
ステータス行も含めて、完全なヘッダを CGI で作成して返すこともできる。 この方法を、Apache では、nph (non-parsed headers) と呼んでいる。
Apache で、nph を使うには、nph- で始まるファイル名を用いる。こ
れを使うと、Apache は、CGI プログラムが作成したヘッダを一切解釈(parse)
せず、そのままクライアントに返す。
HTML記述例:
<H2><A NAME="cgi-example-get">CGI の GET メソッドを使う例</A></H2>
<FORM ACTION="_cgi_printarg_url_rel_rel()" method="get">
<P>
姓: <INPUT type="text" name="lastname">
名: <INPUT type="text" name="firstname"><BR>
<INPUT type="radio" name="sex" value="Male"> 男 <BR>
<INPUT type="radio" name="sex" value="Female"> 女 <BR>
<INPUT type="radio" name="sex" value="others"> その他 <BR>
電子メール: <INPUT type="text" name="email"><BR>
<INPUT type="submit" value="send"> <INPUT type="reset">
</P>
</FORM>
<H2><A NAME="cgi-example-post">CGI の POST メソッドを使う例</A></H2>
<FORM ACTION="No9_files/cgi-printarg.cgi" method="post">
<P>
姓: <INPUT type="text" name="lastname">
名: <INPUT type="text" name="firstname"><BR>
<INPUT type="radio" name="sex" value="Male"> 男 <BR>
<INPUT type="radio" name="sex" value="Female"> 女 <BR>
<INPUT type="radio" name="sex" value="others"> その他 <BR>
電子メール: <INPUT type="text" name="email"><BR>
<INPUT type="submit" value="send"> <INPUT type="reset">
</P>
</FORM>
表示例:
<FORM>と</FORM>で括られた部
分が1つのフォームになる。(1つのページに複数のフォームを置くことがで
きる。)
<INPUT>の部分が、ブラウザからサーバに送られるデー
タを表わす。
「send」ボタンが押されると、ブラウザは、指定された
メソッドを使ってサーバにデータを送る。
メソッドとして GETが使われた時、Web ブラウザは、
普通のファイルを指定する時と同じように、URL に含めて送る。
GET /~syspro/2007/No9_files/cgi-printarg.cgi?lastname=姓&firstname=名&sex=Male&email=who@u-ust.ac.jp HTTP/1.0
URL の長さの上限で制限されるので、長いデータは送れない。
メソッドとして POST が使われた時、Web ブラウザは、データ
をHTTP の本文(ヘッダではない部分、空行の後)に入れて送る。
WWW サーバは、それを受け取ると、指定されたプログラムを実行する。実行さ れたプログラムは、 環境変数と 標準入力 からデータを受け取ることができる。
環境変数
REQUEST_METHOD
QUERY_STRING
SCRIPT_NAME
CONTENT_LENGTH
&」で区切られた
「フィールド名=値」の並びになっている。
例:
lastname=姓&firstname=名&sex=Male&email=who@u-ust.ac.jp
GETメソッドが使われた場合、CGI のプログラムは、環境変数
QUERY_STRING からデータを得る。
POSTメソッドが使われた場合、CGI のプログラムは、データを標準入力から受
け取る。データのバイト数は、環境変数CONTENT_LENGTH に指定
されている。
送られてくるパラメタの中に漢字、
(&自身)、
URLで使えない文字が含まれていた場合、
「%hh( hh は2桁の16進数)」という形になっている。
これを元にもどすにはcgiparse というプログラムや
perl の命令 hex() が使われる。
1:
2: /*
3: cgi-printarg.c -- CGI プログラムに対する引数を表示するプログラム
4: ~yas/syspro/www/cgi-printarg.c
5: Created on: 2002/06/23 18:21:34
6: */
7:
8: #include <stdlib.h> /* getenv(), malloc() */
9: #include <stdio.h> /* printf() */
10: #include <string.h> /* strlen() */
11:
12: extern void print_header(void);
13: extern void print_content(void);
14: extern char *get_query_string();
15: extern char *read_query_string();
16: extern void safe_printenv( char *name );
17: extern void safe_print_string( char *str );
18: extern char *html_escape( char *str );
19: extern char *decode_hex( char *str );
20: extern int make_query_vector( int *qcp, char ***qvp, char *qs );
21: extern void free_query_vector( int qc, char **qv );
22: extern int countchr( char *s, char c );
23:
24: main()
25: {
26: print_header();
27: print_content();
28: }
29:
30: void print_header()
31: {
32: printf("Content-Type: text/html\n");
33: printf("\n");
34: }
35:
36: void print_content()
37: {
38: char *query_string ;
39: char **qv ;
40: int qc ;
41: int i ;
42:
43: printf("<HTML><HEAD></HEAD><BODY><PRE>\n");
44:
45: safe_printenv("REQUEST_METHOD");
46: safe_printenv("SCRIPT_NAME");
47: safe_printenv("QUERY_STRING");
48: safe_printenv("CONTENT_LENGTH");
49:
50: query_string = get_query_string();
51: printf("query_string:\n");
52: safe_print_string( query_string ); printf("\n");
53:
54: if( make_query_vector( &qc, &qv, query_string ) < 0 )
55: {
56: printf("Error while parsing query string\n");
57: printf("</PRE></BODY></HTML>\n");
58: exit( -1 );
59: }
60: for( i=0 ; i<qc ; i++ )
61: {
62: char *decoded ;
63: printf("qv[%d]: ",i);
64: safe_print_string(qv[i]);
65: decoded = decode_hex( qv[i] );
66: printf("("); safe_print_string( decoded ); printf(")\n");
67: if( decoded )
68: free( decoded );
69: }
70:
71: printf("</PRE></BODY></HTML>\n");
72: free_query_vector( qc, qv );
73: free( query_string );
74: }
75:
これは、標準(nphではない)の CGI プログラムな
ので、print_header()では、HTTP のヘッダのうち、Content-Type: 行だけを
出力している。
print_content() では、本文を出力している。
safe_printenv() で、環境変数 REQUEST_METHOD, SCRIPT_NAME, QUERY_STRING, CONTENT_LENGTH の内容を標準出力に出力している。
get_query_string() は、クライアントから送られてきたパラメタ(query string)を読込み、文字列として返す関数である。この結果を、 safe_print_string() で標準出力に出力している。
for ループでは、qv[] をsafe_print_string() で標準出力に出力している。
さらに、decode_hex() で、%hh
(%hhは16進数)の形式を、バイトに元に戻すものであ
る。
76: char *get_query_string()
77: {
78: char *request_method, *query_string;
79: request_method = getenv("REQUEST_METHOD");
80: if( request_method == 0 )
81: return( 0 );
82: else if( strcmp(request_method,"GET") == 0 )
83: {
84: query_string = getenv("QUERY_STRING");
85: if( query_string == 0 )
86: return( 0 );
87: else
88: return( strdup(query_string) );
89: }
90: else if( strcmp(request_method,"POST") == 0 )
91: {
92: return( read_query_string() );
93: }
94: else
95: {
96: printf("Unknown method: ");
97: safe_print_string( request_method );
98: printf("\n");
99: }
100: }
101:
102: char *read_query_string()
103: {
104: int clen ;
105: char *content_length ;
106: char *buf ;
107:
108: content_length = getenv("CONTENT_LENGTH");
109: if( content_length == 0 )
110: {
111: return( 0 );
112: }
113: else
114: {
115: clen = strtol( content_length,0,10 );
116: buf = malloc( clen + 1 );
117: if( buf == 0 )
118: {
119: printf("read_query_string(): no memory\n");
120: exit( -1 );
121: }
122: if( read(0,buf,clen) != clen )
123: {
124: printf("read error.\n");
125: exit( -1 );
126: }
127: buf[clen] = 0 ;
128: return( buf );
129: }
130: }
131:
get_query_string() は、クライアントから送られてきたパラメタを、1つの
文字列として受け取るものである。GET メソッドの場合()は、単に環境変数
REQUEST_METHOD を読めばよい。ただし、文字列の長さの上限がきつい。
strdup() は、文字列をコピーするものである。使い終わったら、free() で解 放する。
POST メソッドの場合は、read_query_string() で続きの処理を行う。
132: void safe_printenv( char *name )
133: {
134: char *val ;
135: char *safe_val ;
136:
137: printf("%s=",name );
138: val = getenv( name );
139: safe_print_string( val );
140: printf("\n");
141: }
142:
143:
144: void safe_print_string( char *str )
145: {
146: char *safe_str ;
147:
148: if( str == 0 )
149: {
150: printf("(null)");
151: return;
152: }
153: safe_str = html_escape( str );
154: if( safe_str == 0 )
155: {
156: printf("(no memory)");
157: }
158: else
159: {
160: printf("%s",safe_str );
161: free( safe_str );
162: }
163: }
164:
165: char *html_escape( char *str )
166: {
167: int len ;
168: char c, *tmp, *p, *res ;
169:
170: len = strlen( str );
171: tmp = malloc( len * 6 + 1 );
172: if( tmp == 0 )
173: return( 0 );
174: p = tmp ;
175: while( c = *str++ )
176: {
177: switch( c )
178: {
179: case '&': memcpy(p,"&", 5); p += 5 ; break;
180: case '<': memcpy(p,"<", 4); p += 4 ; break;
181: case '>': memcpy(p,">", 4); p += 4 ; break;
182: case '"': memcpy(p,""",6); p += 6 ; break;
183: default: *p = c ; p++ ; break;
184: }
185: }
186: *p = 0 ;
187: res = strdup( tmp );
188: free( tmp );
189: return( res );
190: }
191:
safe_printenv() は、環境変数をgetenv() で検索し、その値を
safe_print_string() で標準出力に出力している。
safe_print_string() は、次の html_escape() を使って文字列を安全なもの にして表示する。
html_escape() は、次の表の文字を置換えている。
画面表示 代りに送りだす文字列 ---------------------------------------------------------------------- & & < < > > " "これで、<SCRIPT> のような危険なスクリプトが送り込まれたとし ても「<SCRIPT>」と表示と表示されるだけで、スクリプト は実行されない。
len * 6 は、最大で元の文字列の6倍の長さになることに備えている。
192: char *decode_hex( char *str ) ... 227: int make_query_vector( int *qcp, char ***qvp, char *qs ) ... 272: void free_query_vector( int qc, char **qv ) ... 283: int countchr( char *s, char c ) ...decode_hex() は、「
%hh(2桁の16進数)」を元のバイトに
戻すものである。isxdigit() は、16進数の文字として正しいかを調べてい
る。strtol() で基底を 16 として変換している。
make_query_vector() は、クライアントから送られてきたパラメタ(query string)を、分解して1つひとつバラバラににするものである。分解した結果は、 main() の引数である int argc, char *argv[] と同じ形になっている。qc が、 argc, qv が argv に対応する。
make_query_vector() は、「&」で区切られて送られてきたパラメタをバラバ ラに分解し、main の引数である int argc, char *argv[] と同じ形にするもの である。まず、「&」の数を先に数えて、argv に相当するメモリを確保する。 1つ多く確保しているのは、最後に0を置くためである。ただし、「&&&」 のように空のものは取り除いている。
for() 文で1つの「&」の左、または、終端の0の左のまでを切り出している。 qs が先頭、p が「&」、または、終端の0を差すようにする。malloc() で長 さ分(終端の0を含む)のメモリを確保してmemcpy() でコピーしている。 s[len] で終端の0を付け、qv[i] に保存している。qs を q にして、次のルー プに進む。
free_query_vector() は、make_query_vector() で malloc() したものを free() するための手続きである。
countchr() は、文字列の中でその文字がいくつ出てくるかを数えるものである。
実行例:
REQUEST_METHOD=GET SCRIPT_NAME=/~syspro/2007/No9_files/cgi-printarg.cgi QUERY_STRING=lastname=arg1&firstname=arg2&sex=Male&email=arg3 CONTENT_LENGTH=(null) query_string: lastname=arg1&firstname=arg2&sex=Male&email=arg3 qv[0]: lastname=arg1(lastname=arg1) qv[1]: firstname=arg2(firstname=arg2) qv[2]: sex=Male(sex=Male) qv[3]: email=arg3(email=arg3)
REQUEST_METHOD=POST SCRIPT_NAME=/~syspro/2007/No9_files/cgi-printarg.cgi QUERY_STRING= CONTENT_LENGTH=48 query_string: lastname=arg1&firstname=arg2&sex=Male&email=arg3 qv[0]: lastname=arg1(lastname=arg1) qv[1]: firstname=arg2(firstname=arg2) qv[2]: sex=Male(sex=Male) qv[3]: email=arg3(email=arg3)
/var/log/httpd/www.coins80/access_log.* /var/log/httpd/www.coins443/access_log.*この中で、数字が一番大きなもの、または、日付が新しいものが、 現在増加しているログである。
% cd /var/log/httpd/www.coins80以下は、アクセスログの例である。% ls access_log.* | tail -1
% ls -t access_log.* | head -1
%
![]()
130.158.83.140 - - [10/Jun/2007:21:47:44 +0900] "GET /~syspro/2007/shui/common_files/Ctrl-Z.png HTTP/1.1" 200 189 130.158.83.140 - - [10/Jun/2007:21:53:52 +0900] "GET /~yas/coins/syspro-2007/No9.html HTTP/1.1" 200 570761行が1つのページの転送を意味する。左端は、クライアントの IP アドレス である。どのような要求が来たか、クライアントのIPアドレスがわかる。
アクセスログの他に、エラーが起きた時にもログが作られる。これをエラーロ グという。エラーログは、情報学類のサーバ www (orchid-nwd) では、次の場所にある。
/var/log/httpd/www.coins80/error_log.* /var/log/httpd/www.coins443/error_log.*CGI のプログラムをデバッグする時には、このエラーログを見て原因を探る。
その他に、次のようなファイルがある。
agent_log
referer_log
suexec_log
GET の場合、環境変数 REQUEST_METHOD に GETと設定し、環境変数 QUERY_STRING にパラメタを設定する。
% setenv REQUEST_METHOD GETPOST の場合、まず、データをファイルに保存しておく。そのバイト数を wc コマンドなどで調べておく。環境変数 REQUEST_METHOD に POST と設定する。 環境変数 CONTENT_LENGTH にデータファイルの大きさを設定する(ここでは最 後の改行を取り除いたバイト数を指定している)。% setenv QUERY_STRING 'lastname=name1&firstname=name2&sex=Male&email=who@dom'
% ./cgi-printarg.cgi
Content-Type: text/html <HTML><HEAD></HEAD><BODY><PRE> REQUEST_METHOD=GET SCRIPT_NAME=(null) QUERY_STRING=lastname=name1&firstname=name2&sex=Male&email=who@dom CONTENT_LENGTH=(null) query_string: lastname=name1&firstname=name2&sex=Male&email=who@dom qv[0]: lastname=name1(lastname=name1) qv[1]: firstname=name2(firstname=name2) qv[2]: sex=Male(sex=Male) qv[3]: email=who@dom(email=who@dom) </PRE></BODY></HTML> %
![]()
% echo 'lastname=name1&firstname=name2&sex=Male&email=who@dom' > data% wc data
1 1 54 data % setenv REQUEST_METHOD POST
% unsetenv QUERY_STRING
% setenv CONTENT_LENGTH 53
% ./cgi-printarg.cgi < data
Content-Type: text/html <HTML><HEAD></HEAD><BODY><PRE> REQUEST_METHOD=POST SCRIPT_NAME=(null) QUERY_STRING=(null) CONTENT_LENGTH=53 query_string: lastname=name1&firstname=name2&sex=Male&email=who@dom qv[0]: lastname=name1(lastname=name1) qv[1]: firstname=name2(firstname=name2) qv[2]: sex=Male(sex=Male) qv[3]: email=who@dom(email=who@dom) </PRE></BODY></HTML> %
![]()
rw-rw-rw-や 777
rwxrwxrwx にする)。このため、そのコンピュータにログイン
でき人ならば、そのファイルの名前を知っていれば、データを取り出したり内
容を破壊したりできる。
suEXEC を使う方法の場合、長所と短所は逆になる。
普通のプログラミング言語は、アプリケーション・プログラム本体を記述する 時に使われる。 これに対して、 スクリプト言語 は、アプリケーション本体で はなく、アプリケーションの細かな動作を変更したり、アプリケーション本体 を変更することなく機能を追加したりするために使われる言語である。 普通のプログラミング言語は、アプリケーション・プログラマにより使われ、 コンパイラで機械語に変換されるので、実行時には機械語しか残っていない。 これに対して、スクリプト言語は、アプリケーションのユーザや、システム管 理者などによって使われ、プログラムは、アプリケーションに組み込まれた インタプリタで解釈実行される。 スクリプト言語を使うと、単に変数を設定することに比べて、 高度な機能拡張が機能になる。
スクリプト言語の例:
% cat cgi-hello-csh.cgi#!/bin/csh -f cat <<EOF Content-Type: text/html <HTML><HEAD></HEAD><BODY> hello. </BODY></HTML> EOF % ./cgi-hello-csh.cgi
Content-Type: text/html <HTML><HEAD></HEAD><BODY> hello. </BODY></HTML> %
![]()
JavaScript は、文法が少し Java 言語に似ているが、Java とはまったく別の 言語である。WWW ページの中の Javaアプレットは、 「実行可能なインライン・イメージ」に似ている。これに対して JavaScript の記述は、「実行可能なインライン・テキスト」 に似ている。
JavaScrip のプログラムの例:
<SCRIPT LANGUAGE="JavaScript">
<!--
for( i=0 ; i<10; i++ )
document.writeln("<P>hello,world</P>");
//-->
</SCRIPT>
これは、<P>hello,world</P> を 10 回書いた
のと同じ効果がある。<!--と
//->は、JavaScriptを知らないブラウザにはコメント
として扱われる。関数定義などは、ヘッダ部分
<HEAD></HEAD>に書くという方法もよく使われる。
for、while 、
if, else, continue,
break がある。ただし、switch は使えない。
function で、関数定義ができる。
var で宣言すれば、ローカル変数になる。
配列は、new Array(長さ) で確保する。
class というキーワードはない。
function に new を付けて呼べば、オブジェ
クトになる。オブジェクトのメソッドでは、this を使っ
て要素を参照できる。
JavaScript の記述は、CGI と似ているところもあるが、JavaScript でないと
できないものに、ブラウザの制御がある。たとえば、次の例では、ブラウザの
(戻る)ボタンと同じ動きをさせることができる。
<A HREF="javascript:history.back();">戻る</A>次の例は、
<FORM></FORM>からパラメタを受け取るも
のである。
<SCRIPT LANGUAGE="JavaScript">
function go(s,h,p)
{
location.href = s.value + "://" + h.value + p.value ;
}
</SCRIPT>
<FORM NAME="form1">
<INPUT NAME="scheme" TYPE="text" VALUE="http"><BR>
<INPUT NAME="host" TYPE="text"><BR>
<INPUT NAME="path" TYPE="text" VALUE="/"><BR>
<INPUT TYPE="button" VALUE="go" onClick="go(form1.scheme,form1.host,form1.path)">
</FORM>
onClick 属性の値は、クリックした時に評
価される式であり、関数 go() が呼び出されている。引
数は、<FORM> の値である。関数
go() の中では、
.value フィールドから値が読み出されている。
location.href に代入することで
そのページを表示させることができる。
この例では、<INPUT type="text">と
<INPUT type="button">が使われている。その他に、
<FORM>では、<INPUT
TYPE="radio">、<INPUT TYPE="checkbox">、
<SELECT>が使える。イベントとしては、
onClick が主に使われる。その他に、
onFocus が使われることもある。
JavaScript では、document.open() で新しく HTML の
ドキュメントを生成して、それをブラウザに表示させることもできる。
WWWブラウザでは、信頼しているサイトから送られてくるJavaScriptのプログ ラムだけを実行するようにし、攻撃サイトから送られてくるJavaScriptのプロ グラムを実行しないようにしたい。
脆弱性があるサイトでは、攻撃サイトから送られてきた JavaScript のプログ ラムを中継してしまう。
図? JavaScripの送信元サイトの区別
CGI のプログラムをつくる時には、クロスサイトスクリプティング攻撃に気を
つける。これは、クライアントから送られてる文字列の中に
<SCRIPT>のようなタグが含まれていた場合、それをそのままクラ
イアントに送り返すと問題がある。
(さらに、
%hh
にも気をつける必要がある。)
クライアントから送られてきた文字列は、必ず検査し、安全な状態にして (sanitize)から使う。「<>&"」のようなタグが含まれている場合 には注意する。このような文字列を受け取った場合、不用意に送り返してはい けない。送り返す時には、html_escape() のよう な方法で必ずエスケープする。
他のプログラムを実行する時には、execve() のようなシステムコールを使い、
かつ、限られたプログラムしか実行しないようにすると安全性が高くなる。ク
ライアントから送られてきた文字列をsystem() や popen() に渡してプログラ
ムを実行する時には、必ず検査する。特にシェルが解釈する特殊な文字
「| & ; && || `」などが含
まれていた場合、意図しないプログラムが実行されることがある。
char *user ; ... snprintf(cmd,BUFSIZE,"finger %s",user ); f = poepn(cmd,"r");もし、user に
";" や "|" が含まれていたら、、、
f = poepn("finger yas; /bin/sh","r");
perl の open() には、危険性がある。 C 言語のライブラリ関数 popen() と同じ動きをすることがある。
open(FILE, "|cmd")Perl の危険な関数、式、
産業技術総合研究所 グリッド研究センター セキュアプログラミングチーム
http://securit.gtrc.aist.go.jp/
)
IPA セキュア・プログラミング講座
http://www.ipa.go.jp/security/awareness/vendor/programming/
)
アクセスカウンタなどデータ・ファイルを書き換えるような CGI プログラム の場合、複数のプログラムが同時に実行された時のことを考慮する必要がある。 必要に応じて、ファイルのロックを行うなどして、一度に1つのプロセスしか プログラムを実行しないようにする。このことを、相互排除という。
相互排除について詳しくは、後半のマルチスレッド・プログラミングや 2学期のオペレーティング・システムで扱う。 特に、扱うべき資源が複数になった場合には、デッドロックに気をつける。
Unix では、ファイルに対するロックを扱うために、次のような機能を提供し ている。
セッション管理には、次のような方法がある。
セッション管理にクッキーを使う方法は、正しく使えば比較的安全である。し かし、クッキーはセッションを越えて有効なもの、異なるサイトに送られるも のも定義できる。また、クッキーにパスワードを埋め込むという幼稚で危険な 実装も時々見受けられる。
セッションは、認証とは別である。同じ人が複数のセッションを実行すること もある。
協調して動作しているプログラムの間で、ある一連の作業を識別するための数 を意味する。
RPC、HTTP(WWW)で使われる。
WWW ブラウザや telnet を使って得た結果と /var/www/htdocs/ 以下のファイ ルが同一であることをwww に ssh でログインして確認しなさい。
同様に https でアクセスできるページについても調べなさい。 トップのディレクトリは、/var/www/secure_htdocs/ 以下にある。
/var/log/httpd/access_log というファイルにためられる。
これを、次のようにして観察しなさい。
% cd /var/log/httpd/www.coins80この例では、% ls access_log.*
<ログファイル多数> % ls access_log.* | tail -1
access_log.200706071837 % ls -t access_log.* | head -1
access_log.200706071837 %
![]()
access_log.200706071837 が最新のものである。
% tail -f access_log.200706071837![]()
http://www.coins.tsukuba.ac.jp/や自分の WWW ページを開く。
次の CGI プログラムに対して、フォームを定義し、何かデータを送ってみなさい。
http://www.coins.tsukuba.ac.jp/~syspro/2007/No9_files/cgi-printarg.cgi
ヒント:
char *arg1_s,arg2_s;
arg1_s = getparam("arg1");
arg2_s = getparam("arg2");
getparam() は、次のように自分で定義する。
char *getparam( int qc, char *qv[], char *name /*変数名*/ )
{
qv[i] の中で "変数名=値" となっているものをループで探す。
見つければ "値" の部分を返す。
見つからなければ、NULL を返す。
}
余裕があれば、メニューやラジオボタンなどで足算以外の計算もできるように しなさい。
% calJune 2007 S M Tu W Th F S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 %
![]()
CGI プログラムを実行した月のカレンダーを表示しなさい。固定した表示する だけなら、CGI の意味はない。余計な表示は省略すること。
余裕があれば、今月のカレンダーではなく、任意の年・月のカレンダーを表示 しなさい。
この課題では、system() や popen() を用いてはならない。
CGI プログラムが、どのユーザの権限で動作しているかを調べるプログラムをつくりなさい。
ヒント:CGI のプログラムから id コマンドを実行させる。
レポートには、修正個所が簡単にわかるような説明をつけなさい。
レポートには、修正個所が簡単にわかるような説明をつけなさい。