暗号化された個人メッセージ

エンドユーザー間のプライベートで暗号化されたメッセージングを可能にするプラグインの委託をご希望です。

:mega: このプラグインは現在存在し、公式にサポートされています。詳細は Discourse Encrypt (deprecated) をご覧ください。

現状、安全でプライベートな長文コミュニケーションのためのオンライン手段は極めて限られています。既存のツールは非常に難解で、使用するには高度な技術スキルが要求されます。その結果、大多数の「プライベート」なコミュニケーションは、Telegram、Skype、WhatsApp などの「短文」形式で行われています。さらに、この大量コミュニケーションのモードは、クローズドソースのツールやクローズドソースのプロトコルに依存しているため、適切に監査可能ではありません。

私たちは、専用の Discourse プラグインとして、使いやすく監査可能な暗号化メッセージングソリューションを構築したいと考えています。

これを実現するために、以下の原則に基づいて構築します。

  1. Web Crypto API を基盤としてソリューションを構築する
  2. サーバーにユーザーごとに暗号化された秘密鍵公開鍵を保存する
  3. サーバーにプライベートメッセージの各参加者ごとに暗号化された会話鍵を保存する(エンドユーザーの秘密鍵を使用して暗号化)
  4. すべての Markdown とタイトルを、メッセージごとに**生(raw)**の暗号化形式で保存する(会話鍵を使用して暗号化)
  5. サーバーが「誰が、いつ、誰と話したか」を知っているという制限を受け入れる(守るのは「何を」話したかだけ)。また、このコンテンツに対する検索機能は動作しないという制限も受け入れる。

提案されるエンドユーザー体験

Jane がユーザーページにアクセスし、「暗号化メッセージを有効化」ボタンをクリックします。クリックすると秘密の入力を求められます。UI は、この秘密を忘れた場合、暗号化されたメッセージに決してアクセスできなくなることを明確に説明します。

暗号化メッセージが有効化されると、メッセージ作成 UI に新しい [ ] が表示され、テキストは [ ] メッセージを暗号化 となります。これは、すべての受信者が「暗号化メッセージ」を有効にしている場合にのみクリック可能です。一部の受信者が公開鍵を持っていない場合、クリックしようとした際に「申し訳ありませんが、一部の参加者に暗号化メッセージが有効化されていません」と表示されます。

Jane は Pete と非公開で話したいと考え、参加者リストに彼を追加し、メッセージを暗号化 をクリックして Pete にメッセージを送信します。

Pete は Jane からの暗号化メッセージが届いたという通知を受信します。タイトルはなく、暗号化されたメッセージが存在することと、そのメッセージへのリンク(メール通知と Web 通知の両方)だけが表示されます。

Pete が暗号化メッセージを有効にした元のデバイスにいれば、秘密のパスフレーズは既に入力されており、認証情報は IndexedDB に保存されています。Pete が新しいデバイスにいれば、暗号化メッセージを有効にするために秘密のパスフレーズの入力を求められます。

UI は、メッセージが暗号化されていることを示すオーバーレイまたは視覚的なヒントを明確に表示します。

Jane と Pete の間でコミュニケーションが始まると、いずれかの参加者が公開鍵を持っている限り、新しい人をそのメッセージに招待できます。

Pete も Jane も、いつでも秘密のパスフレーズを変更できます。変更した場合、秘密鍵は新しいパスフレーズを使用して再暗号化され、サーバーへ送信されます(V1 では、実際の秘密鍵の変更ではなく、パスフレーズの変更のみを許可します)。

技術的な詳細

いかなる時点でも、暗号化されていないプライベートな会話や認証情報がサーバーに送信されることはありません。サーバーに対して信頼を置く唯一の点は、クライアントに送信される JavaScript ペイロードが改ざんされていないことです。

秘密鍵と公開鍵のペアは 100% クライアント側で生成され、その後、秘密鍵は AES 対称暗号化を使用して暗号化され、サーバーへの安全な保管のために渡されます。パスフレーズは、サーバーに保存されたランダムに生成されたソルトを使用してストレッチ(伸長)されます。キーストレッチは、クライアント側で PBKDF2(Web Crypto API で利用可能)を使用して行われます。秘密鍵/公開鍵のペアは、エクスポート不可能な CryptoKey オブジェクトとしてクライアントの IndexedDB に保存されます。エクスポート可能な CryptoKey データは、可能な限り速やかにメモリから削除されます。

暗号化された秘密鍵と公開鍵は、ユーザーのカスタムフィールドに保存されます。暗号化された秘密鍵の読み書きが許可されるのは current_user のみであり、すべてのログインユーザーは全ユーザーの公開鍵の読み取りを許可されます。

会話鍵は、会話の開始時または招待時にクライアント側で生成され、会話に関与するすべての公開鍵を使用して暗号化されます。このデータは専用テーブル (user_id, topic_id, encrypted_conversation_key) に保存され、この行は会話内の任意のユーザーによって作成可能ですが、current_user == user_id の場合にのみ読み取り可能です。

下書きが暗号化されていない状態で保存されないようにするなど、暗号化されていないデータがサーバーに漏洩することのないよう特別な注意を払います。

広範なエラーを回避するため、作曲器(composer)の編集とプレビューには新しい DOM 要素(新しい ID を持つ)を使用します。現在の .d-editor-input には常に暗号化されたブロブのみが含まれ、従来のプレビューは無効化されます(つまり、暗号化用と非暗号化用の 2 つのテキストエリアが使用されます)。パフォーマンス上の問題を防ぐため、暗号化処理は大幅にデバウンス(遅延実行)されます。

V1(およびこの作業項目の範囲)では、アップロード機能、暗号化コンテンツのワンボックス化、その他の複雑なエッジケースなどの特定の機能を排除することに問題ありません。

このプライベートコンテンツをレンダリングする際、HTML をホワイトリストでフィルタリングしながら、その場で復号化/生成して渡すことは問題ありません。

ドキュメント要件

実装に先立ち、Web モックアップを含む非常に詳細な仕様書、そして最も重要となる詳細なセキュリティ概要を作成します。

テスト要件

このプラグインには、クライアント側およびサーバー側に基づく包括的な単体テストおよび結合テストが含まれている必要があります。このプロジェクトを受託される場合は、テストを早期に作成し、プロジェクトが終了してからテストを後付けするよう待たないでください。

懸念事項

https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/ に記載されている多くの懸念事項は、Web Crypto API(真の乱数バイト生成、秘密鍵の保存など)の登場により、もはや関連性がありません。存在する主要な攻撃ベクトルは、サーバーがクライアントに悪意のある JavaScript を送信するケースのみです。短期的には、ユーザーは 1Password、LastPass、その他の Web セキュリティ UI を信頼するのと同じように、サーバーを信頼することが期待されます。長期的には(v3/v4、来年以降)、すべての JS ペイロードの安定性ハッシュが既知であるサイトに鍵マークを表示するブラウザ拡張機能の提供を検討できます。

WebCrypto API は比較的新しいですが、広くサポートされており、このプラグインはサポート対象のすべてのブラウザで動作する必要があります。

シンプルなラフモックアップ

* メッセージ開始時、ユーザーは会話を暗号化するオプションを選択できます。参加者のいずれかが公開鍵を持っていない場合、警告が表示されます。

* ユーザーは、そのボタンをクリックして鍵ペアを生成する UI を介して、暗号化メッセージを有効にできます。

暗号化メッセージが有効化されている場合、単に「暗号化メッセージが有効になっています」と表示されます。

* 何らかのアイコンまたはオーバーレイで、メッセージが暗号化されていることを示します。

予算

これは、非常に深い統合と慎重なレビューを必要とする非常に複雑な開発です。クライアント側およびサーバー側の両方を使用したテストが極めて十分に行われていることを期待しています。MVP(最小実行可能製品)の現在の予算は 10,000 米ドルです。

「いいね!」 34

If the implementation is open source with the rest of discourse I could probably get our lot to throw in £1000 or thereabouts to your budget.

「いいね!」 12

Cool, yes, this will be open source under the MIT license, with an additional full license assignment to Discourse.

Regarding browser support, we want to make sure it works on every platform: Chrome + Safari is a minimum (so it works on iPhone / Android / Desktop), but my preference is that we hit Firefox as well. Edge is in the nice-to-have and IE11 is in the I don’t really care department, especially if it is missing crypto stuff.

「いいね!」 7

I’ll get the ball rolling now then, couple of questions though.

  • Is it plausible for this project to accommodate group encrypted chat, even if up to a point?
  • Would “Force every PM encrypted” option be on the cards?

Not for the initial version. Maybe for version 2 or 3, a PM between 20 users should absolutely work for V1.

Yes this is fine as an option. Basically would disallow PM to users that don’t have encryption enabled (but still allow admins to PM them unconditionally). We would need to think this through, but I don’t want to add this to the scope of the spec quite yet.

「いいね!」 4

Thanks i’m obligated to sample an arbitrarily acceptable number of our PMs and reading people’s intimate conversations if only skimming is not only something I don’t want to do it takes time I haven’t got.

a PM between 20 users should absolutely work for V1.

That’s group chat in my context (Forgot where I am :slight_smile: ) and will be fine. I’d expect strong support from our lot.

Keep in mind with this plugin it is technically impossible for you to read members private conversations provided they have a reasonably strong passphrase.

They are encrypted in the database and only decrypted client side. You would have to add a code exploit to your server for you to be able to swing reading encrypted PMs. Longer term (in v3 / v4) this code exploit would result in a giant red flag on the screen for people who install the “confirm my encrypted discourse conversations have not been exploited” browser plugin.

「いいね!」 1

Yes that’s ideal, i’m obligated to look at them because I can look at them.

「いいね!」 1

This is fascinating. It would be refreshing to do something really technically challenging. But, at least for me, the cost / benefit of taking the time to master (assuming, optimistically, that I could) all the relevant pieces doesn’t make sense right now.

That said, I just did a little reading on cryptography and have a few questions / thoughts:

  • Am I correct in understanding that you would need to generate a new conversation key every time you added or removed a member from the PM?

  • You would need some protection or warning against using other plugins as well. I can imagine a scenario in which you install this plugin, then someone writes another plugin that exposes or stores the entered text in some way before it is encrypted.

  • Wouldn’t you need to also only allow this feature on sites that force https? From the article you linked.

    You can’t simply send a single Javascript file over SSL/TLS. You have to send all the page content over SSL/TLS. Otherwise, attackers will hijack the crypto code using the least-secure connection that builds the page.

    Otherwise, in addition to the issue of the server sending malicious javascript you could also have the issue of ‘hijacking’.

  • Searchable semantic encryption is possible (by no means do I mean to imply that I understand the description in that paper). I assume you’re excluding it as the cost / benefit isn’t worth it, at least for the MVP.

  • Why not use the Signal Protocol? (javascript library).

ps. for whoever takes this on

「いいね!」 15

Great questions:

The encrypted conversation key does not need to change on add. All members of the conversation have a copy of the decrypted conversation key. So to add a member you would encrypt this conversation key using the “invitee” public key and teach the server about that.

Technically it would be correct to amend the conversation key if you are removing members from a conversation, but we can wait on v2 for that. Cause it would be a very expensive operation from the client side, entire conversation would have to be re-encrypted, or complex multi conversation key systems need to be built.

Yes, the long term V3 / V4 goal here is to have a whitelist of JS integrity hashes stored in a browser extension. That is really the only way a client can know 100% that the “server is not messing with stuff”. However for V1 we trust the server and server operator not to be malicious.

Yes, if SSL is off all bets are off. We do not expect this feature to even remotely work if SSL is off.

Yes search is off the cards for now cause it is just going to be too hard to get right for the MVP.

Maybe, not against leaning on existing standards, but I suspect the Discourse integration work here is going to be enormous regardless.

「いいね!」 11

Congrats! @dan will be taking this project! We will update here as we make progress!

「いいね!」 29

Signed up to comment on this because I’m curious on the implementation as a developer myself.

I’m personally no cryptography pro, so forgive me if these are silly questions.

Would it not be viable to encrypt the message on the server-side using the public keys of people currently in the conversation, in this case?
Essentially I guess this would lead to a double layer of encryption - the data is encrypted client-side for true E2E encryption as described with the conversation key, and then encrypted on the way out by the server to only the public keys active in the conversation. At that point, removing a member from the conversation would mean any future messages wouldn’t be decryptable by them.

I’m not too sure on the security implications of nested encryption like that, however.

My second question is how well this would work cross-device? Does it require a private key on the device, or is that handled by the passphrase encrypted private key being stored?

「いいね!」 4

Interesting idea the “double” encrypting. It does though add more responsibility to the server and a lot of this is about removing responsibility from the server so I am not sure it is a great idea protocol wise.

Cross device is handled by retrieving the encrypted private key from the server and decrypting using the key phrase.

Longer term we can introduce more “security” modes with a super strict one when say the server never even stores the private key encrypted (like say 1 password), but short term this is out of scope.

「いいね!」 10

Welcome! :discourse: :heart:

Those are not silly questions at all! If you want to dig deeper, I highly recommend Dan Boneh’s courses on Coursera.org.

This depends a lot on the particularities of the system you use. Check out double DES (i.e. vulnerable to meet-in-the-middle) and triple DES.

Let’s say Eve used to be a part of a group discussion with Alice and Bob. Even if Alice or Bob kick Eve out she will still have the conversation key and theoretically, she could intercept the messages before they get to the server and decrypt them.

If the conversation key gets compromised, the safest approach is to revoke it and use a new one for new messages. Ideally, you’d probably want to re-encrypt the old messages.

「いいね!」 7

That makes sense to me, thanks for the explanation! (@sam too)

So adding or removing users from the conversation would involve decrypting everything client-side, revoking the conversation key and generating a new one, then re-encrypting messages before sending to the server?
Plus encrypting the new conversation key with the user passphrase and storing it.

「いいね!」 1

I’ve been talking to my wife about this feature. While this may raise an eyebrow at mealtime conversation in our family, she’s a security engineer for Thales eSecurity.

She’s of the opinion that the Specification needs some use cases with bad actors and how the system should respond. For example: Malicious Admin, Malicious SysOps.

This would then give rise to a security statement that could be presented to users of the system.

「いいね!」 9

I would love it if your wife or anyone interested in this problem at her work would pop by and give feedback.

@dan has made tremendous progress here, I am testing v1 with him this week.

To answer your specific question of bad sys admins. Overall if you can commandeer the web site and deliver malicious payloads our current v1 can not offer privacy. This is by design.

Our plans for v3/v4 is to have a browser plugin that validates every single JavaScript file on the page matches a particular hash in the whitelist. That way if you would be running this plugin you would be able to disallow unrecognized JS payloads.

Overall the underlying design means that passwords can not be fished out after the fact even if the server is malicious, but it can trick the client if it wishes to decrypt arbitrary payloads. Native browser plugins or a more refined web standard longer term will help eliminate this

「いいね!」 14

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.