筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2010/No10.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2010/
http://www.coins.tsukuba.ac.jp/~yas/
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 の注意すべき関数や式
Ruby の open() には、危険性がある。 C 言語のライブラリ関数 popen() と同じ動きをすることがある。
open("|cmd")
Ruby の注意すべき関数、式、クラス
IPA セキュア・プログラミング講座
http://www.ipa.go.jp/security/awareness/vendor/programming/
)
普通のプログラミング言語は、アプリケーション・プログラム本体を記述する 時に使われる。 これに対して、 スクリプト言語 は、アプリケーション本体で はなく、アプリケーションの細かな動作を変更したり、アプリケーション本体 を変更することなく機能を追加したりするために使われる言語である。 普通のプログラミング言語は、アプリケーション・プログラマにより使われ、 コンパイラで機械語に変換されるので、実行時には機械語しか残っていない。 これに対して、スクリプト言語は、アプリケーションのユーザや、システム管 理者などによって使われ、プログラムは、アプリケーションに組み込まれた インタプリタで解釈実行される。 スクリプト言語を使うと、単に変数を設定することに比べて、 高度な機能拡張が機能になる。
スクリプト言語の例:
$ 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>
$
プロセスはインタプリタの実行形式(機械語)から作られ、インタプリタのソー ス・プログラムは、そのプロセスが読み込む単なるデータとなる。
Unix には、スクリプトを簡単に実行する仕組みとして #!がある。
$ cat > run-cat
#!/bin/cat
hello
^D
$ chmod +x run-cat
$ ls -l run-cat
-rwxr-xr-x 1 yas 20 Sep 13 04:11 run-cat
$
「catインタプリタ」用のプログラムを実行する。
$ ./run-cat
#!/bin/cat
hello
$
これは次のように実行したものと同じになる。
$ /bin/cat ./run-cat
#!/bin/cat
hello
$
インタプリタの実行形式の絶対パス名: /dir/interpreter
そのソース・プログラムのファイル名: run
run
の内容:
#!/dir/interpreter arg-1 arg-2 arg-3 <以下、プログラム>このファイルに実行可能属性を付ける(
chmod +x
)と、
ファイル名を入力して実行することができる。
$ chmod +x ./run
$ ./run arg-a arg-b arg-c
<実行結果>
これは、次のようにインタプリタを起動したものと同じ結果になる。
$ /dir/interpreter ./run 'arg-1 arg-2 arg-3' arg-a arg-b argc
./run
ファイルに書いた引数は、シェルから実行する時場合には、
''で囲まれた時と同じような形式でインタプリタのプロ
セスへの引数として渡される。
#!
」行は、Unix のカーネルが解釈する。
シェルが、ファイルの先頭を読み、指定されたインタプリタを起動するのでは
ない。「#!
」行では、シェル変数、環境変数、エイリアスはつ
かえない。
スクリプト言語のプログラムでは
「#
」から始まる行がコメントであると都合がよい。
awk
や sed
のように、プログラムが含まれたファイルを指定
する時に -f
(program file) オプションが必要なものは、次の
ように、この行に -f
を付ける。
#!/usr/bin/awk -f { print }
シェル・スクリプトの先頭の
#!/bin/sh
や
#!/bin/csh
は、
そのインタプリタを起動するという意味である。
bash (Bourne-Again SHell) は、sh (Bourne Shell)の機能を強化したもの。 シェル・スクリプトを書く時には、多くのシステムで備わっている /bin/sh を 使うことも多い。(/bin/sh はあるが /bin/bash がないシステムもある。)
$ ls ~/Desktop | nkf -e
(デスクトップにあるファイルの一覧の表示。
漢字を含む名前のファイルも、nkf -e の働きで表示される。)
$
echo コマンド、history コマンド、あるいは、コピー&ペースト機能を用いて
端末から打ち込んだものを結果をファイルに保存する。以下の例では、echo コ
マンドを使ってファイルを作成している。
$ ls ~/Desktop | nkf -e
(^p で1行もどす。^a で、行頭に移動して 「echo '」と打ち、^e して
「' > ls-desktop」 と打つ)
$ echo 'ls ~/Desktop | nkf -e' > ls-desktop
$ cat ls-desktop
ls ~/Desktop | nkf -e
$ ls -l ls-desktop
-rw-r--r-- 1 yas prof 22 6 17 11:59 ls-desktop
$
不要な部分をエディタで削除すれば、シェル・スクリプトの完成である。この
例では不要な部分がないので、シェル・スクリプト ls-desktop は完成してい
る。完成したシェル・スクリプトを実行するには、次のようにbash コマンドの
引数としてファイル名を指定すればよい。
$ bash ls-desktop
(デスクトップの表示)
$
bash で動くものは、sh でも動くことが多い。
$ sh ls-desktop
(デスクトップの表示)
$
bash
」と打つのは煩わ
しいことがある。その場合は、次のようにする。
1行目にテキスト・エディタで #!/bin/bash
と書き加える。
$ emacs ls-desktop
(1行目に「#!/bin/bash
」と書き加える。)
$ cat ls-desktop
#!/bin/bash
ls ~/Desktop | nkf -e
$
chmod +x
で実行可能属性を付ける
$ ls -l ls-desktop
-rw-r--r-- 1 yas prof 34 6 17 12:09 ls-desktop
$ chmod +x ls-desktop
$ ls -l ls-desktop
-rwxr-xr-x 1 yas prof 34 6 17 12:09 ls-desktop
$
ファイル名を指定すると、実行することができる。ファイル名としては、明示
的に相対パス名であることを示すために先頭に「./
」を付けるか、ホー
ムディレクトリにあるこを示すために「~/
」を付ける(ホーム・ディレ
クトリにあることを仮定している)。
$ ./ls-desktop
(デスクトップの表示)
$ ~/ls-desktop
(デスクトップの表示)
$
~/bin
に置くと、他のコマンド(ls, cp,
emacs) 等と同じように実行することができる。
$ mkdir ~/bin
(注意: mkdir は、1度だけ実行すればよい。)
$ mv ls-desktop ~/bin
$ ls-desktop
(デスクトップの表示)
$
シェル・スクリプトを実行する時に、シェル・スクリプトに対して引数を与え ることができる。
ls-desktop では、「デスクトップ」だけ表示できる。これはこれで便利である が、他の漢字を含む任意のディレクトリを表示したいという要求もある。たと えば、「書類」や「ダウンロード」も表示できるようにしたい。そこで、「引 数」としてディレクトリ名を含むようにする。シェル・スクリプトの名前を 「ls-e」 とする。
$ cd ~/bin
$ ls -l ls-desktop
-rwxr-xr-x 1 yas prof 34 6 17 12:09 ls-desktop
$ cp ls-desktop ls-e
$ ls -l ls-e
-rwxr-xr-x 1 yas prof 34 6 17 13:16 ls-e
$ emacs ls-e
(修正)
$ cat ls-e
#!/bin/bash
ls $* | nkf -e
$ ls -l ls-e
-rwxr-xr-x 1 yas prof 27 6 17 13:15 ls-e
(cp の時点で x ビットは立っているので chmod +x は不要。)
$ ls-e ~/Desktop
(デスクトップの内容)
$ ls-e ~/Downloads
(「ダウンロード」の内容)
$
この例では、ls-desktop の「~/Desktop」を「$*」に置き換えている。「$*」
は、コマンドに与えられた全ての引数ということを意味している。
$1,$2,...
で参照できる。
$*
では、全ての引数を参照できる。
shift
コマンドで引数をずらすことができる。
$#
で引数の数がわかる。
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2010/2010-06-18/
コンピュータリテラシ/シェル・スクリプト
1: #!/usr/bin/ruby 2: # cgi-printarg-ruby.cgi -- CGI プログラムに対する引数を表示するプログラム 3: # ~yas/syspro/www/cgi-printarg-ruby.cgi 4: # Created on 2005/06/27 01:52:36 5: 6: require "cgi" 7: 8: def main() 9: $SAFE = 1 10: @cgi = CGI.new() 11: print_header() 12: print_content() 13: exit( 0 ) 14: end 15: 16: def print_header() 17: printf("Content-Type: text/html\n") 18: printf("\n") 19: end 20: 21: def print_content() 22: printf("<HTML><HEAD></HEAD><BODY><PRE>\n") 23: printf("request_method: %s\n",e(@cgi.request_method)) 24: printf("script_name: %s\n",e(@cgi.script_name)) 25: printf("query_string: %s\n",e(@cgi.query_string)) 26: printf("content_length: %d\n",@cgi.content_length) 27: qh = @cgi.keys 28: i = 0 29: qh.each { |name| 30: val = @cgi[name] 31: printf("qv[%d]: %s=%s \n",i,e(name),e(val) ) 32: i = i + 1 33: } 34: printf("</PRE></BODY></HTML>\n") 35: end 36: 37: def e( str ) 38: return( str == nil ? "" : CGI::escapeHTML(str) ) 39: end 40: 41: main()
Ruby では、require で、必要なライブラリを読み込む。 def から end までがメソッドの定義である。
main() では、CGI.new() により、CGI クラスのインスタンスを生成している。 その結果を @cgi という変数(インスタンス変数、mainの外でも使える)に保存 している。
このプログラムでは、main() という名前のメソッドを定義しているが、、 main() というメソッドから実行が開始させるわけではない。メソッド定義で はないものは、即座に実行される。このプログラムは最後に main() を呼び出 す文がある。これを忘れると何も実行されない。
$SAFE は、グローバル変数である。Ruby では、ファイルからの入力や環境変 数は汚染されたものとして扱われる。$SAFE を 1 にすると、汚染された文字 列でファイルを開くとエラーになる。標準では、0 。安全を確認したら、 obj.untaint() メソッドで汚染を解除する。明示的に obj.taint() で汚染さ せることもできる。
print_header()では、HTTP のヘッダのうち、Content-Type: 行だけを 出力している。
print_content() では、本文を出力している。
関数(メソッド) e() では、CGI ライブラリ (CGIクラス)の CGI::escapeHTML() を呼び出して、安全なものにして表示する。 たとえば、「<」は、「<」と変換している。こ れで、<SCRIPT> のような危険なスクリプトが送り込まれたとし ても「<SCRIPT>」と表示と表示されるだけで、スクリプト は実行されない。
@cgi に保存されたCGI クラスのインスタンスの request_method() メソッド を呼び出すと、"GET" か "POST" が返される。環境変数は、ハッシュ表 ENV に対して ENV['REQUEST_METHOD'] のようにしてもアクセスできるが、環境変 数を CGI クラスで変更してしまうこともあるようである。
@cgi.keys により、パラメタの一覧が配列の形で得られる。配列の各要素につ いて(qh.each)、パラメタ名を得て表示している。この例では、どんなパラメ タでも表示しているので、このようなループになっているが、通常の CGI プ ログラムでは、@cgi['パラメタ名'] のようにして、パラメタの値を文字列と して取り出すだけでよい。
表示例:
実行例:
request_method: GET script_name: /~yas/coins/syspro-2010/No10_files/cgi-printarg-ruby.cgi query_string: lastname=arg1&firstname=arg2&sex=Male&email=arg3 CONTENT_LENGTH: qv[0]: firstname=arg2 qv[1]: lastname=arg1 qv[2]: sex=Male qv[3]: email=arg3
request_method: POST script_name: /~yas/coins/syspro-2010/No10_files/cgi-printarg-ruby.cgi query_string: CONTENT_LENGTH: 48 qv[0]: firstname=arg2 qv[1]: lastname=arg1 qv[2]: sex=Male qv[3]: email=arg3
$ echo "a=b&c=d" | ./cgi-printarg-ruby.cgi
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
request_method:
script_name:
query_string:
CONTENT_LENGTH:
qv[0]: a=b
qv[1]: c=d
</PRE></BODY></HTML>
$
レポートには、最初に脆弱生がある CGI プログラムを作成した年月日、 今回修正した場所、および、その修正箇所の説明をつけなさい。
レポートには、最初に脆弱生がある CGI プログラムを作成した年月日、 今回修正した場所、および、その修正箇所の説明をつけなさい。
ヒント:strtol() のように、文字列から整数値を得るには、Ruby では、 Integer() を使って次のように行う方法がある。
s = "100" i = Integer(s)なお、文字列として不正なものを与えると Integer() は例外を発生する。 この課題では、正しい文字列が与えられると仮定して良いものとする。 例外が発生した時にはそれを補足することなく終了して良いことにする。
この課題では、 練習問題(904) CGIによる足算 と同様に、作成したプログラムをシェルから実行して動作を確認しなさい。
ヒント: CGI オブジェクトの params() メソッドを使い、配列の形で得る。
ヒント: CGI オブジェクトでは、ファイルの場合、StringIO クラスのオブジェ クトになる。IOクラスのオブジェクトのように、read(), each(), gets(), readline() といったメソッドが使える。