先日のブログで記事を書いた、ラズベリーパイを使って作ったIoTシステムに「脆弱性がある」と指摘を受けたので、素人ながら調べつつ最低限のセキュリティ対策を施した話。先日上記のブログ記事を公開したところ、「システムに脆弱性がある」という指摘を多々頂いた。システムというのは、「ラズベリーパイでインターホンを監視して、呼出音を検知したら条件に応じて解錠ボタンを押す」というものである。予定された配達か否かで2通りの解錠方法を考えていた。予定された配達の場合(廃止済み)予定していなかった配達の場合(現行版は常にこれ)受けた指摘は主に2つ。予定された配達時間帯に万が一悪意を持った人物が我が家のインターホンを鳴らすと中央玄関が開いて、中へ入れてしまう解錠コマンド用URLにアクセスした相手を識別しておらず、悪意を持った人物がコマンドを送ることが可能というものである。1つ目に関しては、確かにそうなので、「予定された配達の場合」のモードは廃止した。一方で、2つ目に関しては何らか認証が必要そうだ。そんなときに、IPA(情報処理推進機構)から連絡があった。脆弱性関連情報の届出受付システムを通じてこのシステムの脆弱性を届出てくれた人がいたようだ。内容を読むと脆弱性の詳細が事細かに書いてあり、素人の自分にとっては非常にありがたい内容だった。届出内容には対処方法も書いてあり、どうやらアクセス制御機能を実装する必要がありそうだ。「アクセス制御・・・?」状態だった私はまずそれを調べるところから始め、ようやくID/パスワードによる認証ができるようになったため、その過程を述べる。今回はDigest認証を使用した。アクセス制御機能を追加したシステム。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=(好きなポート番号)を加えてやれば良い。以前の記事ではモザイクを掛けていたとはいえほとんどURL丸見えで公開してしまったため、そりゃ見る人が見ればアクセスしてくるよね、という状態だった。(実際にアクセスログを見るといくつか外部からのアクセスが見られた。インターホンが鳴った時以外は何も反応しないわけだが)なので、URLはより複雑な名前に変更した。(個人使用のシステムなのでURLは非公開)(ただ、先程述べたように今回実装したDigest認証はURLは平文で送られるため、もしパケットキャプチャなどされた場合にはURLはわかってしまう)Digest認証の実装FlaskにDigest認証を実装する。今回は以下のページを参照した。qiita.comDigest認証はFlask拡張を入れれば簡単に実装できる。flask-httpauthで、スクリプト上でインポートする。HTTPDigestAuthFlaskインスタンスを生成する際は以下の通り。HTTPDigestAuth()で、実際にアクセスがあったときの処理部分に以下を記述。your_function():次に、ユーザネームを引数とし、あらかじめサーバに保存してある認証用の情報からパスワードを返す関数を実装する。ID/パスワードはDictionaryで持っておくのが楽で、スクリプト上に直接書いてもよいが、今回は外部ファイルから読み込んでいる。NoneID/パスワードなどの認証用情報は以下の記事を参照してあらかじめ外部ファイルに出力しておいた。neuryo.hatenablog.comここまで実装すると、http://[IPaddress]:[PORT_NUM]/[YOUR_URL]/にアクセスした際にID/パスワードの入力を要求される。認証画面これで最低限のアクセス制御が実装できた。アクセスする度ID/パスワードを打っていては配達員が帰ってしまうため、それらの情報はブラウザに保存した。その他セキュリティ対策にはならないのだが、私はこのシステムにアクセスする端末を決めているので、その機種以外でのアクセスをhttpヘッダのUser-Agentを使って弾いてみた。User-Agentについては以下の記事などが詳しいので、詳細はそちらを参照のこと。requestして、アクセスがあった際にrequest.headers.get('User-Agent')すると、例えば以下のような文字列が得られる。今回はこのシステムにアクセスする端末を限定し、それ以外の種類のものからのアクセスは弾くことにした。(もちろん、これは個人を識別できるIDでは全く無いため、気休めである)その他の例として、iPadから接続すると以下のようなUser-Agentが得られる。Safari/xxx.xxx「iPad」の文字列が入っており、iPadからのアクセスであることがわかる。まとめラズベリーパイを使った再配達撲滅システムに最低限のセキュリティ対策を行い、アクセス制御(Digest認証)を実装した。個人で使うシステムとしてはこれで十分と思うが、もし足りない部分があれば追加で実装していこうと思う。暗号の歴史について、以前読んだこの本が非常に面白かったのでここで紹介しておく。»プロフィールid:kaname_m平日は宇宙開発エンジニア。欲しいものは作ればいいじゃない。kmizta月別アーカイブはてなブログをはじめよう!kaname_mさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。