ラズベリーパイを使ったIoTシステムに脆弱性があると指摘があったためセキュリティ対策を施した話
概要
先日のブログで記事を書いた、ラズベリーパイを使って作ったIoTシステムに「脆弱性がある」と指摘を受けたので、素人ながら調べつつ最低限のセキュリティ対策を施した話。
(ちなみに、上の記事ははてなブログの週間ランキング2位になってしまった)
今週のはてなブログランキング〔2020年2月第1週〕 - 週刊はてなブログ
背景
先日上記のブログ記事を公開したところ、「システムに脆弱性がある」という指摘を多々頂いた。システムというのは、「ラズベリーパイでインターホンを監視して、呼出音を検知したら条件に応じて解錠ボタンを押す」というものである。
もともとは、予定された配達か否かで2通りの解錠方法を考えていた。
受けた指摘は主に2つ。
もともと予定された配達時間帯に万が一悪意を持った人物が我が家のインターホンを鳴らすと中央玄関が開いて、中へ入れてしまう
解錠コマンド用URLにアクセスした相手を識別しておらず、悪意を持った人物がコマンドを送ることが可能
というものである。
1つ目に関しては、確かにそうなので、「予定された配達の場合」のモードは廃止した。一方で、2つ目に関しては何らか認証が必要そうだ。しかしやり方がわからない。
そんなときに、IPA(情報処理推進機構)から連絡があった。どうやら脆弱性関連情報の届出受付システムを通じてこのシステムの脆弱性を届出てくれた人がいたようだ。
脆弱性関連情報の届出受付:IPA 独立行政法人 情報処理推進機構
届出られた内容を読むと脆弱性の詳細が事細かに書いてあり、素人の自分にとっては非常にありがたい内容だった。届出内容には対処方法も書いてあり、どうやらアクセス制御機能を実装する必要がありそうだ。
「アクセス制御・・・?」状態だった私はまずそれを調べるところから始め、ようやくID/パスワードによる認証ができるようになったため、その過程を述べる。今回はDigest認証を使用した。
Basic認証、Digest認証
アクセス制御とは、要はあるシステムに対し誰か何をしていいのかを設定したり識別したりして、それに基づいて操作を拒否したり許可したりすることである。
思いつくのはID/パスワードによる認証だが、ID/パスワードによる最も基本的な認証方法にBasic認証とDigest認証がある。
私はアクセス制御については素人なので、詳細な説明は他の記事を読んでもらうとして、ここではごくごく簡単に説明する。
Basic認証:実装が容易で、簡易的な認証方法。ログインIDとパスワードを入力すると、それらがBase64でエンコードされてサーバへ送信される。サーバはその情報をデコードし、ID/パスワードが一致していたらOKとする。
→Basic認証はとても実装が楽だが、認証情報がただBase64でエンコードされているだけで、誰でもデコードできるので、実質平文で送っていることになる。公衆Wifiで認証情報を入力するな、というのはこういうことのようだ。なので、一般的に通信を暗号化するSSL(HTTPS)と一緒に使うようである。
Digest認証:Basic認証が実質平文でID/パスワードを送っているのに対し、Digest認証ではパスワードをハッシュ化して送信する。ハッシュ化、というのは一方向関数を使ってある固定長のメッセージに置き換えることで、ハッシュ値からもとの値(ID/パスワード)を解析することを困難にするもの。サーバ側ではID/パスワード情報があるので、同様にハッシュ値を計算して、それらが一致すれば許可する、といった具体のようである。
→今回はDigest認証を使ってアクセス制御を実装することとした。
なお、Digest認証ではアクセス先のURLやユーザ名はハッシュ化されず平文で送られるため、それらも隠したい場合はHTTPSによる通信暗号化をする必要がある。
最低限の対策
ポート変更
このシステムではラズベリーパイ側のサーバはFlaskで実装している。Flaskはデフォルトで5000番ポートを使用するが、まずはこれを変更する。49513~65535が自由に使えるポート番号のようだ。
もちろん、これだけでは対策にはなり得ないわけだが、デフォルトポートを狙ってくるようなアクセスには効果があるだろう。
Flaskでポート番号を変更する場合は、起動時の引数にport=(好きなポート番号)
を加えてやれば良い。
(以下の例ではFlaskの処理をスレッド化して起動している、これについては過去記事参照)
rest_service_thread = threading.Thread(name='rest_service', target=app.run, args=('0.0.0.0',), kwargs=dict(debug=False,port=PORT_NUM)) rest_service_thread.start()
URL変更
以前の記事ではモザイクを掛けていたとはいえほとんどURL丸見えで公開してしまったため、そりゃ見る人が見ればアクセスしてくるよね、という状態だった。(実際にアクセスログを見るといくつか外部からのアクセスが見られた。もちろんインターホンが鳴った時以外は何も反応しないわけだが)
なので、URLはより複雑な名前に変更した。(個人使用のシステムなのでURLは非公開)
(ただ、先程述べたように今回実装したDigest認証はURLは平文で送られるため、もしパケットキャプチャなどされた場合にはURLはわかってしまう)
Digest認証の実装
FlaskにDigest認証を実装する。今回は以下のページを参照した。
Digest認証はFlask拡張を入れれば簡単に実装できる。
pip3 install flask-httpauth
で、スクリプト上でインポートする。
from flask_httpauth import HTTPDigestAuth
Flaskインスタンスを生成する際は以下の通り。
# Flaskインスタンス生成 app = Flask(__name__) app.config['SECRET_KEY'] = 'your secret key' auth = HTTPDigestAuth()
で、実際にアクセスがあったときの処理部分に以下を記述。
@app.route("/YOUR_URL/") @auth.login_required def your_function():
次に、ユーザネームを引数とし、あらかじめサーバに保存してある認証用の情報からパスワードを返す関数を実装する。ID/パスワードはDictionaryで持っておくのが楽で、スクリプト上に直接書いてもよいが、今回は外部ファイルから読み込んでいる。
@auth.get_password def get_pw(username): # ユーザデータ読み込み users = pickle_load('./users.pickle'); if username in users: return users.get(username) return None
ID/パスワードなどの認証用情報は以下の記事を参照してあらかじめ外部ファイルに出力しておいた。
ここまで実装すると、http://[IPaddress]:[PORT_NUM]/[YOUR_URL]/
にアクセスした際にID/パスワードの入力を要求される。
これで最低限のアクセス制御が実装できた。なお、アクセスする度ID/パスワードを打っていては配達員が帰ってしまうため、それらの情報はブラウザに保存した。
その他
セキュリティ対策にはならないのだが、私はこのシステムにアクセスする端末を決めているので、その機種以外でのアクセスをhttpヘッダのUser-Agentを使って弾いてみた。
User-Agentについては以下の記事などが詳しいので、詳細はそちらを参照のこと。
from Flask import request
して、アクセスがあった際にrequest.headers.get('User-Agent')
すると、例えば以下のような文字列が得られる。
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/xxx.xx (KHTML, like Gecko) Chrome/xx.x.xxx.xxx Safari/xxx.xx
(xxxはバージョン番号)
文字列を見るとわかるように、アクセス元のOSなどがここからわかる(上の例の場合はWin10 64bit)。
今回はこのシステムにアクセスする端末を限定し、それ以外の種類のものからのアクセスは弾くことにした。 (もちろん、これは個人を識別できるIDでは全く無いため、気休めである)
その他の例として、iPadから接続すると以下のようなUser-Agentが得られる。
Mozilla/5.0 (iPad; CPU OS 13_3 like Mac OS X) AppleWebKit/xxx.xxx (KHTML, like Gecko) CriOS/xxx.xxx Mobile/xxxx Safari/xxx.xxx
「iPad」の文字列が入っており、iPadからのアクセスであることがわかる。
まとめ
ラズベリーパイを使った再配達撲滅システムに最低限のセキュリティ対策を行い、アクセス制御(Digest認証)を実装した。
個人で使うシステムとしてはこれで十分と思うが、もし足りない部分があれば追加で実装していこうと思う。
余談
暗号の歴史について、以前読んだこの本が非常に面白かったのでここで紹介しておく。
- 作者:サイモン シン
- 出版社/メーカー: 新潮社
- 発売日: 2001/07/31
- メディア: 単行本