2014-09-21

第八回 #渋谷java にて、認証系を Java 8 で自作する話をしてきました

Posted on 2014-09-21, 2:52

はじめに

毎回、他の発表者の方々が有益情報を発表してくれる中で僕一人が誰得情報をひたすら発信しているわけですが、今回もご多分に漏れず Java 8 で認証系を自作する、というニッチな話をしてきました。

昨今のパスワード流出系のセキュリティインシデントが業界内で話題になるたびに「認証系なんてものは自作するようなもんじゃない」という認識が醸成されつつあると思います。しかし、マイナー or オレオレ Web アプリケーションフレームワークなどを利用しているとそうもいってはおられず、認証系が用意されていないときはやむなく認証系の自前実装が要求されることもあるでしょう。自分自身も周りを見渡していても、過去何度かそういうことがありました。

ただ、実装者の認証セキュリティに対する認識や知識によって脆弱な認証系を作り込むことも少なくなく、そういうのをなるべく減らしたいなー、という思いを込めて今回の発表に至りました。

どうしても認証系を自前実装しなければならないときに気をつけたいこと

前述した発表資料に書いてあることにはなりますが、今一度文章に起こしておきます。

アカウント別 salt の生成と暗号論的擬似乱数生成器の利用

セキュリティを少しかじったことがある人であれば「salt ってやつとパスワードを組み合わせたものをハッシュする」ということぐらいは知っているものと思います。ただ、どうも salt だけがひとり歩きしていることがあったりして、アプリケーションで共通の事前に用意された salt を使いまわす、みたいなアレな実装を見たことがある人はそこそこいるんじゃないでしょうか?

本来は、認証単位であるアカウント別に salt を生成すべきであり、かつ salt の生成には暗号論的擬似乱数生成器を用いるべきです。この salt の生成を Java で実現するならば、

    static byte[] newSalt(int length) {
        try {
            byte[] result = new byte[length];

            SecureRandom.getInstance(
                    // "NativePRNGNonBlocking"
                    // "NativePRNGBlocking"
                    "SHA1PRNG"
            ).nextBytes(result);

            return result;

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

のような実装になることでしょう。 SecureRandom.getInstance() の呼び出しで指定しているのは擬似乱数生成アルゴリズムで、Java 7 以前は SHA1PRNG だけだったのが、Java 8 では新たに

  • NativePRNG
  • NativePRNGBlocking
  • NativePRNGNonBlocking

が (Windows を除く) *nix-like なプラットフォーム向け JRE にて導入されたようです。

暗号学的ハッシュ関数の利用とストレッチング

salt を生成したら、あとはパスワードと組み合わせてハッシュ化、なのですが、これも単に SHA-* を使えばいい、というほど簡単ではありません。

結論から言ってしまえば、PBKDF2 などのキー派生 (導出?) 関数を使ってストレッチングすべき、となります。

Java 7 では PBKDF2 の実装は PBKDF2WithHmacSHA1 しか用意されていませんでしたが、Java 8 にて

  • PBKDF2WithHmacSHA224
  • PBKDF2WithHmacSHA256
  • PBKDF2WithHmacSHA384
  • PBKDF2WithHmacSHA512

の 4 つが導入されました。この PBKDF2 を用いたパスワードのハッシュ化コードは以下になります。

    static byte[] hash(String password, byte[] salt, int numIterations, int numHashLength) {
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, numIterations, numHashLength);

        SecretKeyFactory factory;
        try {
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

        try {
            return factory.generateSecret(keySpec)
                    .getEncoded();

        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }

まとめ

なんだかんだ書いてはいますが、やっぱり僕含めて素人は認証系の実装をするのはやめたほうがいいですね (過去何度もダメな認証系を作ってしまったし…)。

既存ライブラリの Apache Shiro とか Spring Security とか、僕は使ったことないですが、多分このあたりの面倒なお仕事をこなしえくれるのではないでしょうか。

なお、

こういうライブラリもあるようですよ!

参考文献

0 コメント:

コメントを投稿