SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

伏石ちゃんは意図に反したい

DB接続エラーの原因? リソースリークに気をつけろ!【伏石ちゃんは意図に反したい】

伏石ちゃんは意図に反したい FILE 0x03 リソースリークで図書館ハック

解説:教えて那々子先生

先生
またお前か
充希
すみません、わざとじゃないんです
先生
だろうな。原因を調べたぞ
充希
リソースリークですか? Rei……伏石さんに聞きました
先生
そうだ。今回は悪意はないだろうが、リソースリークは、DoS攻撃、つまりサービス拒否攻撃に利用できるんだ

 リソースリークは、メモリーの解放忘れ、ファイルのクローズ忘れ、通信の接続切断忘れなどによって引き起こされます。これにより、メモリ不足、ファイルのアクセス拒否、最大同時接続数の超過などのリソース不足が引き起こされることがあります。

DB接続のリソースリークの例

 例えば、古いWebアプリケーションで時々見かけるのが、以下のように、セッション変数にDB接続オブジェクトを保存するような脆弱なコードです。

セッション開始時の処理(Global.asax.cs)の例
// 略
    public class Global : HttpApplication
    {
        //略

        void Session_Start(object sender, EventArgs e)
        {
            var connectionStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
            var conn = new SqlConnection(connectionStr);
            conn.Open();
            Session["DB_CONN"] = conn;         
        }

        //略
クエリ実行時の処理の例
var conn = (SqlConnection)Session["DB_CONN"];
using (var cmd = conn.CreateCommand())
{
    cmd.CommandText = "SELECT 1;";
    var result = cmd.ExecuteScalar();
    // 略
}
ログアウト時もしくは一定時間経過時の処理の例
var conn = (SqlConnection)Session["DB_CONN"] ;
conn.Close();
conn.Dispose();
Session.Remove("DB_CONN");

 ASP.NET Web Formsにて、セッション状態モードをInProcに設定されている場合を想定したコード。

 これはパフォーマンスチューニングを意図した物であると考えられます。DBでクエリを実行する度にDBへの接続と切断を繰り返していれば、塵も積もれば……で大きなタイムロスが発生します。それを避けるため、セッションが有効な間はDBへの接続を繋ぎっぱなしにしておこうと考えたのでしょう。しかし、このようにDB接続オブジェクトをセッション変数に保存するのは誤った方法です。

 確かにこの実装は、一見、意図通りに動作します。しかし、セッション数が、DBへの最大接続数を超えたときに不具合が顕在化します。

先生
例えば、通常のケースだと、一時に想定以上のユーザー数に閲覧されたときや、ログアウト操作をするユーザーが想定より少なかったときだな。ただ、もう一つこんなケースも考えられるんだ

 例えば、以下のような模擬攻撃コードをPowerShellで実行することを考えてみましょう。

$i = 101; while($i-- -gt 0) { Invoke-RestMethod -Uri https://localhost:xxxx/ ;  Start-Sleep -s 1}

注意

  • 実際に実験する場合は、自ら所有・管理するコンピューター上で、外部に通信が行なわれないよう措置を講じた上で実行すること
  • xxxxは対象となるテスト用のWEBアプリケーションのポート番号で置き換えること
  • 停止する場合はCtrl+Cキーで停止する

 この模擬攻撃コードは、Invoce-RestMethodでhttps://localhost:xxxx/に対してGETリクエストを、1秒おきに合計101回送信するものです。

 しばらくすると、接続上限(厳密には接続プール内の接続数の上限)に達して以下のような例外が発生します。

 繋ぎっぱなしになったDB接続が積み重なり、接続数の上限に達します。そして、新たな接続を行えなくなり、エラーが発生するようになります。

 なぜこのようなことが起きるのでしょうか。

 それは、PowerShellのInvoke-RestMethodが、デフォルトではCookie情報を保持しないことが原因です。Webサーバー上では、セッションを識別するためにCookieを使用します。しかし、Cookieが維持されなければ、アクセスの都度、新たなセッションが作成されることとなり、そのたびにSession_Startイベントが発生します。先ほどのコードの例では、Session_Startイベント内でDB接続を開始し、セッション変数内に保持していましたから、どんどんDB接続が積み重なっていき、やがて上限に達するという結果となるのです。

 もし悪意がなくとも、Invoke-RestMethod、curlコマンド、wgetコマンドなどを定期実行する場合や、クローラーが自動でページ内容を収集する場合、マンガのようにアプリが異常終了して、新たなセッションが開始してしまうような場合などにこのような状況が生じ得ます。

 このように、Webサーバー側ではセッションの終了を必ずしも検知できないため、リソースリークが発生してしまうということになるのです。

充希
どう修正したらいいんですか?
先生
この場合は、普通に接続プールを使えば良いだけなんだ

 一般的にフレームワーク側に接続プール(コネクションプーリング)の仕組みがあり、これを用いることで接続が再利用されるようになります。

 先ほどの例のASP.NETの場合、接続文字列においてMin Pool Sizeを1以上に設定することで、予め1個の接続が行なわれ、接続プールに溜め置かれます。

Web.configの設定例
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\xxxxx.mdf;Initial Catalog=xxxxx;Integrated Security=True;Min Pool Size=10;Max Pool Size=100"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

 conn.Open();の際には、接続プールに溜め置かれた接続があれば割り当てられ、なければ新規接続が行われます。そして、conn.Close();の際に接続プールに接続が戻されます。再びconn.Open();が実行された時には、接続プールに溜め置かれた接続を再利用することになります。

 そして、Min Pool Sizeに設定した接続数を超えた接続は、一定時間後に自動的に切断されます。

 したがって、わざわざDB接続をセッション変数に保存するというのは、百害あって一利なしの余計なお節介ということになります。むしろ、重大な欠陥といっても過言ではないでしょう。

先生
ところで、似た事例で、昔、Librahack事件という事件があったんだ

次のページ
Librahack事件(岡崎市立中央図書館事件)

この記事は参考になりましたか?

伏石ちゃんは意図に反したい連載記事一覧

もっと読む

この記事の著者

井二 かける(イブタ カケル)

 情報処理安全確保支援士、プログラマー、作家。「物語の力でIT・セキュリティをもっと面白く」をモットーに、作家活動、セキュリティ啓発活動を行う。主な作品はアニメ「こうしす!」、小説「こうしす!社内SE祝園アカネの情報セキュリティ事件簿」(翔泳社)、マンガ「伏石ちゃんは意図に反したい ~ハッキングから始まる高校生活~」(京姫鉄道出版)など。 Twitter:@k_ibuta@kyoki_railway

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山口 しずか(ヤマグチ シズカ)

 やりたいことはなんでもやる精神で急成長中の漫画家。2019年よりマンガアプリにて商業連載デビュー。連載の傍ら企業のPR漫画や漫画動画など媒体・ジャンルにとらわれず時代に合わせた漫画を制作中。趣味はお酒と旅行。 Twitter:@shizuckey

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/14925 2021/10/06 11:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー