システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro-2002/2002-06-24
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
Gateway とは、WWW で使われている HTTP というプロトコルへの入り口という 意味である。 プログラムの実行結果は、普通は、HTML にすることが多いが、普通のテキス トであることもイメージであることもある。
CGI の利用例
情報学類には、www.coins.tsukuba.ac.jp と www2.coins.tsukuba.ac.jp の2 つのサーバがあり、CGI が動作可能であるように設定されているのは、www2 の方である。
CGI になるファイル名のパタンの例(設定により、これ以外も可能)。
/cgi-bin/name /dir/name.cgi
---------------------------------------------------------------------- 1: 2: /* 3: cgi-hello.c --簡単な CGI のプログラム 4: ~yas/syspro/www/cgi-hello.c 5: Start: 2002/06/23 18:21:34 6: */ 7: 8: void print_header(); 9: 10: main() 11: { 12: print_header(); 13: 14: printf("<HTML>\n"); 15: 16: printf("hello.\n"); 17: printf("</HTML>\n"); 18: exit( 0 ); 19: } 20: 21: void print_header() 22: { 23: printf("Content-Type: text/html\r\n"); 24: printf("\r\n"); 25: } ----------------------------------------------------------------------CGI のプログラムは、HTTP のヘッダ(一番最初の 「HTTP/1.0 200 OK」 では なく、2行目以降)を標準出力に出力する所から始まる。Content-Type: 行は、 必ず付ける。空行がヘッダと本分の区切りである。本文には、この場合は、 HTML の文書を置いている。
実行例:
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> hello. </HTML> %
----------------------------------------------------------------------
WWW ブラウザでは、次の URL を開く。
http://www2.coins.tsukuba.ac.jp/~yas/syspro/cgi-examples/cgi-hello.cgi
telnet では、次のようにして実行する。
GET の代りに POST でもよい。---------------------------------------------------------------------- % telnet www2 80Trying 130.158.86.5... Connected to www2. Escape character is '^]'. GET /~yas/syspro/cgi-examples/cgi-hello.cgi HTTP/1.0
![]()
HTTP/1.1 200 OK Date: Sun, 23 Jun 2002 13:48:18 GMT Server: Apache/1.3.22 (Unix) (Red-Hat/Linux) DAV/1.0.2 mod_perl/1.24_01 mod_throttle/3.1.2 Connection: close Content-Type: text/html hello. Connection closed by foreign host. %
----------------------------------------------------------------------
---------------------------------------------------------------------- % telnet www2 80Trying 130.158.86.5... Connected to www2. Escape character is '^]'. POST /~yas/syspro/cgi-examples/cgi-hello.cgi HTTP/1.0
![]()
HTTP/1.1 200 OK Date: Sun, 23 Jun 2002 13:50:17 GMT Server: Apache/1.3.22 (Unix) (Red-Hat/Linux) DAV/1.0.2 mod_perl/1.24_01 mod_throttle/3.1.2 Connection: close Content-Type: text/html hello. Connection closed by foreign host. %
----------------------------------------------------------------------
HTML記述例: http://www.coins.tsukuba.ac.jp/~yas/syspro/cgi-examples/cgi-printarg.html
<H1><A NAME="cgi-example-get">CGI の GET メソッドを使う例</A></H1> <FORM ACTION="http://www2.coins.tsukuba.ac.jp/~yas/syspro/cgi-examples/cgi-printarg.cgi" 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="text" name="email"><BR> <INPUT type="submit" value="send"> <INPUT type="reset"> </P> </FORM> <H1><A NAME="cgi-example-post">CGI の POST メソッドを使う例</A></H1> <FORM ACTION="http://www2.coins.tsukuba.ac.jp/~yas/syspro/cgi-examples/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="text" name="email"><BR> <INPUT type="submit" value="send"> <INPUT type="reset"> </P> </FORM>表示例:
<FORM>
と</FORM>
で括られた部
分が1つのフォームになる。(1つのページに複数のフォームを置くことがで
きる。)
<INPUT>
の部分が、ブラウザからサーバに送られるデー
タを表わす。
「send
」ボタンが押されると、ブラウザは、HTTPの POST メソッ
ドを使って、サーバにデータを送る。
WWW サーバは、それを受け取ると、指定されたプログラムを実行する。実行さ れたプログラムは、環境変数と標準入力からデータを受け取ることができる。
環境変数
REQUEST_METHOD
SCRIPT_NAME
QUERY_STRING
POST
が使われた時、標準入力から
<FORM></FORM>
で指定されたパラメタを受け取ることが
でる。
このデータは、「&
」で区切られた
「フィールド名=値
」の並びになっている。
例:
lastname=姓&firstname=名&sex=Male&email=who@u-ust.ac.jp
環境変数 CONTENT_LENGTH
からは、データの長さが得られる。
メソッドとして、GET
が使われた時には、
環境変数 QUERY_STRING
に
「&
」で区切られた「フィール
ド名=値
」の並びが含まれている。
送られてくるパラメタの中に漢字、
(&自身)、
URLで使えない文字が含まれていた場合、
「%hh
(2桁の16進数)」という形になっている。
これを元にもどすにはcgiparse
というプログラムや
perl
の命令 hex() が使われる。
---------------------------------------------------------------------- 1: 2: /* 3: cgi-printarg.c -- CGI プログラムに対する引数を表示するプログラム 4: ~yas/syspro/www/cgi-printarg.c 5: Start: 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: void print_header(void); 13: void safe_printenv( char *name ); 14: char *get_query_string(); 15: char *read_query_string(); 16: void safe_print_string( char *str ); 17: char *html_escape( char *str ); 18: char *decode_hex( char *str ); 19: int countchr( char *s, char c ); 20: 21: main() 22: { 23: char *query_string ; 24: char **qv ; 25: int qc ; 26: int i ; 27: 28: print_header(); 29: 30: printf("<HTML><PRE>\n"); 31: 32: safe_printenv("REQUEST_METHOD"); 33: safe_printenv("SCRIPT_NAME"); 34: safe_printenv("QUERY_STRING"); 35: safe_printenv("CONTENT_LENGTH"); 36: 37: query_string = get_query_string(); 38: printf("query_string:\n"); 39: safe_print_string( query_string ); printf("\n"); 40: 41: if( make_qcqv( &qc, &qv, query_string ) < 0 ) 42: { 43: printf("Error while parsing query string\n"); 44: exit( -1 ); 45: } 46: for( i=0 ; i<qc ; i++ ) 47: { 48: char *nohex ; 49: printf("qv[%d]: ",i); 50: safe_print_string(qv[i]); 51: nohex = decode_hex( qv[i] ); 52: printf("("); 53: safe_print_string( nohex ); 54: printf(")\n"); 55: } 56: 57: printf("</PRE></HTML>\n"); 58: exit( 0 ); 59: } 60: 61: void print_header() 62: { 63: printf("Content-Type: text/html\r\n"); 64: printf("\r\n"); 65: } 66: 67: void safe_printenv( char *name ) 68: { 69: char *val ; 70: char *safe_val ; 71: 72: printf("%s=",name ); 73: val = getenv( name ); 74: safe_print_string( val ); 75: printf("\n"); 76: } 77: ----------------------------------------------------------------------CGI のプログラムは、HTTP のヘッダ(一番最初の OK ではなく、2行目以降) を標準出力に出力する所から始まる。Content-Type: 行は、必ず付ける必要が ある。
safe_printenv() で、環境変数 REQUEST_METHOD, SCRIPT_NAME, QUERY_STRING, CONTENT_LENGTH の内容を標準出力に出力している。
get_query_string() は、クライアントから送られてきたパラメタ(query string)を読込み、文字列として返す関数である。これを、 safe_print_string() で標準出力に出力している。
make_qcqv() は、クライアントから送られてきたパラメタ(query string)を、 分解して1つひとつバラバラににするものである。分解した結果は、main() の引数である int argc, char *argv[] と同じ形になっている。qc が、argc, qv が argv に対応する。
for ループでは、qv[] をsafe_print_string() で標準出力に出力している。
さらに、decode_hex() で、%hh
(%hh
は16進数)の形式を、バイトに元に戻すものであ
る。
safe_printenv() は、環境 変数をgetenv() で検索し、その値をsafe_print_string() で標準出力に出力 している。
---------------------------------------------------------------------- 78: int 79: make_qcqv( int *qcp, char ***qvp, char *qs ) 80: { 81: char **qv ; 82: int qc_max, i, len ; 83: char *s, *p ; 84: 85: if( qs == 0 ) 86: return( -1 ); 87: qc_max = countchr(qs,'&')+1 ; 88: qv = malloc( sizeof(char *)*(qc_max+1) ); 89: if( qv == 0 ) 90: return( -1 ); 91: 92: for( i=0 ; i<qc_max ; i++ ) 93: { 94: while( *qs == '&' ) 95: qs ++ ; 96: if( *qs == 0 ) 97: break; 98: for( p = qs ; *p!='&' && *p!=0 ; p++ ) 99: continue; 100: /* *p == '&' || *p=='0' */ 101: len = p - qs ; 102: s = malloc( len+1 ); 103: if( s == 0 ) 104: { 105: int j ; 106: for( j=0 ; j<i; j++ ) 107: { 108: free( qv[j] ); 109: qv[j] = 0 ; 110: } 111: return( -1 ); 112: } 113: memcpy( s, qs, len ); 114: s[len] = 0 ; 115: qv[i] = s ; 116: qs = p ; 117: } 118: qv[i] = 0 ; 119: *qcp = i ; 120: *qvp = qv ; 121: return( i ); 122: } 123: ----------------------------------------------------------------------make_qcqv() は、「&」で区切られて送られてきたパラメタをバラバラに分解 し、main の引数である int argc, char *argv[] と同じ形にするものである。 まず、「&」の数を先に数えて、argv に相当するメモリを確保する。1つ多 く確保しているのは、最後に0を置くためである。ただし、「&&&」のよう に空のものは取り除いている。
for() 文で1つの「&」の左、または、終端の0の左のまでを切り出している。 qs が先頭、p が「&」、または、終端の0を差すようにする。malloc() で長 さ分(終端の0を含む)のメモリを確保してmemcpy() でコピーしている。 s[len] で終端の0を付け、qv[i] に保存している。qs を q にして、次のルー プに進む。
---------------------------------------------------------------------- 124: char *get_query_string() 125: { 126: char *request_method, *query_string; 127: request_method = getenv("REQUEST_METHOD"); 128: if( request_method == 0 ) 129: return( 0 ); 130: else if( strcmp(request_method,"GET") == 0 ) 131: { 132: query_string = getenv("QUERY_STRING"); 133: if( query_string == 0 ) 134: return( 0 ); 135: else 136: return( strdup(query_string) ); 137: } 138: else if( strcmp(request_method,"POST") == 0 ) 139: { 140: return( read_query_string() ); 141: } 142: else 143: { 144: printf("Unknown method: "); 145: safe_print_string( request_method ); 146: printf("\n"); 147: } 148: } 149: 150: char *read_query_string() 151: { 152: int clen ; 153: char *content_length ; 154: char *buf ; 155: 156: content_length = getenv("CONTENT_LENGTH"); 157: if( content_length == 0 ) 158: { 159: return( 0 ); 160: } 161: else 162: { 163: clen = atoi( content_length ); 164: buf = malloc( clen + 1 ); 165: if( buf == 0 ) 166: { 167: printf("read_query_string(): no memory\n"); 168: exit( -1 ); 169: } 170: if( read(0,buf,clen) != clen ) 171: { 172: printf("read error.\n"); 173: exit( -1 ); 174: } 175: buf[clen] = 0 ; 176: return( buf ); 177: } 178: } 179: ----------------------------------------------------------------------get_query_string() は、クライアントから送られてきたパラメタを、1つの 文字列として受け取るものである。GET メソッドの場合()は、単に環境変数 REQUEST_METHOD を読めばよい。ただし、文字列の長さの上限がきつい。
strdup() は、文字列をコピーするものである。使い終わったら、free() で解 放する。
POST メソッドの場合は、read_query_string() で続きの処理を行う。
---------------------------------------------------------------------- 180: void safe_print_string( char *str ) 181: { 182: char *safe_str ; 183: 184: if( str == 0 ) 185: { 186: printf("(null)"); 187: return; 188: } 189: safe_str = html_escape( str ); 190: if( safe_str == 0 ) 191: { 192: printf("(no memory)"); 193: } 194: else 195: { 196: printf("%s",safe_str ); 197: free( safe_str ); 198: } 199: } 200: 201: char *html_escape( char *str ) 202: { 203: int len ; 204: char c, *tmp, *p, *res ; 205: 206: len = strlen( str ); 207: tmp = malloc( len * 6 + 1 ); 208: if( tmp == 0 ) 209: return( 0 ); 210: p = tmp ; 211: while( c = *str++ ) 212: { 213: switch( c ) 214: { 215: case '&': memcpy(p,"&", 5); p += 5 ; break; 216: case '<': memcpy(p,"<", 4); p += 4 ; break; 217: case '>': memcpy(p,">", 4); p += 4 ; break; 218: case '"': memcpy(p,""",6); p += 6 ; break; 219: default: *p = c ; p++ ; break; 220: } 221: } 222: *p = 0 ; 223: res = strdup( tmp ); 224: free( tmp ); 225: return( res ); 226: } 227: ----------------------------------------------------------------------safe_print_string() は、次の html_escape() を使って文字列を安全なもの にして表示する。
html_escape() は、次の文字について、置き換えている。たとえば、 「<」は、「<」と変換している。これで、 <SCRIPT> のような危険なスクリプトが送り込まれたとしても 「<SCRIPT>」と表示と表示されるだけで、スクリプトは実 行されない。
len * 6 は、最大で元の文字列の6倍の長さになることに備えている。
---------------------------------------------------------------------- 228: char *decode_hex( char *str ) 229: { 230: int len ; 231: char c, *tmp, *p, *res ; 232: 233: len = strlen( str ); 234: tmp = malloc( len + 1 ); 235: if( tmp == 0 ) 236: return( 0 ); 237: p = tmp ; 238: 239: while( *str ) 240: { 241: if( *str == '%' && isxdigit(*(str+1)) && isxdigit(*(str+2)) ) 242: { 243: char hexstr[3] ; 244: hexstr[0] = *(str+1); 245: hexstr[1] = *(str+2); 246: hexstr[2] = 0 ; 247: c = strtol( hexstr, 0, 16 ); 248: *p ++ = c ; 249: str += 3 ; 250: } 251: else 252: { 253: *p ++ = *str ; 254: str ++ ; 255: } 256: } 257: *p = 0 ; 258: res = strdup( tmp ); 259: free( tmp ); 260: return( res ); 261: } 262: 263: int countchr( char *s, char c ) 264: { 265: int count ; 266: for( count=0 ; *s ; s++ ) 267: if( *s == c ) 268: count ++ ; 269: return( count ); 270: } ----------------------------------------------------------------------decode_hex() は、「
%hh
(2桁の16進数)」を元のバイトに
戻すものである。isxdigit() は、16進数の文字として正しいかを調べてい
る。strtol() で基底を 16 として変換している。
countchr() は、文字列の中でその文字がいくつ出てくるかを数えるものである。
実行例:
http://www.coins.tsukuba.ac.jp/~yas/syspro/cgi-examples/cgi-printarg.html
REQUEST_METHOD=GET SCRIPT_NAME=/~yas/syspro/cgi-examples/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=/~yas/syspro/cgi-examples/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/access_log
以下は、アクセスログの例である。
---------------------------------------------------------------------- 130.158.85.140 - - [23/Jun/2002:23:28:28 +0900] "GET /~yas/index-j.html HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:0.9.4.1) Gecko/20020508 Netscape6/6.2.3" 130.158.85.140 - - [23/Jun/2002:23:28:28 +0900] "GET /~yas/images/shinjo-picture-2002-05.jpg HTTP/1.1" 304 - "http://www2.coins.tsukuba.ac.jp/~yas/index-j.html" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:0.9.4.1) Gecko/20020508 Netscape6/6.2.3" 130.158.85.140 - - [23/Jun/2002:23:28:28 +0900] "GET /~yas/images/mt-tsukuba-3-1996-05-10.gif HTTP/1.1" 304 - "http://www2.coins.tsukuba.ac.jp/~yas/index-j.html" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:0.9.4.1) Gecko/20020508 Netscape6/6.2.3" ----------------------------------------------------------------------1行が1つのページの転送を意味する。左端は、クライアントの IP アドレス である。どのような要求が来たか、クライアントのソフトウェアなどがわかる。 自分が単にWWWブラウザを使っているだけでも、さまざまな情報をサーバに 送っているとに注意しなさい。
アクセスログの他に、エラーが起きた時にもログが作られる。これをエラーロ グという。エラーログは、情報学類のサーバ www (orchid-a) や www2 (orchid-e) では、次の場所にある。
/var/log/httpd/error_log
CGI のプログラムをデバッグする時には、このエラーログを見て原因を探る。
GET の場合、環境変数 REQUEST_METHOD に GETと設定し、環境変数 QUERY_STRING にパラメタを設定する。
POST の場合、まず、データをファイルに保存しておく。そのバイト数を wc コマンドなどで調べておく。環境変数 REQUEST_METHOD に POST と設定する。 環境変数 CONTENT_LENGTH にデータファイルの大きさを設定する(ここでは最 後の改行を取り除いたバイト数を指定している)。---------------------------------------------------------------------- % setenv REQUEST_METHOD GET% setenv QUERY_STRING 'lastname=name1&firstname=name2&sex=Male&email=who@dom'
% ./cgi-printarg.cgi
Content-Type: text/html <HTML><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></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><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></HTML> %
----------------------------------------------------------------------
rw-rw-rw-
や 777
rwxrwxrwx
にする)。このため、そのコンピュータにログイン
でき人ならば、そのファイルの名前を知っていれば、データを取り出したり内
容を破壊したりできる。
suEXEC を使うと、その逆になる。
普通のプログラミング言語は、アプリケーション・プログラム本体を記述する 時に使われる。 これに対して、 スクリプト言語 は、アプリケーション本体で はなく、アプリケーションの細かな動作を変更したり、アプリケーション本体 を変更することなく機能を追加したりするために使われる言語である。 普通のプログラミング言語は、アプリケーション・プログラマにより使われ、 コンパイラで機械語に変換されるので、実行時には機械語しか残っていない。 これに対して、スクリプト言語は、アプリケーションのユーザや、システム管 理者などによって使われ、プログラムは、アプリケーションに組み込まれた インタプリタで解釈実行される。 スクリプト言語を使うと、単に変数を設定することに比べて、 高度な機能拡張が機能になる。
スクリプト言語の例:
---------------------------------------------------------------------- % cat ./cgi-hello-sh.cgi#!/bin/sh cat <<EOF Content-Type: text/html <HTML> <H1>hello.</H1> </HTML> EOF % ./cgi-hello-sh.cgi
Content-Type: text/html <HTML> <H1>hello.</H1> </HTML> %
----------------------------------------------------------------------
利用者が信頼していると設定しているホストにクロスサイトスクリプティング 攻撃への脆弱性があると、悪意をもった別のサイトから危険なスクリプトを実 行させることができる。
クライアントから送られてきた文字列は、必ず検査してから使う。 「」のようなタグがないかどうかを調べる。不用意に同じ文字列 を送り返してはいけない。送り返す時には、html_escape() のような方法で必 ずエスケープする。
| & ; && || `
」などが含
まれていた場合、意図しないプログラムが実行されることがある。
アクセスカウンタなどデータ・ファイルを書き換えるような CGI プログラム の場合、複数のプログラムが同時に実行された時のことを考慮する必要がある。 必要に応じて、ファイルのロックを行うなどして、一度に1つのプロセスしか プログラムを実行しないようにする。このことを、相互排除という。
相互排除について詳しくは、2学期のオペレーティング・システムで扱う。 特に、扱うべき資源が複数になった場合には、デッドロックに気をつける。
Unix では、ファイルに対するロックを扱うために、次のような機能を提供し ている。
セッション管理には、次のような方法がある。
セッション管理にクッキーを使う方法は、正しく使えば比較的安全である。し かし、クッキーはセッションを越えて有効なもの、異なるサイトに送られるも のも定義できる。また、クッキーにパスワードを埋め込むという幼稚で危険な 実装も時々見受けられる。
セッションは、認証とは別である。同じ人が複数のセッションを実行すること もある。
普通のWWWサーバでは、要求を送ってきたコンピュータのIPアドレスを記 録しているので、コンピュータ単位でのアクセス状況を記録することはできる が、個人を特定することはできない。
クッキーを利用することにより、コンピュータではなくどの個人がアクセスし てきたかを記録することができる。
クッキーから電子メールのアドレスや氏名まで調べることはできない。 しかし、インターネットをサーフしている間にどこかでそれを打ち込んだが最 後、クッキーと電子メール・アドレスや氏名との対応が記録されてしまう危険 性がある。
参考
Netscape社によるWWWにおけるクッキー実現の案
http://www.netscape.com/newsref/std/cookie_spec.html
SSI が使えるシステムでは、file.html
の変わりに
file.shtml
とすると、SSI の機能が働くようになる。
例1: ファイルの内容の読込み
<!--#include file="filename"-->例2: プログラム
/bin/cal
の実行結果の埋め込み
<PRE> <!--#exec cmd="/bin/cal"--> </PRE>例3:ファイル
file.shtml
の更新時刻の表示
最終に変更されたのは、 <!--#config timefmt="%Y/%m/%d %H:%M:%S"--> <!--#flastmod file="file.shtml"--> です。
timefmt
の形式は、
ライブラリ関数 strftime()
と同じ。
その他に、ファイルの大きさを得るための#fsize file="..."
,
CGI を実行する #cgi vmf="..."
,環境変数を表示する
#echo var="..."
,がある。
`#include'
や
`#exec'
で、大事なファイルが盗まれないように、細心の注意を払ながら利用する必要
がる。
画面に何かを表示する機能があり、WWWブラウザ上で動く。 Javaアプレットを使うと、アニメーションを作ったり、 CGI を使わずにユーザとの対話できる。
CGI では、プログラムは、WWWサーバが走っているコンピュータで動くが、 Javaアプレットは、プログラムはブラウザの上で動く。
<APPLET></APPLET>
。
HTML記述:
<APPLET CODE="rctext.class" WIDTH="500" HEIGHT="50"> <PARAM NAME="message" VALUE="hello,world"> 与えたメッセージが動き回るJavaアプレット。 </APPLET>表示例:
CODE
属性で、Javaアプレットを保存している
ファイルのURL(ただし、同一ホスト内)、
WIDTH
属性で、幅、
HEIGHT
属性で、高さを指定する。
<PARAM>
タグを使えば、アプレットに
オプションを渡すことがでる。
<APPLET>
と
</APPLET>
の間には、
Java Applet に対応していないブラウザのためのテキストを書く。
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 の
ドキュメントを生成して、それをブラウザに表示させることもできる。
アクセス制御(access control) ユーザ(アクセスの主体) が、ファイルやWWWページなどの資源をアクセスする時、どんな アクセスの仕方なら正しいということを定義して、それがきちんと守られてい ることをということを保証することである。
WWW ページのアクセス制御は、次のような情報がよく使われる。
アクセス制御は、基本的には、WWWサーバの管理者の仕事である。
WWWサーバ (httpd) の設定ファイル
(
/usr/local/etc/httpd/conf/httpd.conf
,
/var/www/conf/httpd.conf
など
)
に記述される。
個人のページについては、
.htaccess
というファイルを作れば、個人で変更できるようになっている場合がある。
.htaccess: ---------------------------------------------------------------------- order deny,allow deny from all allow from 130.158.0.0/16 192.50.17.0/24 ----------------------------------------------------------------------
例:パスワード・ファイル
/home/lab/Denjo/yas/etc/passwd-doc1
に登録されているユーザだけが、
.htaccess
があるディレクトリ以下にあるファイルをアクセスできる。
.htaccess: ---------------------------------------------------------------------- AuthType Basic AuthName "Members Only" AuthUserFile /home/lab/Denjo/yas/etc/passwd-doc1 require valid-user ----------------------------------------------------------------------このパスワード・ファイルは、サーバ(www,www2)上で htpasswd というプログ ラムで作る。
一番最初は、---------------------------------------------------------------------- % ls /home/lab/Denjo/yas/etc/passwd-doc1ls: /home/lab/Denjo/yas/etc/passwd-doc1: No such file or directory % htpasswd -c /home/lab/Denjo/yas/etc/passwd-doc1 user1
Adding password for user1. New password:user1のパスワードを打ち込む
Re-type new password:user1のパスワードを打ち込む
% htpasswd /home/lab/Denjo/yas/etc/passwd-doc1 user2
Adding user user2 New password:user2のパスワードを打ち込む
Re-type new password:user2のパスワードを打ち込む
% cat /home/lab/Denjo/yas/etc/passwd-doc1
user1:1fjr1tHIgoG7U user2:qXaeA9Zge7Yqc %
----------------------------------------------------------------------
-c
オプション付で実行する。passwd
コマ
ンドと同様に、打ち込んだパスワードは、画面には表示されず、また、確
認のために2回打つ必要がある。
htpasswd コマンドの結果、次のようなファイルが作られる。
ユーザ名と暗号化されたパスワードから構成される。 このファイルは、~/public_html 以外の場所に置くと GET で盗まれることは ない。---------------------------------------------------------------------- user1:1fjr1tHIgoG7U user2:qXaeA9Zge7Yqc ----------------------------------------------------------------------
このアクセス制御が有効なファイルをクライアントが GET したとする。
すると、サーバから次のようなエラーが返される。GET /dir1/file1.html HTTP/1.1![]()
![]()
HTTP/1.1 401 Authorization Required WWW-Authenticate: Basic realm="Members Only" Content-Type: text/html401 Authorization Required ...
.htaccess
の AuthType
と AuthName
の内容が、HTTP の応答の WWW-Authenticate:
に現れる。
このページをアクセスするには、クライアント(WWWブラウザ)は、ウインドウ を開いてユーザにユーザ名とパスワードを要求する。そして、次のような GET 要求をもう一度送る。
GET /dir1/file1.html HTTP/1.1Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
![]()
![]()
Authorization:
には、ユーザが打ち込んだユーザ名とパスワードが
Base64 (64進数)で符合化されて含まれている。
これを受け取ったサーバは、
AuthUserFile
で指定されたファイルを開いて、ユーザ名とパスワー
ドパスワードを照合して、正しければアクセスを許する。
Base64 は、「暗号ではない」ので、簡単に元にもどせる。このAuthType Basic を使うアクセス制御の方法では、パスワードがそのままネットワークを 流れてしまうので、盗聴に弱い。
WWW ブラウザや telnet を使って得た結果と /var/www/htdocs/ 以下のファイ ルが同一であることをwww に ssh でログインして確認しなさい。
同様に www2 についても調べなさい。
/var/log/httpd/access_log
というファイルにためられる。
これを、次のようにして観察しなさい。
% tail -f /var/log/httpd/access_log![]()
http://www.coins.tsukuba.ac.jp/
や自分の WWW ページを開く。
次の CGI プログラムに対して、フォームを定義し、何かデータを送ってみなさい。 http://www2.coins.tsukuba.ac.jp/~yas/syspro/cgi-examples/cgi-printarg.cgi
ヒント:2つの引数をフォームでクライアントからサーバへ送る。たとえば、 "arg1=10&arg2=20"のように送るようなフォームを作成する。 cgi-printarg.cで、余計な表示をなくす。 make_qcqv() で分解した後、"arg1=" や "arg2=" があるパラメタを検索する。 そのなかで、atoi() 等で数にもどし、足算を行う。そして、結果を printf() の %d などで表示する。
余裕があれば、メニューやラジオボタンなどで足算以外の計算もできるように しなさい。
% calJune 2002 Su Mo Tu We Th Fr Sa 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 %
![]()
ヒント: proc-create.c をCGI のプログラムに改造する。2002年5月でもなく、2002年6月でもなく、そ の CGI プログラムを実行した月のカレンダーを表示しなさい。固定した表示 するだけなら、CGI の意味はない。余計な表示は省略すること。
余裕があれば、今月のカレンダーではなく、任意の年・月のカレンダーを表示 しなさい。
この課題では、system() や popen() を用いてはならない。この課題に限らず、 もし、遠隔から任意のコマンドを実行可能なような CGI プログラムを作成し た時には、単位を出さない。
CGI プログラムが、どのユーザの権限で動作しているかを調べるプログラムをつくりなさい。
ヒント:プロセスのユーザ属性を表示するプログラム を、CGI から実行させる。あるいは、id コマンドを実行させる。
特定のホスト(たとえば、adonis10)からだけしかアクセスできないような .htaccess を定義しなさい。
注意:ユーザ名は、長くてもよい。Unix や Windows のユーザ名とは別のもの でよい。
注意:ここで設定したパスワードは、ネットワーク上を暗号化されずに流れる。 けっして Unix や Windows のログインのパスワードと同じものを使ってはな らない。