「資格試験/情報処理技術者試験/情報セキュリティスペシャリスト/過去問2013年春午後2/問1」の編集履歴(バックアップ)一覧に戻る

資格試験/情報処理技術者試験/情報セキュリティスペシャリスト/過去問2013年春午後2/問1 - (2013/08/28 (水) 01:13:11) のソース

* 問1 業務パッケージの開発に関する次の記述を読んで、設問1~4に答えよ。

 U社は、従業員数100名のソフトウェア開発会社で、以前は受託開発を主に行っていたが、数年前から業務パッケージの開発も手がけるようになり、業績を伸ばしている。今回、新たに美容室向けの自社製品として、表1の機能を備えた美容室管理システム(以下、HSSという)を開発することが決まり、M専務が開発責任者になった。M専務は、美容室がHSSを運用することは難しいと考え、U社がHSSを運用し、インターネット経由で美容室へサービスを提供する、いわゆるSaaS型の商品とすることにした。また、美容室間の情報の独立性を高めるために、美容室ごとに仮想サーバを用意し、その上でHSSを運用することにした。

** 表1 HSSの機能
|機能名称|機能概要|
|顧客管理|美容室の顧客の氏名、住所、注文履歴などの管理|
|予約管理|来店予約の管理|
|従業員管理|美容室の従業員の氏名、役職、勤務シフト、勤怠などの管理|
|売上管理|売上の管理|

[機能要件の定義]
 M専務は、HSSの要件定義から詳細設計までを担当するグループ(以下、Sグループという)を編成し、T主任をリーダに任命した。Sグループは、HSSの想定契約先に関する調査などを行い、機能要件の定義を行った。そのうち、顧客管理と予約管理に関する機能要件は、それぞれ図1、図2のとおりである。

#divclass(blackdiv){
・顧客データは、データベース内に格納する。
・顧客データのメンテナンスは、美容室内の管理端末から美容室の従業員が行う。ただし、一部の顧客データについては、Webサイトで顧客が変更できる。
・顧客データのうち、顧客から取得するものは次のとおりとする。取得は対面で行う。
・・氏名、住所、生年月日、性別、電話番号、メールアドレス、美容室に対する要望。
・顧客データのうち、システムが付与するものは次のとおりとする。
・・顧客ID、パスワードの初期値
・顧客は、Webサイトで、自分の顧客データわ閲覧できる。
(以下、省略)
}
** 図1 顧客管理の機能要件

#divclass(blackdiv){
・美容室の従業員は、美容室内の管理端末から、全ての顧客の来店予約ができる。
・顧客は、Webサイトで、自分の来店予約ができる。
・予約データはデータベース内に格納し、内容は次のとおりとする。
・・予約年月日、予約時刻、担当美容師、顧客ID、注文メニュー、美容室へのメッセージ
・予約に当たっては、担当美容師を指定する。
・予約に当たっては、注文メニューを指定する。
}
** 図2 予約管理の機能要件

 なお、HSSでは美容室の従業員及び顧客の利用時に認証を行う。

[システム・ソフトウェア要件定義]
 Sグループは、システム方式設計及びソフトウェア要件定義を開始し、まず図3のとおり、ソフトウェア構成を決定した。
 なお、図3中の太線枠内が今回HSSとして開発するWebアプリケーションである。

&ref(2013SPSCPM201_01.png)

[リスクアセスメント]
 M社専務は、HSSでは、美容室が顧客から取得した個人情報を含む情報資産を取り扱うことから、情報の漏えいや滅失の可能性を評価するためにリスクアセスメントを実施し、その結果に基づきソフトウェア要件の中でセキュリティ仕様を定めることにした。この作業を、ソフトウェア要件定義の他のタスクと並行して行うために、Sグループとは別のグループ(以下、Rグループという)を編成した。
 Rグループでは、次の(1)~(3)の手順に従ってリスクアセスメントを実施した。

>(1) 表2のように、美容室における情報資産の分類を定義した。
>(2) 表3のように、HSSで取り扱う情報資産を特定し、情報セキュリティの3要素に照らして情報資産の分類を行った。
>(3) 各情報資産に対する脅威と脆弱性を特定し、それぞれについてリスク分析を行い、その結果を取りまとめた。そのうち、顧客データに対する結果を表4に示す。

** 表2 美容室における情報資産の分類の定義
|分類|定義|
|I|・美容室の顧客に直接被害が及ぶ。&br()・美容室の存続に関わる程の被害が発生する。|
|II|・従業員や取引先に直接被害が及ぶ。&br()・美容室の営業に支障が発生する。|
|III|・直接的なは大きな被害や影響は発生しない。|

** 表3 HSSで取り扱う情報資産(抜粋)
|情報資産|概要|【a】に係る分類|完全性に係る分類|可用性に係る分類|
|顧客データ|顧客の氏名、顧客ID、パスワードなど|I|I|I|
|予約データ|予約状況に関する情報|I|I|I|
|従業員データ|従業員の氏名、従業員ID、パスワードなど|II|II|I|
|売上げデータ|販売状況に関する情報|III|II|III|

** 表4 顧客データに対するリスク分析の結果(抜粋)
|リスクNo.|脅威|脆弱性|被害|発生確率 1)|深刻度 2)|対応要否 3)|
|1|インターネット経由のHSSへの不正アクセス|【b】|情報漏えい|高|高|要|
|2|~|Webアプリケーションのセキュリティホール|情報漏えい|中|高|要|
|3|~|Webサーバ基板のセキュリティホール|情報漏えい|中|高|要|
|4|~|脆弱な認証とアクセス権管理の仕組み|情報漏えい|低|高|要|
|5|美容室従業員による物品持ち去り|物品管理の不備|情報へのアクセス不能|低|中|不要|
|6|~|【c】|情報へのアクセス不能|低|中|不要|
|7|美容室従業員による不正アクセス|【b】|情報漏えい|中|高|要|
|8|~|脆弱な認証とアクセス権管理の仕組み|情報漏えい|低|高|要|
|9|不正プログラムによる管理端末からの情報窃取|ウイルス対策ソフトの停止|情報漏えい|低|中|不要|
|10|~|未知のウイルスへの未対応|情報漏えい|低|中|不要|
注
 1) 脅威が脆弱性を悪用し被害が発生する確率
 2) 被害が発生した場合の、被害の深刻さ
 3) リスクに対応するために、HSSに管理策を施す必要性の有無

[セキュリティ仕様の決定]
 Rグループはリスク分析の結果から、Webアプリケーションに係るセキュリティ仕様とHSSの運用管理に関する要点を取りまとめた。そのうち、顧客管理機能に関するセキュリティ仕様を表5に、HSSの運用管理に関する要件を表6に示す。

** 表5 顧客管理機能に関するセキュリティ仕様(抜粋)
|機能|仕様|リスクNo. 1)|
|顧客の認証|・顧客の認証用パスワード設定時に、次の条件を強制する。&br() - 長さ:8文字以上&br() - 文字種:英字、数字、記号の3種類全てを含む。&br() - 別途指定する禁止文字列を含まない。&br()・パスワードの入力を所定回数連続して誤った場合【d】|1, 4|
|美容室の従業員の認証|・美容室の従業員の認証では、顧客の認証と同様の認証に加えて、クライアント証明書によるクライアント認証を実施する。|7, 8|
|Web画面入出力|・Web画面からの入力については、全て値チェックを行う。&br()・Web画面の出力時には、エスケープ処理を行う。&br()(以下、省略)|【e】|
|権限管理|・次のロールを定義し、最小権限を与える。&br() - HSSのシステム管理者&br() - HSSのデータ管理者&br() - 美容室の従業員&br() - 顧客|1, 4, 7, 8|
注 1) リスクNo.は表4中のリスクNo.のうちで、該当するものを示す。

** 表6 HSSの運用管理に関する要件(抜粋)
|適用対象|要件|リスクNo. 1)|
|インターネットからのアクセスを受け付ける回線|・ファイアウォールを設置し最小限のアクセスだけを許可する。&br()・IPSを設置し、不正アクセスの検出と、自動遮断を行う。&br()・Webアプリケーションファイアウォールを設置し、不正なHTTPアクセスを遮断する。|【f】|
|インターネットからのアクセスを受け付けるWebサーバ|・【g】|3|
注 1) リスクNo.は表4中のリスクNo.のうちで、該当するものを示す。

 Rグループは検討したセキュリティ仕様と運用管理に関する要件をM専務に報告し、その承認を受けた。

[予約管理機能の不具合]
 その後、HSSの開発は順調に進み、予約管理機能のうち、顧客が操作する画面の機能テストを開始した。予約管理機能の画面遷移を図4に示す。
#divclass(blackdiv){
画面1 ログイン
&ref(2013SPSCPM201_02.png)
・顧客IDとパスワードを入力して、”ログイン”ボタンを押す。
・ログインが成功したら、メインニュー(画面省略)が表示される。
・メインメニューで予約を選択すると、予約日選択画面(画面省略)が表示される。
・予約日選択画面で予約希望日を選択すると、画面2が表示される。

画面2 予約状況
&ref(2013SPSCPM201_03.png)
・各予約枠内で、○は仮予約状態又は空き状態を、×は予約済み状態を示す。
・○を選択するとサーブレット”WakuClick”が呼び出される。
・その予約枠が空き状態であれば仮予約状態とした後に、画面3が表示される。
・予約枠が仮予約状態の場合、仮予約の有効期間(10分間)を確認し、有効期間を過ぎていれば、この顧客の仮予約に置き換えた後に、画面3が表示される。有効期間内の場合は予約失敗の画面(画面省略)が表示される。

画面3 本予約
&ref(2013SPSCPM201_04.png)
・注文メニュー項目は、所定の選択肢から一つを選択する。
・美容室へのメッセージ項目は、400字まで自由に入力できる。
・”予約する”ボタンを押すと、サーブレット”KakuteiClick”が呼び出されて予約確定になり、画面4が表示される。

画面4 予約確定
&ref(2013SPSCPM201_05.png)
・予約確定の画面が表示される。
・”戻る”ボタンを押すと、メインメニューに戻る。
}
** 図4 予約管理機能の画面遷移

 機能テストの結果、複数の予約を同時に処理した場合、画面上では正常に予約が完了したように見えるにもかかわらず、データベースには反映されていないことがあるという不具合が発見された。不具合の原因と思われるプログラムは、図5、図6のとおりである。また、データベースのテーブルは、図7、図8のとおりである。

** 図5 サーブレット"WakuClick"
#divclass(blackdiv){{
&nbsp(1)1: package jp.co.u_sha.hssystem;
&nbsp(1)2: 
&nbsp(1)3: import java.io.IOException;
&nbsp(1)4: import javax.servlet.ServletException;
&nbsp(1)5: import javax.servlet.http.*;
&nbsp(1)6: import java.sql.Connection;
&nbsp(1)7: import java.sql.PreparedStatement;
&nbsp(1)8: import java.sql.ResultSet;
&nbsp(1)9: import java.util.Date;
10: 
11: public class WakuClick extends HttpServlet {
12: &nbsp(4)(省略)[initメソッド内に適切な初期化処理を記述]
13: 
14:&nbsp(4) static final int RSV_AKI = 0;
15:&nbsp(4) static final int RSV_KARI = 1;
16:&nbsp(4) static final int RSV_KAKUTEI = 2;
17:&nbsp(4) static final long TIMEOUT_KARI = 600000;
18: 
19:&nbsp(4) protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
20:&nbsp(4)&nbsp(4) Connection conn;
21:&nbsp(4)&nbsp(4) PreparedStatement psSel, psUp;
22: 
23:&nbsp(4)&nbsp(4) (省略)[DriverManagerからConnectionインスタンスを取得し、その参照をconnに代入する]
24:&nbsp(4)&nbsp(4) psSel = conn.prepareStatement("SELECT * FROM rsvList WHERE rsvDate=? AND rsvTime=? AND Biyoshi=?");
25:&nbsp(4)&nbsp(4) psUp = conn.prepareStatement("UPDATE revList SET Status=?, CustID?, Message=?, Menu=?, LastUpdate=? WHERE rsvDate=? AND rsvTime=? AND Biyoshi=?");
26: 
27:&nbsp(4)&nbsp(4) // サーブレットのセッション情報から、顧客ID情報を取得する
28:&nbsp(4)&nbsp(4) HttpSession session = request.getSession(false);
29:&nbsp(4)&nbsp(4) if (session == null) {
30:&nbsp(4)&nbsp(4)&nbsp(4)y (省略)[ログイン未処理のエラーのHTML出力を行って終了する]
31:&nbsp(4)&nbsp(4) }
32:&nbsp(4)&nbsp(4) String logintUserID = (String)session.getAttribute("userID"); // ログイン時の顧客ID
33:&nbsp(4)&nbsp(4) // 選択された予約枠の情報を取得する
34:&nbsp(4)&nbsp(4) String rsvDate = requset.getParameter("rsvDate"); // 予約枠(予約年月日)
35:&nbsp(4)&nbsp(4) String rsvTime = requset.getParameter("rsvTime"); // 予約枠(予約時刻)
36:&nbsp(4)&nbsp(4) String rsvBiyoshi = requset.getParameter("Biyoshi"); // 予約枠(担当美容師)
37:&nbsp(4)&nbsp(4) (省略)[前出の四つの変数の入力値チェックを行う]
38:&nbsp(4)&nbsp(4) (省略)[入力値チェックで問題があったら、エラーのHTML出力を行って終了する]
39:
40:&nbsp(4)&nbsp(4) // 選択された予約枠の予約状況を参照する
41:&nbsp(4)&nbsp(4) psSel.setString(1, rsvDate); psSel.setString(2, rsvTime); psSetl.setString(3, rsvBiyoshi);
42:&nbsp(4)&nbsp(4) ResultSet rs = psSel.executeQuery();
43:&nbsp(4)&nbsp(4) rs.next();
44:&nbsp(4)&nbsp(4) long lastDateTime = rs.getLong("LastUpdate");
45:&nbsp(4)&nbsp(4) // 現在の時刻を取得する
46:&nbsp(4)&nbsp(4) long nowDateTime = new Date().getTime();
47:&nbsp(4)&nbsp(4) // 既に仮予約状態であった場合、仮予約の最終更新日時を確認し、TIMEOUT_KARI経過後なら空きにする
48:&nbsp(4)&nbsp(4) int rsvStatus = rs.getInt("Status");
49:&nbsp(4)&nbsp(4) if (rsvStatus == RSV_KARI) {
50:&nbsp(4)&nbsp(4)&nbsp(4) if ((nowDateTime - lastDateTime) > TIMEOUT_KARI) {
51:&nbsp(4)&nbsp(4)&nbsp(4)&nbsp(4) rsvStatus = RSV_KARI;
52:&nbsp(4)&nbsp(4)&nbsp(4) }
53:&nbsp(4)&nbsp(4) }
54:&nbsp(4)&nbsp(4) // 予約状況が空きであることを確認し、仮予約処理を行う
55:&nbsp(4)&nbsp(4) if (rsvStatus == RSV_AKI) {
56:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setInt(1, RSV_KARI); psUp.setString(2, loginUserID); psUp.setString(3, "");
57:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(4, ""); psUp.setLong(5, nowDateTime);
58:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(6, rsvDate);
59:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(7, rsvTime); psUp.setString(8, rsvBiyoshi);
60:&nbsp(4)&nbsp(4)&nbsp(4) psUp.executeUpdate();
61:&nbsp(4)&nbsp(4)&nbsp(4) (省略)[図6の処理を引き継ぐために、選択した予約枠の情報をサーブレットのセッション情報に保存する]
62:&nbsp(4)&nbsp(4)&nbsp(4) (省略)[画面3のHTML出力を行う]
63:&nbsp(4)&nbsp(4) } else {
64:&nbsp(4)&nbsp(4)&nbsp(4) (省略)[予約失敗の画面のHTML出力を行う]
65:&nbsp(4)&nbsp(4) }
66:&nbsp(4)&nbsp(4) (省略)[後処理を行う]
67:&nbsp(4) }
68:&nbsp(4)&nbsp(4) (省略)[その他の必要なメソッド]
69: }
}}
注記1 SQL例外を含め、例外処理については省略している。省略した部分で必要とするクラスに関わるimportの記述も省略している。
注記2 (省略)[・・・]は、その位置に[]内に示す処理が記述されているが、省略していることを示す。
** 図5 サーブレット"WakuClick"終わり

** 図6 サーブレット"KakuteiClick"
#divclass(blackdiv){{
&nbsp(1)1: package jp.co.u_sha.hssystem;
&nbsp(1)2: 
&nbsp(1)3: import java.io.IOException;
&nbsp(1)4: import javax.servlet.*;
&nbsp(1)5: import javax.servlet.http.*;
&nbsp(1)6: import java.sql.Connection;
&nbsp(1)7: import java.sql.PreparedStatement;
&nbsp(1)8: import java.sql.ResultSet;
&nbsp(1)9: import java.util.Date;
10: 
11: public class KakuteClick extends HttpServlet {
12: &nbsp(4)(省略)[initメソッド内に適切な初期化処理を記述]
13:&nbsp(4) static final int RSV_AKI = 0;
14:&nbsp(4) static final int RSV_KARI = 1;
15:&nbsp(4) static final int RSV_KAKUTEI = 2;
16: 
17:&nbsp(4) protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
18:&nbsp(4)&nbsp(4) Connection conn;
19:&nbsp(4)&nbsp(4) PreparedStatement psSel, psUp;
20:
21:&nbsp(4)&nbsp(4) (省略)[DriverManagerからConnectionインスタンスを取得し、その参照をconnに代入する]
22:&nbsp(4)&nbsp(4) psSel = conn.prepareStatement("SELECT * FROM rsvList INNER JOIN custMaster ON rsvList.CustID=custMaster.CustID WHERE rsvDate=? AND rsvTime=? AND Biyoshi=?");
23:&nbsp(4)&nbsp(4) psUp = conn.prepareStatement("UPDATE revList SET Status=?, CustID?, Message=?, Menu=?, LastUpdate=? WHERE rsvDate=? AND rsvTime=? AND Biyoshi=?");
24:
25:&nbsp(4)&nbsp(4) // サーブレットのセッション情報から、選択された予約枠の情報を取得する
26:&nbsp(4)&nbsp(4) HttpSession session = request.getSession(false);
27:&nbsp(4)&nbsp(4) if (session == null) {
28:&nbsp(4)&nbsp(4)&nbsp(4)y (省略)[ログイン未処理のエラーのHTML出力を行って終了する]
29:&nbsp(4)&nbsp(4) }
30:&nbsp(4)&nbsp(4) String rsvDate = requset.getParameter("rsvDate"); // 予約枠(予約年月日)
31:&nbsp(4)&nbsp(4) String rsvTime = requset.getParameter("rsvTime"); // 予約枠(予約時刻)
32:&nbsp(4)&nbsp(4) String rsvBiyoshi = requset.getParameter("Biyoshi"); // 予約枠(担当美容師)
33:&nbsp(4)&nbsp(4) String logintUserID = (String)session.getAttribute("userID"); // ログイン時の顧客ID
34:&nbsp(4)&nbsp(4) // 本予約画面で入力された情報を取得する
35:String serviceMenu = request.getParameter("Menu"); // 選択されたメニュー
36:String toShop = request.getParameter("Message"); // 入力されたメッセージ
37:&nbsp(4)&nbsp(4) (省略)[前出の六つの変数の入力値チェックを行う]
38:&nbsp(4)&nbsp(4) (省略)[入力値チェックで問題があったら、エラーのHTML出力を行って終了する]
39:&nbsp(4)&nbsp(4) // 指定日時の予約状況を参照する
40:&nbsp(4)&nbsp(4) psSel.setString(1, rsvDate); psSel.setString(2, rsvTime);
41:&nbsp(4)&nbsp(4) psSetl.setString(3, rsvBiyoshi);
42:&nbsp(4)&nbsp(4) ResultSet rs = psSel.executeQuery();
43:&nbsp(4)&nbsp(4) rs.next();
44:&nbsp(4)&nbsp(4) int rsvStatus = rs.getInt("Status");
45:&nbsp(4)&nbsp(4) String rsvUserID = rs.getString("CustID");
46:&nbsp(4)&nbsp(4) // 現在の時刻を取得する
47:&nbsp(4)&nbsp(4) long nowDateTime = new Date().getTime();
48:&nbsp(4)&nbsp(4) // 予約状況が仮であり、自分の予約であることを確認する
49:&nbsp(4)&nbsp(4) if (rsvStatus == RSV_KARI && rsvUserID.equals(loginUserID)) {
50:&nbsp(4)&nbsp(4)&nbsp(4) // 予約状況を予約確定に変更する
51:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setInt(1, RSV_KARI); psUp.setString(2, loginUserID);
52:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(3, toShop); psUp.setString(4, serviceMenu);
53:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setLong(5, nowDateTime); psUp.setString(6, rsvDate);
54:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(7, rsvTime); psUp.setString(8, rsvBiyoshi);
55:&nbsp(4)&nbsp(4)&nbsp(4) psUp.executeUpdate();
56:&nbsp(4)&nbsp(4)&nbsp(4) psSel.setString(1, rsvDate); psSel.setString(2, rsvTime);
57:&nbsp(4)&nbsp(4)&nbsp(4) psSetl.setString(3, rsvBiyoshi);
58:&nbsp(4)&nbsp(4)&nbsp(4) rs = psSel.executeQuery();
59:&nbsp(4)&nbsp(4)&nbsp(4) (省略)[rs内のデータを基にして図4の画面4(予約確定)のHTML出力を行う]
60:&nbsp(4)&nbsp(4) } else {
61:&nbsp(4)&nbsp(4)&nbsp(4) (省略)[予約失敗の画面のHTML出力を行う]
62:&nbsp(4)&nbsp(4) }
63:&nbsp(4)&nbsp(4) (省略)[後処理を行う]
64:&nbsp(4) }
65:&nbsp(4) (省略)[その他の必要なメソッド]
66: }
}}
注記1 SQL例外を含め、例外処理については省略している。省略した部分で必要とするクラスに関わるimportの記述も省略している。
注記2 (省略)[・・・]は、その位置に[]内に示す処理が記述されているが、省略していることを示す。
** 図6 サーブレット"KakuteiClick"終わり

#divclass(blackdiv){{
rsvList[予約リスト] (&u(){rsvDate[予約年月日]}, &u(){rsvTime[予約時刻]}, &u(){Biyoshi[従業員ID]}, CustID[顧客ID], Status[予約状態], Menu[注文メニュー], Messages[美容室へのメッセージ], LastUpdate[最終更新日時])

custMaster[顧客マスタ] (&u(){CustID[顧客ID]}, Name[氏名], Address[住所], Birth[生年月日], Gender[性別], Tell[電話番号], Mail[メールアドレス], Password[パスワード], LastUpdate[最終更新日時])
}}
注記 表記ルール次のとおり。下線は、主キーを表す。
>テーブル名[注釈] (&u(){列名1[注釈]}, &u(){列名2[注釈]}, 列名3[注釈], …, 列名n[注釈])
** 図7 主なテーブルの構造

#divclass(blackdiv){{
・rsvListテーブルには、毎月最初の営業日に、翌々月の予約枠に相当する行が全て空き状態として追加される。
・Statusに"0"が設定されている行は、空き状態を表す。その場合、CustIDには空文字列が設定される。
・Statusに"1"が設定されている行は、仮予約状態を表す。その場合、CustIDには仮予約を行った顧客の顧客IDが設定される。
・Statusに"2"が設定されている行は、予約状態を表す。その場合、CustIDには予約を行った顧客の顧客IDが設定される。
・Biyoshiには、顧客が指定した美容師の従業員IDが設定される。
・ある行のいずれかの列に更新があった場合、その時刻をLastUpdateに設定する。時刻の単位はミリ秒であり、LastUpdateの型はlong型である。
・LastUpdateには初期値として"0"が設定される。
}}
** 図8 予約リストテーブルの仕様

 不具合の発見を受けて、図5及び図6について、顧客αと顧客βがそれぞれのブラウザから操作を行うことを想定して、ソースコードレビューを行った。
 その結果、顧客αと顧客βが、画面2で空き状態であった同一の予約枠をほぼ同時に選択した場合、表7に示す順序で各処理が完了すると、顧客αの仮予約がデータベースに反映されないことが確認できた。

** 表7 仮予約が無効になる処理の順序
|順序|対象の顧客|処理|
|1|α|図5の42行目|
|2|β|図5の【 h 】行目|
|3|【 i 】|図5の【 j 】行目|
|4|【 k 】|図5の【 l 】行目|

 更に、&u(){①空き状態であった予約枠を、顧客αが画面2で選択して画面3に進むのと並行して、同一の予約枠を顧客βが画面2で選択した場合、表8に示す順序で各処理が完了すると、顧客βの個人情報が顧客αの画面4に表示されてしまう}ことが確認できた。

** 表8 個人情報の漏えいにつながる処理の順序
|順序|対象の顧客|処理|備考|
|1|α|図5の60行目||
|2|α|図6の42行目|順序1の処理が完了し、10分以上経過してから実行される。|
|3|β|図【m】の【n】行目||
|4|α|図【o】の【p】行目||
|5|β|図【q】の【r】行目||
|6|α|図6の58行目||

[予約管理機能における不具合の修正]
 Sグループのリーダとしてソースコードレビューに参加していたT主任は、この不具合を修正するために、executeUpdateメソッドでUPDATE文を実行した時、更新した行数が戻り値になることに着目した。そして、図5の25行目のパラメタ付きSQLのWHERE句を修正した上で、図5の60行目のexecuteUpdateメソッドの実行時に異常の発生を検知する仕組みを提案した。この提案に基づき修正されたプログラムは図9のとおりである。

** 図9 修正されたサーブレット"WakuClick"
#divclass(blackdiv){{{
1~23:(省略)[図5の1~23と同じ]
24:&nbsp(4)&nbsp(4) psSel = conn.prepareStatement("SELECT * FROM rsvList WHERE rsvDate=? AND rsvTime=? AND Biyoshi=?");
25:&nbsp(4)&nbsp(4) psUp = conn.prepareStatement("UPDATE revList SET Status=?, CustID?, Message=?, Menu=?, LastUpdate=? WHERE rsvDate=? AND rsvTime=? AND Biyoshi=?&u(){ AND CustID=? AND 【s】}");
26: 
27~46:(省略)[図5の27~46と同じ]
47:&nbsp(4)&nbsp(4) int rsvStatus = rs.getInt("Status");
48:&nbsp(4)&nbsp(4) &u(){String rsvCustID = rs.getInt("CustID");}
49:&nbsp(4)&nbsp(4) &u(){if (rsvStatus == RSV_KARI) {}
50:&nbsp(4)&nbsp(4)&nbsp(4) &u(){// 予約状況が空きであることを確認し、仮予約処理を行う}
51:&nbsp(4)&nbsp(4)&nbsp(4) &u(){(省略)[予約リストを適切に更新し、仮予約処理を行う]}
52:&nbsp(4)&nbsp(4) &html(<u>} else if (rsvStatus == RSV_KARI && ((nowDateTime - lastDateTime))&html(<u> > TIMEOUT_KARI)) {</u>)
53:&nbsp(4)&nbsp(4)&nbsp(4) &u(){// 既に仮予約状態であっても、TIMEOUT_KARI経過後なら、自分の仮予約に変更する}
54:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setInt(1, RSV_KARI); psUp.setString(2, loginUserID); psUp.setString(3, "");
55:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(4, ""); psUp.setLong(5, nowDateTime);
56:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(6, rsvDate);
57:&nbsp(4)&nbsp(4)&nbsp(4) psUp.setString(7, rsvTime); psUp.setString(8, rsvBiyoshi);
58:&nbsp(4)&nbsp(4)&nbsp(4) &u(){psUp.setString(9, rsvCustID); 【t】;}
59:&nbsp(4)&nbsp(4)&nbsp(4) &u(){if (psUp.executeUpdate() == 【u】) {}
60:&nbsp(4)&nbsp(4)&nbsp(4)&nbsp(4) (省略)[図6の処理を引き継ぐために、選択した予約枠の情報をサーブレットのセッション情報に保存する]
61:&nbsp(4)&nbsp(4)&nbsp(4)&nbsp(4) (省略)[画面3のHTML出力を行う]
62:&nbsp(4)&nbsp(4)&nbsp(4) &html(<u>} else {</u>)
63:&nbsp(4)&nbsp(4)&nbsp(4)&nbsp(4) {(省略)[予約失敗の画面のHTML出力を行う]}
64:&nbsp(4)&nbsp(4)&nbsp(4) &html(<u>}</u>)
65:&nbsp(4)&nbsp(4) } else {
66~71:(省略)[図5の64~69と同じ]
}}}
注記1 SQL例外を含め、例外処理については省略している。省略した部分で必要とするクラスに関わるimportの記述も省略している。
注記2 下線は、修正した部分を表す。
注記3(省略)[・・・]は、その位置に[]内に示す処理が記述されているが、省略していることを示す。
** 図9 修正されたサーブレット"WakuClick"終わり