Amazon Dash Buttonで二要素認証してみた


最近パスワードだけだと怖いのでなるべく二要素認証をするようにしてるのですが、スマートフォン開いて6桁の数字を打つのは割としんどいと思いAmazon Dash Buttonを使ってみました。

結論を言うとPCにsecretを置いているので二段階認証というべきかも知れませんし、そもそも安全性がどうか微妙なのでネタとして軽い気持ちでやってみました。

# 概要
Amazon Dash ButtonをIoTとして使う方法を見ていると、ARPリクエストを拾って処理させるのが普通のようでした。
そこで、Amazon Dash Buttonを押したときのARPで発火して二要素認証の6桁の数字を勝手に入力してくれるようにしてみました。

# 環境
- OS
	- macOS Sierra
- node.js
	- v7.4.0
- Amazon Dash Button
	- 天然水


# 参考
http://albno273.hatenablog.com/entry/2016/12/07/Amazon_Dash_Button_%E3%81%A7%E3%80%8C%E3%83%9C%E3%82%B9%E3%81%8C%E6%9D%A5%E3%81%9F%E3%80%8D%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%92%E4%BD%9C%E3%82%8B


# 実装
基本的には、Mac上でARPを待ち受けてリクエストが来たらtokenを生成して入力するだけです。
実装にはnode.jsを使いました（ほとんど使ったことないです）。

## TOTPのtoken
まずTOTPのtokenを生成します。
TOTPの仕組みは解説しませんが基本的にsecret_keyを共有していて、時刻をseedにワンタイムトークンを生成する感じです。
なのでsecret_keyさえ分かればスマートフォンのアプリなどを使わなくても簡単に自動生成できます。
実装もシンプルなので自分でも書けますが、今回の主題はそこではないので以下のライブラリを使いました。
https://github.com/guyht/notp

tokenは以下のようにすると生成できます。

```js
var notp = require('notp');

var secret_key = 'secret key for user... could be stored in DB';
var token = notp.totp.gen(secret_key);
```

とても簡単です。secret_keyはQRコードとか読みこむと書いてあります。
例えばSlackで二要素認証を有効にするときに表示されるQRコードの中身は以下のようになっています
`otpauth://totp/Slack%20(team_name):test@example.com?secret=XXXXXXXXXXXXX&issuer=Slack`

このsecretの部分がsecret_keyに該当します。
ただしこれらはbase32エンコードされているので、デコードしたものを渡します。
まとめると以下のような感じです。

```js
var notp = require('notp');
var base32 = require('thirty-two');

var key = 'secret key for user... could be stored in DB';
var decoded_key = base32.decode(key);

var token = notp.totp.gen(secret_key);
```

## Amazon Dash Buttonの待ち受け
ココらへんは最近記事も多いので書くまでもないですが、以下のライブラリを用いました。
https://www.npmjs.com/package/dash-button

参考にさせていただいたページに従って以下のように書きます。

```js
const DashButton = require("dash-button");

const MAC_ADDR = "xx:xx:xx:xx:xx:xx";
let button = new DashButton(MAC_ADDR);

button.addListener(() => {
    // 何かの処理
});
```

## キーイベントの送信
これも参考ページ同様にrobot.jsを使いました。
READMEのように文字を打ったあとEnterを押すようにします。
tokenを計算して自動で入力するようにします。

```js
// Type token then press enter.
var robot = require("robotjs");

// Type TOTP token
robot.typeString(token);

// Press enter.
robot.keyTap("enter");
```

## 全体
上記をまとめたものが以下です。
シンプルなので特に難しいところはないかと思います。

```js
const DashButton = require("dash-button");
const notp = require('notp');
const base32 = require('thirty-two');
const robot = require("robotjs");

var key = 'XXXXXXXXXXXXXXX';
key = base32.decode(key);

const MAC_ADDR = "88:71:e5:xx:xx:xx";
let button = new DashButton(MAC_ADDR);

button.addListener(() => {
    console.log("TOTP!");
    token = notp.totp.gen(key);
    robot.typeString(token);
    robot.keyTap("enter");
});
```

# 実験
ということで実際に使ってみましょう。
今回はSlackの二要素認証で使ってみます。
設定画面からQRコードを表示してsecretを取得しておきます。

そして上のプログラムを適当に `totp.js` とかで保存して実行します。
secretやMACアドレスは自分のを埋めておきます。

```
$ vim totp.js
$ sudo node totp.js
```

これで待ち受け状態になったので、Slackにログインしてみます。
Slackのauthentication codeを入力する画面でAmazon Dash Buttonを押します。
すると勝手に数字が入力されログインが成功します。

以下のような感じです。
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/56222/f32082b1-c6b9-9970-1d15-b0985d29a67a.gif "output.gif")

# まとめ
Amazon Dash Buttonで二要素認証できたら便利だなと思って試してみました。
実際やってみると結構便利で実運用したい気持ちになりました。
ですが、PC内にsecretを保存しているのでPCが感染とかするとダメですし、あまり実用的ではなさそうです（あと1サービスに付き1個Dashボタンが必要なのが致命的...）。
rootしかread権限ないようにして動かしておけば多少はマシかなと思ったりしました。
あまり重要でないサービスになら使ってもいいかもしれません。

本当はARPの待ち受けはRaspberry Piでやって、PCに対してtokenだけ送るようにしたかったのですが一旦PCでやってみました。
secretはRaspberry Piに置いて別ネットワークで動かしておき、PCへはBluetoothとかでtoken送ってあげればセキュリティ強度も高そうだし実際に使えそうかなと思ってます。
プログラム可能なAWS IoT ボタンが日本でも出れば解決ですが。