技術メモ

神奈川在住のITエンジニアの備忘録。おもにプログラミングやネットワーク技術について、学んだことを自分の中で整理するためにゆるゆると書いています。ちゃんと検証できていない部分もあるのでご参考程度となりますが、誰かのお役に立てれば幸いです。

依存関係逆転の原則

オブジェクト指向の「依存関係逆転の原則 (Dependency Inversion Principle)」について簡単に纏めてみる。

 

例えば、パッケージAとパッケージBあり、パッケージAのクラスがパッケージBのクラスを使用しているとする。ここで、パッケージBの方が変更されることが多く、リビルドされることが多いとすると、通常、それに依存するパッケージAもその都度リビルドが必要になる。

そこで、パッケージB内のクラスのインタフェースをパッケージA側で持ち、パッケージB内のクラスはそのインタフェースを実装するよう設計する。こうすれば、そのインタフェースが変わらない限り、パッケージA側はパッケージB側の変更のたびにリビルドせずに済む。(型=クラスの情報としては、パッケージAはパッケージBに依存していないので。) 機能としては、パッケージAはパッケージBに依存したままだが、ビルドの際の依存関係については、パッケージBがパッケージAに依存するかたちになる。

 

いろいろ書いたがざっくり言うと、あまり変更されない方(パッケージA)が良く変更される方(パッケージB)のインタフェースを持って規定し、良く変更される方はそれに合わせて実装するというアーキテクチャにすると、開発の際にリビルドする回数が減らせるのではないか、ということ。

CSRFについて

セキュリティ脆弱性の一つであるCSRF (クロス・サイト・リクエスト・フォージェリ) について簡単に纏めてみた。

CSRFを一言で言うと、攻撃者により、webサイトの利用者が意図しないリクエストがwebサイトに対して実行されてしまうセキュリティ脆弱性のこと。

 

攻撃のメカニズム

  1. ユーザはwebサイトA(正規のサイト)にログインしている状態。(前提条件)
  2. ユーザは攻撃者が用意したwebサイトB(悪意のあるサイト)にアクセスする。
  3. webサイトBからダウンロードされたJavaScriptがユーザのwebブラウザ上で動作し、そのJavaScriptがwebサイトAに対して、ユーザの意図しないリクエストを行う。これにより、例えば、webサイトAが掲示板の場合、ユーザが意図しない書き込み等が行われてしまう。

この攻撃は、ユーザはwebサイトAにログインしている状態であり、webサイトA側からすると正規のユーザからのリクエストに見えてしまうので、webサイトAでの権限チェックなどでは防げない。

なお、webサイトB由来のJavaScriptがwebサイトAに対してリクエストを出すのは「同一生成元ポリシー」に反するので、webブラウザ側でブロックできるのは?と思った。しかし、調べたところ、上記の 3. の攻撃で使用されるであろう form タグの action 属性には同一生成元ポリシーが適用されず、任意のURLを指定できるようなので、ブロックできないらしい。

 

対策

webサイト利用者側

攻撃の前提条件はwebサイトにログインしていることなので、利用が終わったwebサイトからはこまめにログインする。

webサイト側

フォームが入ったページをクライアント側に返す時はページにCSRFトークンを含めて、クライアントからフォームデータが来たら、それに含まれているトークンをチェックする。

Windows10環境における「コマンドウィンドウをここで開く」

Windows7環境で良く使っていた、

 

 エクスプローラー ⇒ シフト+右クリック ⇒ コマンドウィンドウをここで開く

 

がWindows10環境ではなくなっているので、ちょっと不便だなあと思って調べたところ、以下の記事を見つけた。

https://www.softantenna.com/wp/tips/windows-10-command-prompt-here/

 

これによると、Windows10ではエクスプローラーのロケーションバーに「cmd」と入力すれば同等のことができる。(試したところできた。)

また、レジストリを編集して、「コマンドウィンドウをここで開く」メニューを復活することもできるらしい。

Javascriptのpromiseパターン

JavaScript の promise パターンについて簡単に書く。promise パターンについては、以下のページに詳しく書かれている。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises

https://blogs.msdn.microsoft.com/ie_jp/2011/10/04/promise-javascript/

 

このパターンを使うと、Javascriptで非同期処理を書くときにコードがシンプルになる。仕組みをざっくり説明すると、非同期処理を行う関数を発行した際、その関数から受け取った戻り値(promise)に対して、任意のタイミングで then(成功時のコールバック関数, 失敗時のコールバック関数) 関数を実行し、その時点で非同期処理が終わっていればコールバック関数が実行され、終わっていなければそこでwaitするというもの。

 

非同期処理発行時の戻り値をあとでチェックするという方式は、JavaのFutureパターンに似ているなあ、と思った。

https://docs.oracle.com/javase/jp/8/docs/api/java/util/concurrent/Future.html

以下は、参考として、以前JavaのFutureパターンを使って書いたサンプルプログラム。

https://akrad.hatenablog.com/entry/2018/04/25/220836

完全コンストラクタパターン

オブジェクト指向の勉強をしていて、「完全コンストラクタパターン」というものが出てきた。これは、オブジェクト生成時にコンストラクタに必要な情報を全て渡して、後からそのオブジェクトの状態(フィールド変数)を変更しない設計方式。つまり、オブジェクトのもととなるクラスについては、setter は持たず、フィールド変数の定義に final が付くことになる。

このパターンのメリットは、オブジェクト生成時点からオブジェクトの状態が変更されない、つまりイミュータブルなオブジェクトになるので、どこかでそのオブジェクトが変更されている可能性を気にする必要がなくなるし、意図せずオブジェクトの状態が変更されているということも起きなくなる。

オブジェクト生成時点で必要な情報が全て揃う場合に有用だと思う。

JavaのInetAddressクラスのメソッドで名前解決(逆引き)する時は正引きも行われている。

JavaのInetAddressクラスのgetHostName()やgetCanonicalHostName()で、IPアドレスからホスト名への名前解決(逆引き)を実行すると、裏で正引き(ホスト名⇒IPアドレス)も行われるようだ。詳細は以下の通り。

InetAddressクラスのソースを確認すると、getHostName()とgetCanonicalHostName()は、その延長でInetAddress#getHostFromNameService()をコールする。このgetHostFromNameService()の実装は以下になっている。

    private static String getHostFromNameService(InetAddress addr, boolean check) {
        String host = null;
        for (NameService nameService : nameServices) {
            try {
                // first lookup the hostname
                host = nameService.getHostByAddr(addr.getAddress());

                /* check to see if calling code is allowed to know
                 * the hostname for this IP address, ie, connect to the host
                 */
                if (check) {
                    SecurityManager sec = System.getSecurityManager();
                    if (sec != null) {
                        sec.checkConnect(host, -1);
                    }
                }

                // ★★
                /* now get all the IP addresses for this hostname,
                 * and make sure one of them matches the original IP
                 * address. We do this to try and prevent spoofing.
                 */

                InetAddress[] arr = InetAddress.getAllByName0(host, check);
                boolean ok = false;

                if(arr != null) {
                    for(int i = 0; !ok && i < arr.length; i++) {
                        ok = addr.equals(arr[i]);
                    }
                }
                // ★★

                //XXX: if it looks a spoof just return the address?
                if (!ok) {
                    host = addr.getHostAddress();
                    return host;
                }

                break;

            } catch (SecurityException e) {
                host = addr.getHostAddress();
                break;
            } catch (UnknownHostException e) {
                host = addr.getHostAddress();
                // let next provider resolve the hostname
            }
        }

        return host;
    }

(JDK 1.8 のInetAddressクラスのソースから抜粋)


上記の ★★ で囲んだ部分で、(名前解決の結果得た)ホスト名からIPアドレス群を取得し、取得したIPアドレス群の中に問い合わせの元となったIPアドレスが存在するか確認している。この処理は、ソースのコメントにもある通り、DNS spoofing対策のために入れらている。
つまり、Javaにおいて名前解決(逆引き)を行う時は正引きも実施され、逆引き・正引きの結果が不整合だと、名前解決に失敗するということになる。

JavaのURLの名前解決で、DNSの逆引きが発生しています。DNSの逆引きを抑止することはできますか?
という記事もあるし、Javaで名前解決処理を含むアプリケーションを動かす場合、正引きと逆引きの結果が一致する環境にした方がアプリケーションの動きは安定しそうな気がする。(まあ、Javaのアプリケーションに限らず、正引きと逆引きは一致させた方が予想外の挙動に悩まされなくて済みそう。)

J2EEのEntity Manager経由でDBから取得したオブジェクト

J2EEのEntity Manager経由でDBから取得したオブジェクトを、そのオブジェクトがEntity Managerの管理下にある状態の時に変更すると、それがDBに反映されるので注意が必要である。
例えば、

User user = entityManager.find(User.class, id);

として取得した user オブジェクトに対して、

user.setName("Taro");

とすると、この変更はトランザクション終了時などに自動的にDBに反映される。DBに反映されるタイミングについては、以下のページが分かりやすかった。
http://enterprisegeeks.hatenablog.com/entry/2015/01/19/142730
http://itref.fc2web.com/java/jpa/


上述のことを意識しておかないと、メモリ上のオブジェクトの情報を変更しただけのつもりで、実際にはその変更はDBにも反映されて「あれ?DBのデータは変更した覚えがないけど・・・」とかなってしまう。