はじめに
賢明な諸兄諸姉におかれましてはソフトウェア・プロテクションのサービスを作ってみました というタイトルにそもそも違和感を感じられたかもしれません、普通はライブラリでしょうから
今回、ご紹介させていただきます Koshinto は、クライアント - サーバ型のソフトウェア・プロテクション・サービスです。サーバを Koshinto、アプリに組み込むライブラリを sansi と呼んでいます1。ライセンスファイルやライセンスキーを使わず、サービスと sansi を組み込んだユーザアプリだけで anti-piracy を実現しています
次の3点の設計上の工夫でライセンス・ファイルをなくしています
- アプリの束縛条件はライセンス・ファイルではなく koshinto サーバに保管
- アプリにリンクする sansi ライブラリ自体がクレデンシャルを兼ねる
- sansi ライブラリの取得は正当な所有者のみにシステムで制限
koshinto と sansi によってアプリ起動時の確認シーケンスは以下のようになります
- sansi が Node の情報を収集してサーバの Koshinto に送信
- Koshinto は登録されている条件に応じて sansi に Yes/No を返す
- sansi は受け取った Yes/No をアプリに返す
- アプリは Yes/No に応じて処理を続行/Exit
なぜライセンス・ファイルを排除したかというと、例えば IoT の Edge のようにストレージの保護が難しい2環境でライセンス・ファイルは弱点になると考えたのが一つ、もう一つは運用時のライセンス・ファイルの取り回しが煩雑3でなくしたいと思ったことが理由です
このような仕組みなのですが、この仕組自体が tamper されるようでは元も子もなく、つきましては本稿では Koshinto 自体への攻撃を防ぐために採用した手法をご紹介させていただければと存じます次第です。諸兄諸姉からのごご奇譚のないご意見・ご批判を広く参考にさせていただくことが賜れば至福の至と存じております次第です4。
想定される攻撃と Koshinto の保護手法
Token の保護
sansi と Koshinto との間でやりとりされる Token が改ざんされたり、Token が再利用されてなりすまされたりすることを防ぎます
1. 時刻情報とパラメータ・ハッシュの利用
Token にはパラメータとして時刻情報と、全パラメータを元にしたハッシュを含めます。
サーバは受け取った Token の時刻情報が自身の時計とくらべて有意にずれている場合は再利用とみなし、アプリケーションの起動を拒絶します。サーバからアプリケーションへの応答にもサーバの時刻情報を含め、クライアントは有意な時差をなりすましによる再利用とみなしてアプリケーションに対して起動しないように応答します
サーバは受け取った Token に付随するハッシュと、自身で計算したハッシュを比較して、一致しない場合には改ざんとみなします。さらに、サーバは応答 Token にもサーバで計算したハッシュをつけて返し、改ざんされた応答トークンをクライアントが発見するのに利用しています
2. 通信の暗号化
伝送路は HTTPS で保護されているとはいえ、悪意のユーザが通信をキャプチャして解析することを防ぐため Token は AES で暗号化した上で HTTPS に乗せています
AES は gcm モードでブロック化してます。IV は/dev/urandom を使って毎回違うものを生成しています。余談ですが go の AES-GCM の実装がちょっと特殊な上にドキュメントもなくて半日ほど悩みました5
ライブラリの保護
1. Bind_id の隠蔽
Bind_id の文字列はライブラリの中に定数で埋め込まれているのではなく、小さな種からつど生成してつかっているので、strings コマンド等で探しても無駄です ^^/
複数の Bind_id のライブラリをバイナリで diff を取るなどしてシードを見つけられる事がないように、シードの前後に Bind_id 事にランダムな(同じBind_id であれば同じ、異なるBind_idであれば異なるランダム文字列)文字列を埋め草にして、どこがシードなのかを隠しています
2. 名前の隠蔽
strip で消してもライブラリとして支障のない名前は消してあり、消せないものはランダムな文字列に obfscate してあります
3. ライブラリの管理
Koshinto のユーザーがクライアントの sansi ライブラリをダウンロードした段階で Bind_id はライブラリにバイナリで後述するようにセキュアに内蔵されているので、悪意のユーザーによる Bind_id の改ざんは防がれていると考えています
悪意というかちょっとおかしいユーザーが自分のライブラリを公開してしまったとしても、そのライブラリが通信する先はその人の Bind なので、その人の所有する Bind が汚染されるだけで、他のユーザーに迷惑をかけることはできません
Koshinto でカバーできない攻撃
1. OS の改ざん
Node を特定するための鍵となる情報を sansi ライブラリは proc ファイルや socket などから収集しているので、そこを改ざんされてしまうともうお手上げです。
でも、改ざんした OS でないと動かないようだとパクったアプリが広く広まってしまう心配はないので気が楽です
2. 根気よく binary analysis
sansi 自体が 50KByte 弱の、そんなに大きくもないライブラリなので、私は無理なのですが6優秀な人が根気よく読んだら読めてしまうものなのかどうかよくわかっていません。IDA とか Hydra みたいなツールのサポートがあるとどの程度のエンジニアが何日ぐらい使うとで読めちゃうものなのか、obfscate してバイナリサイズを増やしたらその時間が何倍ぐらい伸びるものなのかとか、そういうソフトウェア工学な興味もあったりもします7
むすび
Koshinto はこんなかんじで守ってあるので、ヤワなカジュアルハッキングぐらいなら余裕で弾き返せるだろうと考えている次第です
なにかお気づきの点がございましたらご奇譚なくご意見をいただければ幸いである旨、重ねて申し上げさせていただきます次第にございます
references
(https://qiita.com/UedaTakeyuki/items/a3ffb76f51b8daaf8ccd)
- Raspberry Pi のアドベントに Raspberry Pi での利用の観点での記事を書きました
- 個人開発のアドベントに個人開発なさってる方に気軽に使っていただけるような観点で記事を書く予定です
- Koshinto Docs Koshinto のドキュメント
- Koshinto Koshinto サービスの入り口
- Sansi_Example アプリケーション への Sansi の組み込み方の言語毎の解説とサンプル。現在 c, go, python, bash の例を用意しています
-
IoT Edge でよくストレージとして利用される SD Card に対してでも暗号化ストレージを用意することは可能なのですが、そもそも SD Card を5分ほどダマで借りてコピーしてしまえばあとはじっくり鍵を探すなり、そのままなりすまし端末として使うなり、いろいろとやられてしまいそう怖いです ↩
-
何十年も前の話ですが、flexlm でガードしてたアプリケーションのライセンスファイルの申請の受付とか発行とかに人手を要して高コストな上、恥ずかしながら人手ゆえの事故が起きがちだったので。人の存在ってシステムの阻害要因でしかないんですよね。そういうのは Let's Encript の ACME プロトコルみたいなので発行先の正当性の自動確認から送付まで自動化できるのかもしれませんが、無いで済むならないほうが ↩
-
いただきましたコメントに対してその場で反論して気まずい思いをさせたりすることは絶対にいたしませんので安心してお気軽にコメントいただければ幸いです。否定的なご意見こそ issue report に勝るともおとらぬコントリビューションであるとの信念の元、いつもあらゆるコメントを感謝して拝領させていただいております次第です。だいたい本稿の author なんてただの野良犬ですのでお気兼ねなしに諸兄諸姉の御高説をビシッと曰ってやってくださればよろしいかと存じます ↩
-
詳細はこちらに、実装コード見ないと使い方のわからないライブラリって嫌だな自分はそんなライブラリ書かないようにしようとその日は肝に命じたのでした ↩
-
単なるヘタレなのかもしれませんが、strip してあるだけで私はもう読むのギブです。自社製品のバイナリを IDA ありでも無理でした ↩
-
読めてないんですが最近は Software Deobfuscation Framework なんて実装もあるみたいだし。プレミアムコンテンツに関わってた頃は事件もおきないので安全に思えていた obfscation ですがちょっと最近、また個人的に腑に落ちなくなくなってたりしてます今日このごろでございます ↩