情報応用演習Ⅰ(2024)

【T2d】Npgsqlを用いたデータベースへのアクセス(5/6)

プロジェクトタイプC#コンソールアプリ※
プロジェクト名T2d
ソリューション名PIT2
ターゲットフレームワーク.NET 8.0(長期的なサポート)
最上位レベルのステートメントを使用しない使用する(チェックオフ)

※ 「コンソールアプリ(.NET Framework)」ではないので注意せよ!

注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.

2d-5. 手動構築したSQL文の危険性

先ほどの例では,絞り込みなどを行わないSQL文を使用したがWHERE句を使って絞り込みを行うことももちろん可能である. 今回はキーボードから従業員番号を読み取って,それを絞り込みに使用することにしてみよう. Program.cs を_のように書き換える. あらかじめ言っておくと,この方法は期待どおり動作するが絶対にやってはいけない方法である. このやり方で覚えてはいけない.

Program.csの内容
 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
31
32
33
34
35
36
37
38
39
using Npgsql;

// ① 接続文字列
var connectionString = "Host=localhost;Port=5432;Database=SampleDB_hr;Username=hr_user;Password=n1z3Lo9AQap3";

//
// 表示する従業員の従業員番号をキーボードから入力させる.
// 
Console.Write("対象の従業員番号を入力: ");
string? targetNo = Console.ReadLine()?.Trim();
if (string.IsNullOrEmpty(targetNo)) Environment.Exit(1);

// ② 「接続」を表すオブジェクトの作成
using (var conn = new NpgsqlConnection(connectionString))
{
    conn.Open(); // 接続開始

    // ③ SQLコマンドの作成    
    var sqlStatement = @$"
SELECT jugyoin_no, sei, mei
  FROM tbl_jugyoin
 WHERE jugyoin_no = {targetNo}
 ORDER BY jugyoin_no ASC
";
    var cmd = new NpgsqlCommand(sqlStatement, conn);

    // ④ ↑のコマンドの実行
    var reader = cmd.ExecuteReader();

    // ⑤ ↑の実行結果の読み取り
    while (reader.Read())
    {
        var jugyoin_no = reader.GetInt32(0);
        var sei = reader.GetString(1);
        var mei = reader.GetString(2);

        Console.WriteLine($"jugyoin_no = {jugyoin_no}, sei = {sei}, mei = {mei}");
    }//while
}//using

書き換えたら実行してみよう.実行すると従業員番号の入力を求められるので適当な従業員番号(例えば100)を入力して Enterキーを押す.実行結果は_のようになる.

対象の従業員番号を入力: 100
jugyoin_no = 100, sei = 孫, mei = 正義
実行結果

_は,指定した従業員番号の従業員の氏名が表示されている.にもかかわらずなぜ 絶対やってはいけない方法なのだろうか.ではもう一度実行して キーボードから従業員番号を入力する代わりに, _の文字列を入力してみよう(コピペ推奨)

入力値
1
100; DELETE FROM tbl_shokureki; DELETE FROM tbl_bumon; DELETE FROM tbl_jugyoin; SELECT * FROM tbl_jugyoin

実行結果は_のようになる.

対象の従業員番号を入力: 100; DELETE FROM tbl_shokureki; DELETE FROM tbl_bumon; DELETE FROM tbl_jugyoin; SELECT * FROM tbl_jugyoin
jugyoin_no = 100, sei = 孫, mei = 正義
実行結果

一見何の問題もないように見えるはずであるが,これによって従業員データベースは破壊されてしまった

ではpgAdminで確認してみよう.【★】のクエリツールは開いたままになっているはずだが, 誤って閉じている場合には同じ手順で開きなおし,リスト2d-4-1のSQL文を実行してみよう. 結果は_のようになるはずである. また,同様にして職歴テーブルや(tbl_shokureki)や部門テーブル(tbl_bumon)の中身も同様に確認しておこう(__).

破壊された従業員データベース

これらのテーブルの中身が空になってしまったことがわかるだろうか. なぜこうなったのかというと_のSQL文に,意図しない文字列が差し込まれたためである. 当該部分を_に再掲する.

SQL文を構築する処理
1
2
3
4
5
6
7
8
    // ③ SQLコマンドの作成    
    var sqlStatement = @$"
SELECT jugyoin_no, sei, mei
  FROM tbl_jugyoin
 WHERE jugyoin_no = {targetNo}
 ORDER BY jugyoin_no ASC
";
    var cmd = new NpgsqlCommand(sqlStatement, conn);

これによって構築されるSQL文は_のようになるはずである.

SQL文を構築する処理
SELECT jugyoin_no, sei, mei
  FROM tbl_jugyoin
 WHERE jugyoin_no = targetNo
 ORDER BY jugyoin_no ASC

targetNoの部分が,キーボードの入力値で置き換えられるわけであるが,今回は_の ような入力値を使用した.したがって最終的に構築されるSQL文は_のようになってしまう.

SQL文を構築する処理
SELECT jugyoin_no, sei, mei
  FROM tbl_jugyoin
 WHERE jugyoin_no = 100; DELETE FROM tbl_shokureki; DELETE FROM tbl_bumon; DELETE FROM tbl_jugyoin; SELECT * FROM tbl_jugyoin
 ORDER BY jugyoin_no ASC

これに分かりやすいように改行を加えたものが_である.

SQL文を構築する処理(改行付き)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SELECT jugyoin_no, sei, mei
  FROM tbl_jugyoin
 WHERE jugyoin_no = 100;

DELETE FROM tbl_shokureki;

DELETE FROM tbl_bumon;

DELETE FROM tbl_jugyoin;

SELECT *
  FROM tbl_jugyoin
 ORDER BY jugyoin_no ASC

1行のSELECT文のつもりが,その後ろに3つのDELETE文が差し込まれている.最後のSELECT文はORDER BY句が孤立しないように つじつまを合わせるためのものである.

このような攻撃手法をSQLインジェクションといい,実際にウェブアプリケーションでもユーザーからの入力を必要とする 場面で問題になることが多い.このため_のように実行するSQL文を手動で組み立てるような実装は決して書いてはいけない

ここまで確認したら,先ほどの「Query Tool」で以下の復元用SQL文を実行し破壊されてしまったテーブルをもとの状態に戻しておこう.

Last updated on 2024-04-26
Published on 2024-04-26

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.