Secure Viewを用いてタッチイベントをフィルタリングする


Android 2.3から、Viewのセキュリティーを強化するためのSecureViewが採用されました。

ユーザーのタッチ動作に対し、タッチフィルタリングを用いることで、なりすまし等のクリック詐欺被害を防ぐことができます。

具体的な実装方法は続きをご覧ください。

タッチフィルタリングが施されていない場合の動作

それではまず、タッチフィルタリングが施されていない場合の実装と動作を見て下さい。

samplenosecure.xml(一部抜粋)

1
2
3
4
5
6
7
8
<Button android:text="次のページへ移動"
android:id="@+id/Button_NextPage"
android:layout_gravity="center_horizontal"
android:filterTouchesWhenObscured="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
 
</Button>

SecureActivity.java(一部抜粋)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void onClick(View v){
    if(v.getId() == R.id.Button00){
        //偽トースト生成
        Toast toast = Toast.makeText(SecureActivity.this, "安全です", Toast.LENGTH_LONG);
        toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0);
        // トースト表示
        toast.show();
    }else if(v.getId() == R.id.Button01){
        AlertDialog.Builder b = new AlertDialog.Builder(this);
        b.setTitle("Alert");
        b.setMessage("ボタンは押されました。100円振り込んでください。");
        b.show();
    }
}

まず初めに、「push!」ボタンを押してください。そして、「次のページへ移動」ボタン上に表示される「安全です」ダイアログをタッチします。

すると、「ボタンは押されました。100円振り込んでください。」とダイアログが表示されます。

タッチイベントがフィルタリングされていないため、ボタンが押されたときの処理が実行されてしまい、クリック詐欺に引っかかってしまっています。

タッチフィルタリングが施されている場合の動作

それでは次に、タッチフィルタリングが施されている場合の実装と動作を見てみましょう。

samplesecure.xml

1
2
3
4
5
6
7
<Button android:text="次のページへ移動"
android:id="@+id/Button_NextPage"
android:layout_gravity="center_horizontal"
android:filterTouchesWhenObscured="true"</span>
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>

SecureActivity.javaの方は、先ほどと変わりありません。

先ほどと同様、まず初めに、「push!」ボタンを押してください。そして、「次のページへ移動」ボタン上に表示される「安全です」ダイアログをタッチします。

すると今度は、「ボタンは押されました。100円振り込んでください。」のダイアログは表示されません。タッチイベントが無効化され、onClick()メソッドが起動していないためです。

samplenosecure.xmlとの実装上の違いは、android:filterTouchesWhenObscured 属性が追加されているところです。 この属性が”true“に設定されている場合は、タッチフィルタリングが有効になっており、タッチした際に属性を設定したビュー(ここでは「次の画面へ移動」ボタン)の上にトーストやダイアログ等が重なっている場合には、タッチイベントが無効化され、ボタンが押されたときの処理が実行されません。

タッチフィルタリングをデモンストレートする

では、どのような仕組みでタッチイベントが無効化されているのでしょうか。以下の実装を見てください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void onClick(View v){
    if(v.getId() == R.id.Button_Toast){
        //偽トースト生成
        Toast toast = Toast.makeText(SecureActivity.this, "安全です", Toast.LENGTH_LONG);
        toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0);
        // トースト表示
        toast.show();
    }else if(v.getId() == R.id.Button_NextPage){
        AlertDialog.Builder b = new AlertDialog.Builder(this);
        b.setTitle("Alert");
        b.setMessage("ボタンは押されました。100円振り込んでください。");
        b.show();
    }
}
private void setTouchFilter(Button button) {
    button.setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
                //フィルタリング時の処理
                return true;
            }
        return false;
        }
    });
}

これは、Android SDK付属の「API Demo」に内包されている「Secure View」のサンプルコードの一部を引用しており、どのような仕組みでタッチイベントがフィルタリングされて無効化しているのかをソースコードでデモンストレートしたものです。

ポイントは、次の部分です。

1
if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {

FLAG_WINDOW_IS_OBSCURE フラグによって、タッチされたビュー(今回は「次の画面へ移動」ボタン)が、トーストやダイアログ等によって部分的に(もしくは完全に)隠されているかどうかを判定し、隠れていた場合は”true“を返すことで、タッチイベントの処理を中断しています。よってonClick()メソッドにイベントは飛んで来ず、「ボタンは押されました。100円振り込んでください。」ダイアログは表示されません。