【情報処理安全確保支援士試験 令和5年度 春期 午後1 問1 No.1】

情報処理安全確保支援士試験 令和5年度 春期 午後1 問1

【出典:情報処理安全確保支援士試験 令和5年度 春期 午後1 問1(一部、加工あり)】

問1 Webアプリケーションプログラム開発に関する次の記述を読んで、設問に答えよ。

 G社は、システム開発を行う従業員100名のSI企業である。このたび、オフィス用品を販売する従業員200名のY社から、システム開発を受託した。開発プロジェクトのリーダーには、G社の開発課のD主任が任命され、メンバーには、開発課から、Eさんと新人のFさんが任命された。G社では、セキュリティの品質を担保するために、プログラミング完了後にツールによるソースコードの静的解析を実施することにしている。

[受託したシステムの概要]
 受託したシステには、Y社の得意先がオフィス用品を注文する機能、Y社とY社の得意先が注文履歴を表示される機能、Y社とY社の得意先が注文番号を基に注文情報を照会する機能(以下、注文情報照会機能という)、Y社とY社の得意先が納品書のPDFファイルをダウンロードする機能などがある。

[ツールによるソースコードの静的解析]
 プログラミングが完了し、ツールによるソースコードの静的解析を実施したところ、Fさんが作成した納品書PDFダウンロードクラスのソースコードに問題があることが分かった。納品書PDFダウンロードクラスのソースコードを図1に、静的解析の結果を表1に示す。

 この解析結果を受けて、Fさんは、Eさんの指導の下、ソースコードを修正した。表1の項番1について図1の8行目から11行目を図2に示すソースコードに修正した。項番2と項番3についてもソースコードを修正した。

 再度、ツールによるソースコードの静的解析が実施され、表1の指摘は解消していることが確認された。

a:13

脆弱性「ディレクトリトラバーサル」、指摘箇所「a」、指摘内容「ファイルアクセスに用いるパス名の文字列作成で、利用者が入力したデータを直接使用している。」
 ファイルアクセスに関する内容を図1のソースコードから探すと、13行目のFile〜が該当します。
File fileObj = new File(PDF_DIRECTORY + “/” + clientCode + “/” + “DeliverySlip” + inOrderNo + “.pdf”);
(省略)//PDFファイルが既に存在しているかの確認など

 新しくファイルを取り扱うのにファイルのパス名を指定しているようです。
 ()内を確認していくと、PDF_DIRECTORYは2行目に「private static final String PDF_DIRECTORY = “/var/pdf”; //PDFディレクトリ定義」とあるように「/var/pdf」と定義されています。
 clientCodeは12行目に「String clientCode = resultObj.getString(“client_code”); //得意先コード取得」とあるように「得意先コード」です。
 ”DeliverySlip”は「”」で囲まれていて、単なる文字列です。
 inOrderNoは、入力された注文番号でしょう。
 例えば、得意先コードが「111」、注文番号が「aaa」の場合、ファイルのパス名は「/var/pdf/111/DeliverySlipaaa.pdf」になります。
 これは、指摘内容にあるように、利用者が入力した注文番号=inOrderNo:aaaを直接パス名として使用しているため、ディレクトリトラバーサルの危険性があります。
 例えば、攻撃者が注文番号を「../../222//DeliverySlipbbb.pdf」とした場合、ファイルのパス名は「/var/pdf/111/DeliverySlipaaa.pdf../../222//DeliverySlipbbb.pdf」となります。
 相対パスである「../../」を変換すると、「/var/pdf/222//DeliverySlipbbb.pdf」となり、得意先コードを任意の値にして表示できてしまいます。

b:in

脆弱性「確保したリソースの解放漏れ」、指摘箇所「(省略)」、指摘内容「変数 stmt、変数 result0bj、変数(b)が指すリソースが解放されない。」
 リソースの解放漏れは、主にメモリを解放しないことでメモリ使用量が加算され異常状態になる、メモリリークという事象を引き起こします。
 変数 stmt、変数 resultObjとも、リソース解放処理としてstmt.close()、resultObj.close()が必要になります。
 変数 stmt、変数 resultObj以外の変数としては、14行目の「BufferedInputStream in」の「in」についても16行目「in.read(buf)」が最後の処理であり、解放処理「in.close()」が必要になります。

c:WHERE head.order_no = ?、d:PreparedStatement stmt = conn.prepareStatement(sql)

sql = sql + “(c)”;
 sql = sql + (省略); //抽出条件の続き
 (d);
 stmt.setString(1, inOrderNo);
 ResultSet resultObj = stmt.executeQuery();

 表1の項番1の脆弱性であるSQLインジェクションに対する修正内容です。
 元のソースコードは以下のとおりでした。
8: sql = sql + “WHERE head.order_no = ‘ ” + inOrderNo + ” ‘ “;
9: sql = sql + (省略); //抽出条件の続き
10: Statement stmt = conn.createStatement();
11: ResultSet resultObj = stmt.executeQuery(sql);
ディレクトリトラサーバルでもあったように、SQLを実行するのに利用者からの情報である「inOrderNo」を直接利用する8行目に問題がありそうです。
 SQLインジェクションでは、利用者に入力する情報に任意のSQL文を入力することで、不正なSQL文が実行されてしまいます。
 SQLインジェクションの対策としては、利用者の入力情報を文字列として扱い、SQL文として扱わない仕組みであるプレースホルダを利用します。
 Javaの場合は「PreparedStatement」というコードを使用します。
sql = sql + “WHERE head.order_no = ?“; ←パラメータを「?」(プレースホルダ)にする
sql = sql + (省略); //抽出条件の続き
PreparedStatement stmt = conn.prepareStatement(sql); ←「PreparedStatement」を作成
stmt.setString(1, inOrderNo); ←「1」が「?」を指定して、inOrderNoの値に変換
ResultSet resultObj = stmt.executeQuery(); ←「PreparedStatement」を使用してSQLクエリを実行