近年、なりすましや不正ログインなどのセキュリティインシデントが多く発生し、セキュリティ強化のために2要素認証の導入が求められています。
実際に、大手コンビニの決済アプリでは、2要素認証が導入されていなかったことが1つの原因となり不正ログインが発生し、顧客の信頼を失ったことでサービス停止に追い込まれる事態も発生しています。
この記事では、セキュリティを強化するために、手軽に2要素認証を実装する方法を解説します。特にお金が関係するシステムでは、2要素認証の導入は必須といえる状況ですので、是非覚えておきましょう。
2要素認証とは
二要素認証とは「本人だけ知っている情報」「指紋などの身体的特徴」「本人だけが所有している物」の内、2つの要素を使って認証する仕組みのこと指します。例えば、本人だけ知っているID/パスワードと、身体的特徴である指紋認証の2つの組み合わせて本人確認するようなものを2要素認証と言います。
私たちが普段利用している銀行のATMも、実は2要素認証を採用されています。ATMは、本人しか所有していないキャッシュカードと、本人しか知らない暗証番号の2つの要素を使って認証を行っているため、普段はあまり意識してないかもしれませんが、まさに2要素認証と言えます。
Google Autehnticator
Google Authenticator(Google認証システム)は、Google社が開発したオフラインで認証を行うための仕組みです。
専用のアプリ(iOS / Android)をインストールして、スマホに表示された6桁の数字からなる認証コードを使用して認証を行い、従来の ID/パスワードの認証に加えて Google Authenticator での認証を追加することで2要素認証を実現します。
Google Autehnticator の認証コードは、時刻同期型のワンタイムパスワードとなっており、有効時間内に画面に表示された認証コードを、認証先の Webサービス等に入力することでログイン認証を行います。
このように、時間によってワンタイムパスワードが変わることから「Time-based One-Time Password」省略してTOTP認証とも呼ばれています。
認証コードは、ワンタイムパスワードの標準として定められている RFC 6238に基づいて生成されます。Google Authenticator では認証コードを生成するための 80ビットの秘密鍵を含むQRコードを表示し、それをユーザーが読み取ることでユーザーのGoogle Authenticatorアプリに秘密鍵を登録します。この秘密鍵を認証先のサービスと共有することで、Google Authenticatorアプリと認証先のシステムがオフラインでワンタイムパスワードが正しいかどうかを検証できるようになります。
Google Authenticator アプリを使った2要素認証は、クラウドサービスで最もシェアがある AWS (Amazon Web Services)などでも採用されています。
JavaでGoogle Autehnticator認証を実践
それでは、実際にJavaで秘密鍵と認証コードを生成し、Google Autehnticator アプリで認証するための実装を行ってみましょう。
プロジェクトの作成
Eclipse 等のIDEを使って、Maven プロジェクトを作成します。そして依存関係(pom.xml)に次のライブラリを追加します。
<dependency>
<groupId>de.taimos</groupId>
<artifactId>totp</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
totp は、秘密鍵から30秒おきに変わる認証コードを生成するためのライブラリで、commons-codec は16進数とbase32に変換するためのライブラリです。
秘密鍵とQRコード用のURLを生成するコード
まず、ユーザーの Google Autehnticator アプリとサーバーで共有する秘密鍵を作るコードを作ります。
// 秘密鍵の生成
public static String generateSecretKey() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
Base32 base32 = new Base32();
return base32.encodeToString(bytes);
}
上のコードで生成された秘密鍵は次のようになります。秘密鍵は必ずユーザーごとに別の値を生成するようにし、安全な場所で管理するようにしましょう。
ADWXM3OYBPGTZVEPB5FKVDM3CSNCWHEK
次に、上のメソッドで生成した秘密鍵と、メールアドレスなどをQRコードに表示するためのURLを組み立てます。
public static String getBarCodeUrl(String secretKey) {
String secretKey = "<generateSecretKeyで作成した秘密鍵>";
String email = "two_factor@exsample.com";
String label = "Sample Two Factor";
try {
return "otpauth://totp/"
+ URLEncoder.encode(label + ":" + email, "UTF-8").replace("+", "%20")
+ "?secret=" + URLEncoder.encode(secretKey, "UTF-8").replace("+", "%20")
+ "&issuer=" + URLEncoder.encode(label, "UTF-8").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
URLの詳しい仕様は、次の Google 公式 github のページを参照してください。
Key Uri Format
この後は、上で作成した URL を QRコードの画像にする必要がありますが、今回はブラウザ上で簡単に文字列をQRコード化できるツールを使って、QRコードの画像を作成しましょう。
ブラウザ上でQRコードを作成するサービスはいくつかありますが、今回は「CMAN – QRコード作成」というサービスを使って、getBarCodeUrlメソッドで作成した UR Lを QR コードにします。
6桁の認証コードの作成
次に、30秒おきに変化する Time-based One-Time Password (TOTP) を作成するコードを作ります。ここでは、generateSecretKeyメソッドで作成した秘密鍵をパラメータに、totpライブラリを使用してワンタイムパスワードを生成します。
public static String getCode(String secretKey) {
Base32 base32 = new Base32();
byte[] bytes = base32.decode(secretKey);
String hexKey = Hex.encodeHexString(bytes);
return TOTP.getOTP(hexKey);
}
認証コードを入力・検証するページを追加
秘密鍵の作成から、30秒おきに変化する6桁のワンタイムパスワードを生成する実装について解説してきました。
あとは、ID・パスワードでユーザー認証した後、6桁の認証コードを入力するページを用意し、ユーザーが Google Autehnticator アプリを使って入力した認証コードと、上で作成した getCode メソッドで作成した認証コードが一致するかを検証するロジックを追加すれば、2要素認証の実装の完成です。
近年では、銀行もネットバンキングでの利用が増えてきており、ネットの場合はID・パスワードの他に、スマホアプリや専用のカードを使ったワンタイムパスワードを使って、2要素認証を行います。