Android アプリで外部サービス認証する際にドメイン名を確認できるようにする

Android アプリで Twitter などの外部サービスを利用した認証を行う場合、開発者にセキュリティ上の注意が求められます。この記事では、私がアプリを作った際にどんな配慮をしたか、他の著名アプリではどういう対策を行っているかを紹介します。

ダイアログでURLを確認できるようにする

先日 Android アプリ同人誌在庫速報(play.google.com)を作ってみました。このアプリは「ツイートを行う」が主目的なため、 Twitter アカウントでの認証が必須となっています。認証は WebView を使って api.twitter.com 上の認証画面から行うのですが、ブラウザでウェブサイトを見るときとは違い、アプリには「アドレスバー」が存在しません

オリジナル画像
図1アプリで Twitter の認証画面を開いたところ

ユーザーの立場で考えると、このような状態でパスワードを入力するわけにはいきません。今開いているこの画面が本当に Twitter のサイトなのか確認できないからです。この状況に対し、アプリの開発者は何をすればいいのか。以下のような対策が考えられます。

  • アドレスバーを自作する
  • 認証画面はブラウザで開くようにする
  • (アドレスバーではないが)URLを確認できる機能を作る

おそらく、もっともユーザーに分かりやすいのはアドレスバーの自作だと思います。アドレスバーといっても、ブラウザでのそれのようにURLを入力してそのページに飛ぶ機能は必要ありません。現在のURLを表示したり、そのページがHTTPSなら鍵アイコンも表示して、TLS証明書を確認できるようにすればいいのです。ただ、私は Android アプリの開発歴がまだ半月の状態でして、このような機能を作る技術力がありませんでした。

2番目のブラウザで開く機能については後述するので、いったん説明は飛ばします。

今回、私は3番目の機能を採用しました。やり方は非常に単純で、「現在ページのURLを確認」というメニューを設け、ダイアログにURLを表示するものです。

オリジナル画像
図2メニュー内に「現在ページのURLを確認」がある
オリジナル画像
図3ダイアログにURLが表示される

メニューを押した際に呼ばれる onOptionsItemSelected メソッド内のコードはこんな感じです。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.url:
            /* 現在ページのURLを確認 */
            final Uri url = Uri.parse(webView.getUrl());

            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("現在ページのURL");
            builder.setMessage(url.toString());
            builder.setNeutralButton("ブラウザで開く", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    webView.getContext().startActivity(new Intent(Intent.ACTION_VIEW, url));
                }
            });
            builder.setNegativeButton("閉じる", null);
            builder.create().show();

            return true;
    }
    return super.onOptionsItemSelected(item);
}

このようにすることで、ユーザーは

  • このページが暗号化されていること(URLが https: で始まっていること)
  • ドメイン名が api.twitter.com なこと

が分かります[1]。また、 api.twitter.com というドメインが本当に Twitter のものかどうか分からないユーザーのために「ブラウザで開く」ボタンを設けています。ブラウザで開けばアドレスバーの鍵アイコンからTLS証明書を見ることができますから、 Twitter 社が管理するドメインであることが確認できます[2]

これでセキュリティ上の問題点はつぶせていると思うのですが、どのようなサイトでも同じ位置にあるアドレスバーと違って、統一的な機能ではないため、ユーザーに「このような手順で確認すれば安全」ということが伝わりにくいものとなっています。

開発コストと必要な技術力はもっとも低いものですが、やり方としてあまりいいものだとは思っていません。

認証画面をブラウザで開く

特定ジャンルに限らないニュースの閲覧はSmartNews(www.smartnews.com)を使っているのですが、このアプリにはニュース記事を各種SNSやメモツールで共有できる機能があります。しかし以前 Twitter 認証を行おうとしたところ、アプリ内で認証画面が開かれたためアドレスバーがなく、また代替機能も用意されていない状態でした。「これではセキュリティ上の問題があるからURLを確認できるようにすべきではないか」という意見を送ったこともあるのですが、先日再び同機能を試してみたところ、認証画面がブラウザで開くよう改修されていました。

オリジナル画像
図4SmartNews アプリで右上の共有ボタンを押したときのメニュー
オリジナル画像
図5Twitter 認証を選択したときの、ブラウザで開く旨の確認ダイアログ
オリジナル画像
図6OKするとブラウザが立ち上がる(アドレスバーや鍵アイコンが見える)

ブラウザでログイン & 認証を行った後は自動でアプリに戻るのですが、どうやっているのかと通信内容を覗いたところ、いったんスマートニュース社の管理するページへリダイレクトした後、

jp.gocro.smartnews://auth/succeeded?service=twitter&userName=(ユーザー名)

が呼ばれていました。「jp.gocro.smartnews」というアプリ固有のスキームにアクセスすることで、「アプリを起動する」みたいな処理が走り、 service や userName のパラメーターをプログラムで受け取ることができるのでしょうかね。詳細は分かりませんが、ともかくブラウザを利用することで、独自にアドレス確認機構を作る必要がないというのは大きなメリットだと思います。

また、ブラウザにパスワードを覚えさせているユーザーにとっては、(アプリ内で認証画面を開いたときと違って)登録されているパスワードを呼び出せば良くて、わざわざ打ち込む必要がないというのも大きなメリットでしょう。

このように非常に優れたやり方だと思うのですが、なぜか Evernote 認証については現在もなおアプリ内での認証画面となっており、URLが確認できない状態です。 Twitter や Facebook 認証は改善されたことから、そういう状態がマズいことはスマートニュース社側も把握しているはずですが、なにか技術的な問題があって対応できないのでしょうかね。

  • [1]アプリの開発者に悪意があったり、コードにバグがあったりして、実際とは異なるURLを表示している可能性も捨てきれません。ただ、アプリをインストールする時点で開発者のことは信用している前提ですので、そのような点は別問題とします。
  • [2]残念なことに、現実には証明書の確認ができないブラウザも一部存在します。少し古い(2014年)記事ですがスマホブラウザでSSL/TLS証明書を確認するを参照ください