今日もまた踏みました

非定期更新:主に何とも言えない事態にあった際に更新しています

このご時世にEdgeさんがフィッシングサイトに拘束される事案が発生…

姪っ子からヘルプ要請が入り見に行った所、フィッシングサイトにEdgeさんが拘束され、二進も三進も行かない状態になっていた

どんな状況か
  • フィッシングサイトが開かれ、フィッシングサイトのOKボタン以外の操作がEdge上でできなくなった
  • 内容は不正利用しているから支払え系の内容
  • OKボタンを押すと印刷のダイアログが表示される
  • ×ボタンで閉じる事ができない(各ページ及びEdgeそのものも不可)
  • F12で開発ツールも開けない

基本的に何も操作ができません…

何が問題か
  • ×で閉じれないため、Windowsをシャットダウンするなり再起動するなりしていた
  • Edgeのクラッシュ対策機能により、次回のEdgeは強制的に閉じたページをまた開く仕様になっている
  • 結果、Edgeを開くとフィッシングサイトがまた開く

簡単に言うと無限ループします

さらに何が問題か
  • Edgeのキャッシュクリアなどの作業はEdgeにあるメニューを選択する必要がある
    ※今の所、ショートカットする手段は見つからず
  • フィッシングサイトが開いているとEdgeの設定が開けない

EdgeのキャッシュをクリアするにはEdgeを開かなければならないがEdgeが操作できない…みたいな感じです

対応方法

色々と考えましたが、おまじないが必要です

  1. Edgeを閉じます。閉じ方がわからない場合はWindowsを再起動します
  2. コマンドプロンプトを起動して頂き、下記をコピペします
    %LOCALAPPDATA:~0,2%
    cd "%LOCALAPPDATA%\Packages\Microsoft.MicrosoftEdge_*\AC\MicrosoftEdge\User\Default\Recovery\Active\"
    start .\
  3. Activeというディレクトリが開かれます
  4. Activeの中のファイルを削除します
  5. Edgeを起動します

という手順で基本的に大丈夫なはずです

 

全てが終わってから、実はもう一つEdgeを立ち上げて、そちらでキャッシュをクリアすれば良いのではとも思いましたが、問題のページを見失ったのと、ページ復帰機能はキャッシュのクリアと連動しない様な気もしたので、手堅い方法として記載しておきます

Excel で URL をクリックした場合とブラウザに URL を貼り付けた場合で違うページが開かれる事案が発生

前提条件です

基本的なところですが、下記の様な手順で URL のリンクを作成し、クリックにて URL を開いた時の事になります
また、Excel と記載していますが、Word や PowerPoint など Office 製品全般に言える様ですが、ここでは代表して Excel にて説明をします

1. Excel を開きますf:id:itrident_kumakawa:20160309120125p:plain

2. セルに URL を入力しますf:id:itrident_kumakawa:20160309120129p:plain

3. Enter キーなどで入力内容を確定します ( 入力内容がリンクになります )f:id:itrident_kumakawa:20160309120133p:plain

4. このリンクをダブルクリックして開きますf:id:itrident_kumakawa:20160309120137p:plain

上記の様な手順を実行したときに直接ブラウザで開いた場合と異なるページが開かれる場合があります

「お断り事項」
当内容は、2016/01/01時点でのExcel 2010 および 2013にて確認を行っております。
この動作が Microsoft 社の想定している動作か否かはわかりません。
よって知らぬ間に修正されている可能性はあります

 

どの様な事が起きるのか

参考用サイトを作成しました(誤って消してしまっていたらごめんなさい)
http://www.hhsb.jp/exceltest/subpage.php

 普通の操作Excel リンククリック

ログインページが開く
User:hoge
Password:hogehoge
を入力しログイン

f:id:itrident_kumakawa:20160309121103p:plain f:id:itrident_kumakawa:20160309121103p:plain

 

ログイン後のページへ遷移する
f:id:itrident_kumakawa:20160309121453p:plain f:id:itrident_kumakawa:20160309121544p:plain

このサイトはサブページを参照する際にログインを必要としています
そのため、普通の操作および Excel リンククリックでの操作ともに最初はログインの画面が表示されます。本来であればログイン後にサブページへと画面が移りますが、Excelのリンクをクリックした場合は、サブページではないページへと移ってしまいます

 

どの様なサイトで発生するのか

下記の様な構成になっている場合に発生します

f:id:itrident_kumakawa:20160329174519p:plain

特徴として、ログインしていない場合の画面遷移を Forward ではなく Redirect で行っているところです(処理として間違っている訳ではないです)
そして Excel で開いたリンク先でリダイレクトが発生した場合に想定と異なります

 

なぜ、このような現象が発生するのか

Excel にて リンクをクリックした場合、まずは Excel 内部で持っているブラウザにて URL へのアクセスを試みます
特にリダイレクトされている訳でもなく、また初めて開く URL の場合は、次の様な流れとなります

内部ブラウザにて URL のヘッダーを確認 (HEAD リクエスト)
→ 内部ブラウザにて URL のダウンロードを実施 (GET リクエスト)
→ デフォルトブラウザを URL 指定で起動

具体的に何をやっているのかはわかりませんが、HEAD リクエスト後に GET リクエストが発生している関係上、内部でキャッシュを持っており、そのキャッシュの更新確認を行っていると思われます
あくまでも推測の域を出ませんが、URL の信頼性などの確認を行っている様です

 

問題のケースとなりますが、間にリダイレクトが入ると下記の様な動作に変わります

内部ブラウザにて URL1 のヘッダーを確認 (HEAD リクエスト)
→ レスポンスにより URL2 への 302 リダイレクトを受けとる
→ 内部ブラウザにて URL2 のヘッダーを確認 (HEAD リクエスト)
→ 内部ブラウザにて URL1 のダウンロードを実施 (GET リクエスト)
→ レスポンスにより URL2 への 302 リダイレクトを受けとる
→ 内部ブラウザにて URL2 のダウンロードを実施 (GET リクエスト)
→ デフォルトブラウザを URL2 指定で起動

なぜか途中でリダイレクトを受け取ると、最後にデフォルトブラウザを呼び出す段階になって、リダイレクト先の URL を渡してしまっています
上記のサンプルもそうですが、ログイン後にアクセスされた URL へと戻る様に作成してありますが、Excel のリンククリックより起動した場合は、最初からリダイレクト先の URL で渡されてしまうため、戻る先がわからなくなります
そのため、普通に開いた場合と Excel のリンクを開いた場合で動作が異なってしまう事になります

この動作を狙って作ってあるのか、それとも何らかミスなのかはわかりませんが、現象としては、この様に動くようです
狙ったページが開かれない場合は、URL をコピペして開く様にした方が良いです

 

ついでにこの現象を経てわかった事

Excel のリンクをクリックした場合は、ダウンロードが 2 回発生する…
1GB のファイルがダウンロードされる URL をクリックしたら、1GB のファイルを 2 回ダウンロードする様です…

また、URL の先が無い場合は、タイムアウト待ちを行った後にブラウザが起動されます…たまにもっさりとした動作になるのは、これが原因の様です

こういった事より、基本的に Office 製品に貼りついている URL のリンクはクリックで開かずにコピペにて直接開いた方が良さそうです

e-Tax ソフトのインストールが途中で止まって先に進まないという事案が発生

どのような状況か

e-Taxソフトが新しいソフトウェアのインストールを構成中です。」のまま止まってしまう。という現象が出る場合の事です ( 下記画像の様な状況です )

f:id:itrident_kumakawa:20160227040018p:plain

『追記』
公的個人認証サービス ( jpki )のインストールプログラムでも同様の現象が出ている様な気がします。同じ対応で対処できましたので参考にしてください

細かい事はいいんだよ。結論を言え結論をという方へ
  1. アプリを閉じる
    動いているアプリケーションを閉じましょう
    まずはタスクバーで動いているアプリケーションを順次閉じます

    f:id:itrident_kumakawa:20160227040815p:plain

    セットアッププログラム以外をすべて閉じ終わったら少し待ちます

  2. まだ動かない場合は、さらにアプリを閉じる
    引き続き動いているアプリケーションを閉じましょう
    タスクトレイで動いているアプリケーションを順次閉じます

    f:id:itrident_kumakawa:20160227041216p:plain
    概ね閉じ終わったら少し待ちます

  3. まだ動かない場合は、さらにアプリを閉じる
    ダメな場合は、ここに居るアプリケーションも忘れずに閉じましょう

    f:id:itrident_kumakawa:20160227041433p:plain

  4. 数分待ちます

それでもダメな場合はセーフモードで起動しなおして入れてみると良いです

 
なぜこんな事が?という方へ ( ここからは推察です )

≪おことわり≫
アセンブルなどをして解析した訳ではなく現象からの推察です
従いまして情報に誤りがある場合があります

 

このインストールプログラムは、複数のインストールプログラムを順次実行し、アプリケーションを構成していきます

この作りは、1 つ 1 つのインストールプログラムが小さくなりますし、不要なものを入れないため使い勝手が良くなりますので良い作りと言えましょう

問題は、この『順次』の部分になります
順次処理を行うには、動いているプログラムが終わるのを待つ必要があります

色々とやり方はありますが、メジャーな所では
・プログラムのタイトルに特定の文字が含まれるかをチェックする
・特定のプログラム名に特定の文字が含まれるかをチェックする
などを行います

上記の様なルールに合致するアプリケーションが起動していますと、誤判定をしてしまい、いつまで待っても終了しないという事象が発生します

なお待ち時間が必要なのは、上記チェックを行うタイミングが不明なためです。これは数ミリ秒~数秒、まれに 1 分周期などのケースもあります。よってコーヒーブレイクするぐらいが無難です

判定ルールの調査を行っておりませんので、具体的にダメなルールはわかっていませんが、インストール作業の時のみの現象ですので、片っ端からアプリケーションを閉じるのが良いと思われます

Windows 10で電源ケーブルを抜くとプログラムが終了するという事案が発生

とりあえずですが、タスクスケジューラを利用していない方は該当しません…
また、初期のバグっぽいので、改善される可能性は大きいです

発生条件は、タスクスケジューラに登録されている電源の部分、赤枠で囲ってますが、「コンピュータの電源をバッテリに切り替える場合は停止する」の部分にチェックが入っている場合に発生します

f:id:itrident_kumakawa:20150806142049j:plain

Windows 10にアップグレードした方が主にこの現象に嵌ると思いますが、Windows 8.1までであれば、「コンピュータを AC 電源で使用している場合のみタスクを開始する」のチェックが外れている場合、「コンピュータの電源をバッテリに切り替える場合は停止する」のチェックのON・OFFに関わらず、OFFの扱いとなります

それが、Windows 10になり、独立して認識してしまっている様です

 

ですので、下記の様に両方のチェックを外しておく事でこの現象は解決します

f:id:itrident_kumakawa:20150806142054j:plain

 

PHP の try...catch で Exception がキャッチされない事案が発生

とりあえず結論を知りたいという技術者の方へ

とりあえず

catch (Exception $e)

catch (¥Exception $e)

に変更してリトライしてみてください。
それでもダメな場合は、エラーの内容を確認してみてください

PHP は throw した例外以外は catch しません

例えば 0 の除算とか… オブジェクトが null でしたとかは catch しないです
そちらの場合は、 set_error_handler を使う必要があります

まずは大丈夫なパターンから

このパターンは問題なくキャッチされる

<?php
class TestException extends Exception { }

class Test
{
  public function goTest()
  {
    try
    {
      echo "### Start ###¥n";
      throw new TestException('Test Exception!');
      echo "### End ###¥n";
    }
    catch (Exception $e)
    {
      echo "### Catch ###¥n";
      echo $e->getMessage() . "¥n";
    }
  }
}

$test = new Test();
$test->goTest();

 実行結果

 ### Start ###
### Catch ###
Test Exception!

正しくキャッチされている

似ているけどキャッチされないパターン

上記のソースに namespace を付けるとキャッチされなくなる

<?php namespace sample;
class TestException extends ¥Exception { }

class Test
{
  public function goTest()
  {
    try
    {
      echo "### Start ###¥n";
      throw new TestException('Test Exception!');
      echo "### End ###¥n";
    }
    catch (Exception $e)
    {
      echo "### Catch ###¥n";
      echo $e->getMessage() . "¥n";
    }
  }
}

$test = new Test();
$test->goTest();

 実行結果

### Start ###
PHP Fatal error:  Uncaught exception 'sample¥TestException' with message 'Test Exception!' in test.php:11
Stack trace:
#0 test.php(23): sample¥Test->goTest()
#1 {main}
  thrown in test.php on line 11

とまあ、キャッチされない

原因

当たり前は当たり前なんだけど…

catch (Exception $e)

と記載すると、 catch は sample¥Exception で待ち構えているため、 ¥Exception を継承した sample¥TestException はキャッチしない
なので namespace を付けた場合は、「 catch (¥Exception $e) 」としておくか、 use で Exception を宣言しておく必要がある様だ
他の言語に慣れていると、 Exception で全部キャッチできそうな気になるので気を付けておきたい
最も実害が無いので、常に ¥Exception にしておいても良いかも知れない

Apache を Proxy にしていたら Shift JIS 系のサイトが文字化けするという事案が発生

発生した理由

直接接続した場合のレスポンスヘッダ

Content-Type: text/html

Proxy 経由した場合のレスポンスヘッダ

Content-Type: text/html; charset=UTF-8

Content-Type に UTF-8 が追加されている…

疑う余地も無くこれが原因と断定した

 

まあ、PHP でアプリケーション作ってた Apache に無理やり Proxy 機能を追加したので仕方なかったですけどね…

 

対処

httpd.conf の Proxy ディレクティブに下記を追加

AddDefaultCharset Off

全体に影響しても良い場合は httpd.conf 内の AddDefaultCharset を変更すれば良いとは思うのだけど、今回は Proxy の場合のみ影響を出したかったの でProxy ディレクティブ内に記載とした

 

C# から SSH を使って MySQL を使う時に MySQL への再接続が遅いという事案が発生

とりあえず結論を知りたいという技術者の方へ

ConnectionString に下記を加えて変化が無ければここに答えはありません
頑張って次を探してください

Pooling=False

これで解決したものの上司への説明が欲しい場合は…

MySQL Connector / Net は IIS などで利用する事を前提にしているのか、Connection Pool  の機能が標準で ON になっている。そのため、MySQL をクローズしたのみでは切断されておらず、再接続の時にポートの解放待ちが発生するため、結果として再接続が遅くなる

とダメ元で言ってみてください…

 

開発ツールと利用ライブラリ

今回は下記を利用して問題が発生しました
Visual Studio 2010
SSH : SSH.NET Library - Home
MySQL : MySQL :: Download Connector/Net

 

本題はここから

お客様より、こちらで作成したアプリが「お昼休みに手を放してると午後に登録しようとするとエラーで落ちるんだよね」と言われた

MySQL を利用している WEB システムのメンテナンスプログラムを C# で拵えて利用して頂いていたのだが、お客様より上記のような話があがってきた
仕方ないので修正を加える事となったのだが、今回は SSH を利用して MySQL へ接続するという仕組みとなっていた。落ちているのは SSH なのか MySQLなのか…そこから考えなければならないのだが、そんなに頻繁にアクセスする類でもないので、検索や登録などのボタンアクションの開始で接続し、終了で切断する様に修正を加えた

結果、検索を 2 回連続でクリックしたら 2 回目が異常に遅いという現象が発生した

ちょっとトレースしてみたところ、再接続のタイミングで謎のウェイトが発生している事がわかった。そう言えば誰かがそんな現象が出るって言ってた気がする…
という事で追跡の旅は始まる

切断したのに切断されないという現象

MySqlConnection にて作成された connection に対して Close() と Dispose() を呼び出したが MySQL 側でウォッチしている限り切断されていない
何か切断手順が間違っているのかなとリファレンスをなめるが、それらしき答えは発見できなかった

今回は SSH 経由で接続してる事もあり、何か意外な事実があるのではと、SSH 側も調査を開始、まずはポートフォワードを行うための ForwardedPortLocal の切断処理をチェック Stop() を呼べている。だが MySQL は切断されない

それではと、SSH の切断も確認する。 Disconnect() と Dispose() を呼べている。だが MySQL は切断されない

Dispose してるから普通に考えたらありえないけど、オブジェクト生きてたりするのかなと、接続と切断の部分を中心に、ソースコードを洗い直してみたが、それらしき場所は見つからなかった

諦めて実証実験用のソースコードを作成

プログラム本体では、想定外の事があってもわかりにくいので、SSH 経由で MySQL へ接続するだけのサンプルを作成し、それを使って再現するかの実験を開始した

こんな感じで作成した

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet;
using MySql.Data.MySqlClient;

namespace SSHTest
{
    class Program
    {
        static void Main(string[] args)
        {
            SshClient client = null;
            try
            {
                string host = "192.168.xxx.yyy";
                int port = 22;
                string user = "user";
                string sshPasswd = "password";
                string keyFile = @"keyfile.idrsa";

                PrivateKeyFile pkfile = new PrivateKeyFile(keyFile, sshPasswd);
                client = new SshClient(host, port, user, pkfile);
                client.Connect();

                uint dbPort = 3306;
                string server = "127.0.0.1";
                uint localPort = 13306;
                string dbUser = "dbuser";
                string dbPassword = "dbpassword";
                string dbName = "dbname";

                ForwardedPortLocal forward = new ForwardedPortLocal(server, localPort, "127.0.0.1", dbPort);
                client.AddForwardedPort(forward);
                forward.Start();  // ①

                var connectStr = "server=" + server + ";";
                connectStr += "port=" + localPort.ToString() + ";";
                connectStr += "user=" + dbUser + ";";
                connectStr += "password=" + dbPassword + ";";
                connectStr += "database=" + dbName + ";";

                var connection = new MySqlConnection(connectStr);
                connection.Open();  // ②

                connection.Close();  // ③
                connection.Dispose();  // ④
                forward.Stop();  // ⑤
                client.Disconnect();  // ⑥
                client.Dispose();  // ⑦
                Environment.Exit(0);  // ⑧
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

結果… MySQL は切断されなかった
何か根本的な所に原因があるらしい

実際はこんな感じに動く

この仕組みの特徴的な所だが、SSH の接続完了後に ForwardedPortLocal がポートフォワードするために Listen する
今回は、127.0.0.1:13306 となる。MySQL Connector は、そのサーバー及びポートに対して接続を実施する。そのため接続先は 127.0.0.1:13306 となる

①の部分、「forward.Start(); 」を通ると、Forwardの準備が開始され
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
となり、Listen される

②の部分、「connection.Open();」を通ると、MySQL へ接続され
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz ESTABLISHED
となり、MySQLへの通信が開始される

③、④の MySQL に対する終了処理を実施しても変化なし
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz ESTABLISHED

⑤の ForwardedPortLocal に対する停止処理を実施すると、Listenが終了し、
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz FIN_WAIT_2
となる
この状態から⑥に移るまで、こちらの環境では30秒ほど必要だった

⑥の時点ではもうポートは閉じていた
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz FIN_WAIT_2

⑦の Disconnect だが、ForwardedPortLocal を Dispose してしまうと落ちてしまうので、ForwardedPortLocal のDisposeを行わずに SSH Client を Disconnect した

ここで再確認できたのは、ForwardedPortLocal の Stop は Listen の終了だったらしく、それまでの接続を強制的に切断する事ではなかったらしい
また、時間がかかるポイントは、MySQLの切断ではなく、SSH で利用してる Forward ポートの解放だったようだ。切断の処理などを雑に作成していると再接続を行った際に待ち時間が発生するという理屈のようだ

本体のプログラムで感じた挙動と違うのは、本体側に接続ポートを転がす仕組みを入れていたためだった(ポートが握りっぱなしになる話を聞いて、この処理を入れたのだった…時間無かったんだよ…)。やはり再現確認用プログラムを作る習慣は大事だ

MySQL だけの再接続は実は早かった

今回の問題が発生した理由は、SSH までクローズしてしまっていた事だろう。ForwardedPortLocal を Stop せずに、そのまま MySQL を再接続する分には問題無く再接続できた
なら MySQL の再接続で問題無いじゃないとなる所なのだが、そうもいかない
切断されたのが SSH なのか MySQL なのかのハンドリングができなかったからだ
できなかった(方法が見つからなかった)ので、ボタンアクションで再接続という方向にシフトした。けど SSH ごと切断→再接続すると遅い。まだ何も解決していなかった

MySQL 側の設定を再確認

そもそもの所に頭を戻す。そう、そもそも③(Close)か④(Dispose)の時点で MySQL が切断されていないのが気持ち悪い。Dipose してるのに消えないとか何者よ…と思いながらリファレンスを参照し、設定項目の漏れを探す
ない、全然見つからない。そもそも設定できる項目少ないんだよね。この子
他に何か無かったっけと思い、Connection String にも設定項目がある事を思い出して、MySQL Connector で利用できる設定項目を眺める

MySQL connection strings - ConnectionStrings.com

ここのサイトを眺めていたら、とても気になる一文を発見
「Recycle connections in pool」
ようやく攻略の糸口を見つけた。クライアントアプリケーションとして作成していたので、完全に頭から離れていたが、そんな人居たよね Connection Pool さん
もしかしてデフォルトで ON になってるの?と思い、OFFにする設定を探す

有りました「Pooling=False」試しに入れてみるとようやく狙った通りに動いた

ありがた迷惑な感じではあるけれど、サーバーで利用する事を考えれば、接続を再利用する設定がデフォルトなのは当たり前と言えば当たり前なのか
SSH 経由の場合に、Forward ポートを握りっぱなしにされて困るとか、レアケースよね。けどまあ、実例のサイトも多いので、それなりに使ってる人居ると思うけど

とりあえず MySQL Connector をサーバーで使わない時は、Pooling=False を入れると頭に刻んだ