【VRChat】Discordメンバーのみ自動開錠する鍵ギミックを作りました【無料配布】
こんにちは、元気です!
今回も鍵ギミックの制作と配布ですが、VRChatの新機能のおかげで過去最高のものが出来ました!
本記事の構成
- ギミックの紹介と配布
- ギミックの仕組み解説
- セキュリティ対策 (中間者攻撃や改ざん対策など)
それでは行ってみましょう!
ギミック概要
Udonで出来たオートロック、UdonAutoLockです!
概要はDiscordを日常的に利用する方ならこの画像一枚で分かると思います。
VRChat側:
ワールド入室後10秒で自動開錠します。操作不要なのでこれ以上簡単になりようがないですね!
開錠したメンバーは操作パネルで同インスタンスの人を開錠できます。(機能有効時)
Discord側:
VRChat名の登録はメンバー本人にして頂くため、discordサーバー管理者は開錠させたいメンバーにロールを付与するだけです。
登録後は約1分でワールドに反映されます。もちろんワールドの更新は不要です。
セキュリティ機能:
開錠データは難読化(ハッシュ化)されます。
また後述しますが偽の開錠データを防ぐ機能なども備わっています。
その他、ギミックの設定やbotの運用方法は配布物のマニュアルを確認して頂ければと思います。
配布先
ギミック
配布先はこちらです。
マニュアル付属で記事を読まなくても使えます。是非使って頂ければ!
またギミックのライセンスはCC0で自由に改変・再配布可能です。
※ VRChat公式のモデレートガイドラインの対応については導入マニュアルの序章に記載しています。
Discord-bot
公開botを用意していますのでそちらをDiscordサーバーに導入して頂く形になります。
(導入URLはギミック同梱のマニュアルに)
さらに自分でbotを運用したい方のためにbotのコードもGitHubにてCC0で公開しています。
github.com
(なおbotはwebsocketで接続するので運用に固定グローバルIPアドレスは不要です。)
実績:
本ギミック及びbotは開錠メンバー200人以上のDiscordサーバーで10日前からテスト運用され基本的なバグは修正済みです。
デモ・サポートサーバー:
こちらのDiscordサーバーでUdonAutoLockのデモ体験や技術サポートを行っているので良ければ!(入退室自由です)
discord.gg
ギミックの仕組み
構成
本ギミックの構成を図示します。
本システムはVRChatでTextDownloading (インターネットから文字列情報をダウンロードする機能)が実装されたため制作が可能になりました。
ストレージサービスにFirebaseを選定したのはVRChatのホワイトリストサービスを全部試して今回の用途には適さなかったためですが、
Firebaseはアップロード時のデータ更新やダウンロードが1秒以内に完了するため非常に適してます。
(アップロードから更新に6分かかるサービスもありました)
現状、運用に掛かるコストはBotサーバーの維持費以外は無料です。
開錠データの構造
開錠データはバイナリでギッチリ詰まってます。これをBASE64形式でテキストに変換してVRChatに送ります。
個人情報はハッシュ化しているので開錠データから割り出す事は(通常)できません。
Discordサーバーごとのメンバーが最大3000人なのはU#の処理速度を考慮して制限したものです。udon2が来れば実質無制限にできると思います。
ロールはDiscordサーバーごとに8個まで設定可能です。これは開錠ギミックを複数使う際に各々のメンバーに開錠権を個別に割り当てるためです。
セキュリティ
本項はやや複雑ですので分からなければ読み飛ばして頂いても大丈夫です。
ただDownloading系の機能を使った鍵やセーブ・ロード機能を持つギミックの作成に興味のある人には参考になる内容ではと思います。
まず本ギミックにおけるセキュリティとは「開錠メンバー以外の人が開錠できないこと」です。
(ただし、VRChatアプリの改造などのチート行為は考慮しません)
また説明のため、非開錠メンバーかつ不正に開錠しようとする人を「攻撃者」と呼びます。
攻撃者の持つ情報
前提として攻撃者は配布ギミックのU#コードを読む、もしくはリッピング(ぶっこ抜き)によって開錠したいワールドのギミックに設定された値とコードを全て取得した結果、以下の重要な情報を持っていると仮定します。
- 攻撃者は自分を開錠する偽の開錠データを作成できるものとします。コードから逆算できるのでこれは防げません。
- ダウンロード先のURLもログや設定値から知っているものとします。
自動ダウンロードは安全か
しかしそれでも本ギミックは開錠データを自動でダウンロードして照合するので、攻撃者に偽の開錠データを入力する手段はなく、開錠できません。
と、思いますよね?
開錠されました。
開錠者はギミック製作でいつもお世話になっているkoyashiroさんです。
https://twitter.com/koyashiro
経緯はこうです。
koyashiroさんにギミックの仕組みを説明したときに「チート無しで開けられるかも」と仰られたので
「じゃあやってみてください」とデータを渡してみたところ見事突破されました。ありがとうございます!(地獄!)
こうして偽データはギミックに届けられる。
突破方法は「テキストの保存サーバーに成りすまして偽の開錠データをダウンロードさせる」というものです。
専門的には「中間者攻撃」という手法です。
以下はkoyashiroさんに聞いた攻撃手順を(知識の無い状態の私を想定して)噛み砕いた説明をします。
厳密性を欠く表現をあえて使っている部分もありますがご容赦ください。
VRChatと偽サーバーを接続
図で説明していきます。
まず偽開錠データを返す偽サーバーにアクセスするまでの手順です。
黒丸の数字に対応して説明します。【】内はkoyashiroさんが実施した内容です。
① 攻撃者は自分のPC内に偽httpsサーバーを建てます。偽の開錠データもここに置きます。
【nginxを使用】
② 攻撃者は嘘のDNS情報を使って、TextDownloadingの通信先を偽httpsサーバーに強制的に変更します。
【C:\Windows\System32\drivers\etc\hostsを書き換え】
VRChatに偽サーバーを信じさせる
偽サーバーに接続してしまってもhttps通信にはTLS(SSL)というセキュリティ機能が備わっていて、偽りの接続先を確認・拒否してくれます。
この確認をするために利用されるのがサーバー証明書です。
サーバー証明書とはhttps通信においてその通信先のサーバーの身元を保証する電子証明書です。
身元保証人は公に信頼性が認められたルート認証局が利用されます。(storage.googleapis.comならGoogle Trust Services LLC)
当然ですが攻撃者はルート認証局からサーバー証明書を発行してもらえないので、自分で自分を保証する自己署名証明書(オレオレ証明書)を発行して使わざるを得ません。
通信にそんな得体の知れない証明書は受け入れられないので普通はここでhttps通信が始まる前に拒否されて終わりです。
しかし攻撃者は以下のように細工します。
Windowsの「信頼された証明書ストア」には本来、公のルート認証局しか登録されてないのですがここに自己署名証明書を加えるのです。
③ 攻撃者は自己署名証明書を発行してWindowsの証明書ストアに登録します。
【opensslで証明書を発行】
④ 証明書を偽サーバーに設定して、VRChatとの接続時に送付します。
⑤ VRChatは証明書ストアを見て自己署名証明書の登録を確認し、信頼された通信先だと判断して通信を開始します。
⑥ 通信が開始されてしまえばあとは普通に偽の開錠データがVRChatにダウンロードされます。開錠!!!
以上で攻撃完了です。
ここで有識者のコメントを紹介します。
koyashiroさん 「マニュアル化すれば10分くらいで出来るんじゃないですかね」
ureishiさん 「マニュアル化しました」 (具体的すぎてここに載せられない!)
・・・コワイ!
対策
問題をまとめると「攻撃者は自分のローカルのString Loadingに対してどんなデータでもダウンロードさせられるし、ワールド製作者がそれを防ぐことは不可能」という事です。
攻撃者自身のPCのセキュリティを破壊するメガンテみたいなものですが確かに効果は抜群です。
このときVRChat自体には完全にノータッチなので確かにチートでもありません。
koyashiroさんは鍵が開けられて大変楽しそうでしたが、私は(一瞬とはいえ)この鍵ギミックを作るのを辞めようかと頭をよぎるくらいには効きました。
しかし同時に対策案も提案してくれました。
koyashiroさん 「公開鍵で署名検証するしかないんじゃないかな」
その手があった!!
詳細は省きますが、秘密鍵で署名されたデータ(トークン)は公開鍵で改ざんされて無いことを確認できます。(データの発行者を確認できるとも言える)
つまり攻撃者がどんなデータを入力してもそれが本来の開錠データから少しでも違うデータならギミック側で拒否することが出来ます。
そしてトークンの署名検証をVRChatで可能にするライブラリがkoyashiroさん作のUdonJwtです。(詳細は前回の記事を参照してください)
またお世話になるとは・・・
トークンを利用したシステムの仕組みは図のようになります。
結果として鍵ギミックは正しい発行者のデータのみ受け付ける事ができます。
また鍵ギミック及び公開鍵から正しいトークンを作ることはできないためリッピングに対しても耐性があります。
以上を本ギミックに実装しました。
トークンのつくり方:U#と和解しよう
ギミックのプログラム言語であるU#はネイティブC#より2000倍ほど遅い事が分かっています。(前回記事参照)
よって普通にjwtトークンに全ての開錠データを含めると署名検証が実用時間で終わりません。
だからJwtトークンに含めるデータを少なくする工夫が必要です。
図で示します。
対策②の通り、データの小さいハッシュのみをtokenに含め開錠データと一緒に送ります。(文字列を連結しています)
この場合、署名検証が成功したときに改ざんされてないといえるのはトークンの中のハッシュだけです。
よって鍵ギミック内で(改ざんされたかもしれない)開錠データをハッシュ化してトークンのハッシュと同じ値かを確認することで改ざんされてないことを確認できます。
以上で実用的速度で動く中間攻撃対策が構築可能です。お疲れさまでした・・・!
(Udon2は処理速度が速くなるらしいので実装されたら普通にトークンを作ります。)
さらにセキュリティ!永久パスポート対策
中間者攻撃を防いでもまだ考慮すべきことがあります。
仮定の話ですが、あなたがDiscordサーバーの運用者(コミュニティ管理者)だったとします。
そしてpublicワールドにメンバー限定の特典コンテンツを置いた特別室への鍵ギミックを設置したとします。(メインコンテンツは誰でも閲覧可能)
① 趣味が中間者攻撃だけど根は良さそうなAさんをあなたは開錠メンバーに入れました。
② しばらく経ち、Aさんが言うほど良い人じゃないと分かったのであなたは開錠メンバーから外しました。
この場合Aさんが開錠メンバーの①の期間は、Aさんを通す内容で開錠データ(およびトークン)が生成されます。
ここでAさんは言うほど良い人じゃなかったので①の期間の間にログから開錠データのダウンロード先URLを入手、
自分を通す開錠データを入手して保管していたとします。
Aさんがメンバーを外されたあとに抜いた開錠データで中間者攻撃をするとどうなるでしょう?
もちろん開錠します。
この永久パスポート問題は鍵に有効期限を設けることで対策できます。
本ギミックで行った対策を説明します。
JWTトークンには有効期限を設定できます。そしてUdonJwtはこれを(標準機能として)署名検証の際にチェックしてくれます。("exp" Claim)
そしてbotサーバーは有効期限より短い周期で鍵を更新します。
鍵の更新はbotサーバーが自動的に行うので、Discordサーバー運用者は意識する必要はありません。
以上の仕組みでAさんがある時点で取得した鍵はすぐに期限切れになって使えなくなり、特別室に入れなくなります。
セキュリティ対策は以上です。読んで頂いてありがとうございました!
スタンドアロンにはJwtLockerを
今回のUdonAutoLockの公開に伴い、前回の鍵ギミック「JwtLocker」のワンタイムパスワードbotの運用は終了します。ご利用ありがとうございました。
ただしdiscord-botを用いない単体の鍵ギミックとしては現在も強固で有用のため引き続き配布を続けるのでよろしくお願いします。
ちなみにですがJWT Lockerの付属アプリ「JWT Utility」を使うとVRChat名付きトークンをローカルで発行できるので、自分で中間者攻撃対策ギミックを作りたい方の役に立つかもです。コードもMITで公開しています。
ポエム:やっていきをしましょう
前回の記事と同じ話ですが、私からはVRChatのセキュリティ対策に対する意見はありません。 対策は常に実装工数やパフォーマンス低下とトレードオフで、そのバランスをとった結果が今のVRChatだと考えているからです。
だから今後もVRChat上でやっていきます!
まとめ
- 自動開錠するギミックを作ったよ。便利だから使ってみてね。
- セキュリティの勉強までできるVRChatは最高だなぁ!
- udon2・・・待ってるよ!
ギミック配布先:
booth.pm
それでは良きVRChatライフを!!
参考文献
① 【VRChat】リッピングでパスワードギミックが破られるので公開鍵を利用した対策品を作りました【無料配布】 - チカラの技術(前回記事)
② JWT(JSON Web Token)って何に使うの?仕組みとその利便性 #JavaScript - Qiita
③ SSLサーバ証明書とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
謝辞
ありがとうございます!
koyashiroさん
いつもお世話になっています!セキュリティやストレージサービスの相談に乗って頂きました。そして対策の要であるUdonJwtを作成してくれたのもkoyashiroさんです。
感謝、感謝です!
https://twitter.com/koyashiro
ureishiさん
koyashiroさんと共に中間者攻撃の手順を私にも分かる形で教えて頂きました。対策のための理解が深まりました!
https://twitter.com/aivrc
スペシャルサンクス
ギミックのデバッグや助言を頂きました。ありがとうございます!
CHIHAYAさん、HAYA-CHANさん、ヨドコロちゃんさん(暗闇シェーダーのCC0配布の許可も頂きました)
(匿名の方でストレージサービスの紹介をしてくれた方も!)