気ままに解説(H22春SC午後I問1)

平成22年度 春期 情報セキュリティスペシャリスト試験 午後I 問1
http://www.jitec.jp/1_04hanni_sukiru/mondai_kaitou_2010h22_1/2010h22h_sc_pm1_qs.pdf


スレッド間でリソースが競合する話。知識問題。
情報セキュリティスペシャリストの試験問題だが、Java(または、C#など)のプログラマであれば、(セキュリティの人でなくても)簡単。Servletを用いたプログラム(Strutsでもよいが)を書いている人にとっては常識*1
簡単に(オレオレな)解説を書いてみる。

はじめに

「本文」「設問文」「問題文」は、次の意味で用いる。

  • 「本文」とは、問1、問2などに続く一文の後から、設問の前まで
  • 「設問文」とは、設問の最初から最後まで
  • 「問題文」とは、問題の最初から最後まで(本文と設問文)

引用と解答例は、次のように表す。

  • 『 』は、問題文中からの引用箇所を示す
  • 解答例は、背景色グレーで示す

問題の解説

まずは、問題文全体をざっとながめてみる。Webアプリケーションそのものの問題(脆弱性)だろうとわかる。
次に、本文を読む。(上記の観点で)気になった箇所をメモ。

  • P.2、『システムの利用者IDには、6けたの数字からなる従業者番号を使用する』。よいと思う。
  • P.3、図2中の『勤務時間集計表をPDFファイルとして動的に作成し、HTTPサーバのWeb公開領域に保存する』仕様*2、、これが問われるんだろうな。
  • P.4、図3のプログラム。ファイルが、インスタンス変数(フィールド)で宣言されている。ダメだ。。これも問われるんだろうな。
  • P.5、[トラブルの発生と対策]。。やっぱり、、上の2つが問われていた。

その上で、設問に取り組む。設問文を読みながら、本文の必要な箇所を読み直す。

設問1

本文中より、『C言語又はC++言語で開発したプログラムにおいてよく発生するセキュリティ上の脆弱性』であり、対策は『プログラム上でメモリを使用する際の境界チェック』であるとわかる。まぁ、バッファオーバフローでしょ。

設問2

下線①の問題は、『25名の利用者が同時に勤務時間管理システムへアクセス』した時に発生している。そりゃそうだ。理由は次の2つ。

  • HttpServletのインスタンスは、シングルトンである(ただ一つ生成される)。StrutsではActionが該当する
  • インスタンス変数(フィールド)は、スレッド間で共有される

まぁ今回のプログラムでは、PDFDownloaderのインスタンスは1つだけ生成されて、利用者ごとのスレッドは、全てこのインスタンスを使うということ。PDFファイルを出力するためのファイルオブジェクトは、doGetメソッド内のローカル変数*3で十分なのに(であるように見えるのに)、わざわざインスタンス変数に参照させている。

(1)

設問文中『マニュアルどおりに操作したにもかかわらず、他人の勤務時間集計表をダウンロードしてしまった』のは、インスタンス変数tmpPDFが、スレッド間で共有されているからである。この旨が解答できていれば、(きっと)正解になるだろう。
本文中、図3のプログラムでは、次のようにして勤務時間集計表のPDFファイルを作成している。

  1. インスタンス変数tmpPDFに、新しいファイルインスタンスを生成する
  2. makePDFメソッドにより、tmpPDFに対してPDFファイルを出力する
  3. ファイル名tmpPDF.getName()で、レスポンスのリンクを出力する

1を実行後、2を実行する前に、別スレッドが1を実行した場合には、他の利用者のファイルに自分の勤務時間集計表が出力される。1を実行後、3を実行する前に、別スレッドが1を実行した場合には、自分のレスポンスに他の利用者の勤務時間集計表へのリンクが出力される。いずれにせよ、他の利用者の勤務時間集計表へのリンクが出力される。

従って『他人の勤務時間集計表をダウンロードしてしまった』のは、変数tmpPDFがスレッド間で共有され、他の利用者の勤務時間集計表へのリンクが出力されてしまったためである。

なお、利用者IDはHttpServletRequestオブジェクト(これはリクエストごとに生成される)から取得しているため、競合することはない。

(2)

ずばり、レースコンディション(競合状態)。

(3)

『図2に示した仕様は変更しないものとする』のは、U氏が指摘した根本解決ではなく、スレッド間の競合を解消する方法を問いたいからである。で、対策は、次の2つのいずれかである。

  1. そもそもプロセス間やスレッド間での共有リソースをなくす*4
  2. 共有リソースの同期をとる(ロックをかける)

今回は1。プログラムの変更内容は、変数tmpPDFを、doGetメソッドのローカル変数として定義することである。
2の方針で、synchronized修飾子やsynchronizedブロックにより同期化してもよいが、そうする必要がない*5

(4)

『どのようなHTTPリクエスト』かと言えば、「勤務時間集計表を作成するリクエストで、利用者IDが異なるもの」だろう。『どのようにWebサーバに送信すればよいか』といえば、「同時に複数のHTTPリクエストを送信する」だろう。他に答えようがないような。。なので、利用者IDの値が異なる、複数の勤務時間集計表を作成するリクエストを、同時に送信する。みたいな。

設問3
(1)

公開領域に、ファイル名が「利用者ID.pdf」で保存されているため、利用者ID(6けたの数字からなる従業者番号)がわかれば、容易にアクセスできるという話。ダウンロードする方法としては、PDFファイルのURLのうち、ファイル名の利用者ID部分を他人の利用者IDに変更してアクセスする

(2)

図4中に、『Javaサーブレット経由でだけアクセス可能な領域に保存する』とあるので、JavaサーブレットでPDFファイルを読み込み、レスポンス(HTTPのレスポンスデータ)として送信していることがわかる*6。このレスポンスは、『ブラウザのPDFプラグイン機能によって表示』される*7。よって、Javaサーブレットでは、PDF形式の勤務時間集計表のデータをブラウザへ送信する仕様に修正したとわかる。

おわりに

この問題は、新人研修のServlet/JSPの卒業試験によいかなと思った。あるいは、開発がスタートする前に、実装者に解いておいてもらうのもよいかも。

ちなみに、この問題にある脆弱性を排除するには、次が必要だと思う。

  • 勤務時間集計表が公開領域に保存されている件: 要件定義で、どんな情報を取り扱うのか、どのくらい保存するのか、誰がアクセスできるのかを決めておく必要がある
  • インスタンス変数にて、不要な共有リソースを作ってしまった件: 詳細設計時または実装時の問題である。詳細設計規約およびコーディング規約の作成、徹底*8、および遵守状況のチェックが必要である。この問題の場合は、FindBugsで検出できるので、チェックはけっこう楽

*1:でないと困る。

*2:要件によっては悪くないと思う。そもそもWebサーバ自体の利用者が限られていて、利用者全員が同一権限である場合。

*3:ローカル変数は、スレッドで共有されない(参照先がいっしょだったら、参照先のオブジェクトは共有されてしまうが)。メソッドが呼ばれるごとに、コンテキスト情報の一部としてスタックされる。もちろんスタックはスレッドごと。

*4:可能であるならば。設計ミスの場合など。

*5:カットオーバー直前で、ソースコードにできるだけ手を入れたくない場合には、こうするかもしれないが、、、いや、しないか。

*6:ファイルシステムではなく、直接レスポンスに書きだしてもよい。既存の勤務時間集計表作成プログラムを呼び出すため、ここでは一度ファイルにしている。

*7:.pdfに対するリクエストではないが、Javaサーブレットによってレスポンスヘッダに「Content-Type: application/pdf」を含める。

*8:これが一番大変かと。なんせ、色々なスキルの人がいるから(コストをおさえれば特に)。