web-dev-qa-db-ja.com

実際に知らなくても一致する電話番号を見つける

2人のユーザーを想定して、電話番号に基づいて一般的な連絡先を確認する必要があるモバイルアプリがあります。暗号化された安全な方法でこれを実行し、ユーザーのプライバシーを尊重するにはどうすればよいですか(つまり、ユーザー間で、またはサーバーとの間でプレーンテキストの数値を共有せずに)?


現在のソリューションは次のとおりです。

  1. (電話A)ランダムなソルトを1つ生成します。
  2. (On Phone A)電話Aからすべての電話番号を取得し、手順1で生成したソルトを使用して、各電話番号に対して複数のラウンドでSHA512ハッシュを生成します。sha512(sha512(sha512(phonenumber + salt) + phonenumber + salt) + phonenumber + salt)のようなもの。
  3. (電話A)これらのハッシュをソルトとともに電話Bに(サーバー経由で)送信します。
  4. (On phone B)手順2を繰り返して、電話Aで生成されたものと同じソルトを使用して独自の電話番号のハッシュを生成します。
  5. (電話B)ハッシュの2つのリストを比較します。一致するハッシュは、一致する電話番号、つまり共通の連絡先を意味します。

これはレインボーテーブル/ブルートフォース攻撃を受けやすい欠陥のあるアプローチですか?そうであれば、より適切な他のソリューションはありますか?多分、与えられたソルトでbcryptを使用する方が、sha512を複数回実行するよりも良いでしょうか?

39
liviucmg

bcrypt designed が(プログラム可能に)遅くなるため、多少優れたアプローチになります。

十分な大きさのソルトと適度なcomplexFactorを使用すると、bcrypt(salt + number, complexityFactor)は実行可能なハッシュを生成するはずであり、「独自の暗号をローリング」することは避けられます。セキュリティを強化するには、complexityFactorを上げてください。

攻撃者は10桁ごとの電話番号だけでなく、bcryptを生成する必要があります(これは実行可能かもしれません。10 結局のところ数字)、しかしすべての可能な塩漬けシーケンスの。 10文字のbase64ソルト(60ビットのエントロピー)では、複雑さが20桁増加します。

ブルートフォーシング

1,000件の連絡先があるとします。平均的な電話のCPU らしい サーバーコアアレイよりも2桁遅い。 bcryptの半専用GPU実装よりも桁違いに遅くなると言うのは妥当だと思います それほど効率的ではないと言われています

そのため、エンコーディングごとにbcrypt100ミリ秒かかる を調整します。これは、1,000のハッシュを生成するために1分40秒を必要とすることを意味します。これは、1,000の連絡先にとって妥当な時間です(進行状況バーが整然と見えます)。ユーザーの連絡先が100しかない場合は、10秒で完了します。

攻撃者は、1つの数値の塩を考えると、おそらく10を生成する必要があります。8 携帯電話番号のスペースを合理的にカバーする番号(最初の番号、そしておそらく最初の2つは、実際には10または100ではありません-私は1として数えます)。それは10より3桁少ないかかります8 100ミリ秒、つまり10ミリ秒7 秒。これは10までです4 秒、または約2時間半(またはGPU最適化がうまくいかないことが判明した場合は1日)。

4か月も経たないうちに、最適化された1台のサーバーを使用して、1,000件の連絡先全体が復号化されます。 ten そのようなサーバーを使用すると、攻撃者は 2週間で完了します。

アンゲルの回答とニール・スミスラインのコメントで指摘されている問題は、キースペースが small であるということです。

実際には、ユーザーAは何か(ハッシュブロックなど)を生成して、何らかの方法でBが利用できるようにします。ユーザーBはメソッドのように機能する必要があります

matches = (boolean|NumberArray) function(SomethingFromA, NumberFromB)

(2番目のパラメーターがN個の数値のセットである場合はほとんど変更されません。UserBは、1つの真の数と、偽物または興味深いものではないことがわかっているN-1個の数値を使用してセットを作成できるため、攻撃時間をN倍に延長できます)。

この関数は時間Tで動作します...実際にはこの関数must商用の現実世界のアプリケーションでユーザーBが満足するのに十分短い時間Tで動作します。

したがって、簡単に回避できない1つの限界は、次のとおりですM番号は、平均的なスマートフォンで許容可能な時間内に確認する必要があります合理的に回避できないもう1つの限界は、ユーザーBがアルゴリズムに偽の番号を提供できることです(つまり、実際には連絡先ではなく、おそらく存在さえしない人々)。

チェッカーが3番目のサーバーにある場合も、両方の境界が適用されます。これは、 some シナリオ( "decrypt all UserA's numbers"など)を阻止できる低い実行時間制限を保証するだけですが、 drewbennの回答のように、「この番号を誰が持っているかを確認する」。

これらの2つの限界から、スマートフォン(または実行時間が最小のサードパーティサーバー)を使用すると、10個すべてを循環するという事実が生じます。8 合理的な数は約10を取ります8 スマートフォンの時間、または約1000か月。

この時間を短縮するための攻撃戦略は、複数の検証者間で攻撃を分散するか、抑制されていない高速サーバーで実行することです(これにはアルゴリズムが使用可能である必要がありますが、逆に、あいまいさによるセキュリティが想定されます)。 。

抜け穴?

1つの可能性としては、誤検知の可能性が低いを導入することが考えられます。つまり、上記のOracle関数はときどき(たとえば、連絡先が1万回に1回)、確定的にUserAの入力で、 true をUserBの番号の1つに返します。

これは、10に対するブルートフォース攻撃が8 数字は10と混ざったUserAの連絡先を生成します4 他の番号。 UserAの入力の決定論は、これらの104 見つかったアイテムは、それらをさらに荒らしません。 UserBがUserAの入力の別のコピーを取得できる場合を除いて、異なる誤検知のセットが生成され、真の連絡先を2つのセットの共通部分としてフィルタリングできるため、ブルートフォースされた回答の魅力が低下する可能性があります。これには代償が伴います-正直なUserBは時々誤ったヒットを受ける必要があります。

本当に勝てない

UserBの場合must「UserAの連絡先の番号Xですか?」という質問に答えられる妥当な時間内に時間の消費は線形です。これは、システムがこのような2つの要求が番号X1とX2に対して行われるのを防ぐことができず、要求X2の時間は要求X1と同じになるためです。したがって、2つの数値を解くには、妥当な時間の2倍の時間が必要です。帰納法により、N個の数値を解くと、その妥当な時間のN倍の時間が必要になります( not 、たとえば、N2)。

正当なリクエストと攻撃の違いは、攻撃が10倍から10万倍のスペースで機能することです。線形であるため、最大で10万倍の時間が必要になりますが、マシンまたはマシンのグループで100倍から1000倍速く実行することもできます。

したがって、攻撃者はalways UserAのすべての連絡先を「不当ではない」時間で解読できます。これに対する唯一の深刻なチェックは、レート制限と可能性のある攻撃を検出する手段を備えた3番目の信頼できるマシンで実行されるチェックです。

攻撃者を阻止するには、Nの増加に伴って増加する何か悪いものが必要であり、それは実行時間(十分に増加しない)にはならないので、残っている唯一の手段は誤検出の確率だと思います。攻撃者は依然として回答を取得しますが、ブルートフォースされた回答を使いにくくすることに成功する可能性があります。

1つの単純な実装(貧乏人のブルームフィルター)

マインドウィンのコメントに答えるには、ローカルアルゴリズムは情報を非表示にして機能することはできません-そもそも情報が欠落している必要があります。

1つの方法は、UserA(アリス)が(たとえば)1000の連絡先にbcryptソルトを送信し、その後に1000 incompletebcryptハッシュを送信することです。ハッシュがi番目のバイトで切り捨てられると、疑似ランダム衝突が発生します。 UserB(Bob)の連絡先の数は少ないですが、衝突は非常にまれです(私が本当に小さい場合を除きます)。攻撃者(イブ)の整数空間の中で、衝突が顕著になります。

電話番号の分布はnotフラットであることに注意してください。イブは、たとえば未使用の番号付けシーケンスを削除することで、これらの衝突を回避する方法を持つことができます。

すべての連絡先ハッシュが1000分の1の衝突の確率を持っている場合、ボブは1000の連絡先をチェックすると、確率は(1-1/1000)になります。1000 衝突がまったくないことの70%です。衝突の確率が1/10000の場合、1,000の連絡先を持つボブは、90回の確率で1回の衝突さえも受け取れません。 100件の連絡先でのみ、Bobのno-coll確率はそれぞれ90%と99%です。

イブ、検証中108 数は、p = 1/10000であっても、何があっても常に1万回衝突します。

衝突確率が高い2つ以上のハッシュを送信しても、衝突確率が個別のハッシュの積に等しい単一のハッシュを送信する場合と比較して、BobとEveのどちらにとっても大きな変化はありません。

たとえば、p = 1/10000の1ラウンドの代わりに、1/100 * 1/100 = 1/10000なので、p = 1/100の2ラウンドを使用します。

したがって、アリスは、異なるシードと、1%のより高い衝突確率を持つ、順序付けされていない不完全なハッシュの2つのセットを送信します。ボブは1000件の連絡先をテストし、が持っている100件の連絡先に共通する肯定的な一致を取得します。残りの900は一致しないはずですが、ハッシュが不完全であるため、そのうちの1%は一致します。つまり、9つの偽の連絡先があり、ボブは1000のテストを実行した後、109の可能性の高い候補で終わります。彼は今度は2番目のハッシュでそれらの109をテストする必要があり、これも1%の確率です。 100の真の交差は依然として一致します。残りの9つのうち、おそらく何も合格しません。このような2つのラウンドを通過する連絡先の確率は1%の1%、つまり10000分の1であり、一致しない連絡先1000ででも1 偽陽性ではない確率は(1- 1/10000)1000、または90.48%、以前とまったく同じです。

同じ数の場合、イブは最初のラウンドで100万の誤検知を取得し、100万の追加テストを実行する必要があります。それらの1%は第2ラウンドと一致し、1万の誤検知がアリスからの1000の連絡先と混ざり合ったイブが残ります。

イブは100ではなく1億100万回のテストを実行する必要があり、ボブは1000ではなく1109回のテストを実行する必要がありました。それに比例して、ダブルハッシュスキームはボブにイブよりも大きな影響を与えます。より複雑な単一のハッシュを使用する方が良いでしょう。

プライバシーの問題「アリスは番号Nを知っていますか?」という質問に答える対処されないままになります-答える時間はボブとイブにとって同じです。

26
LSerni

まだ検討していない、潜在的に他のプライバシー問題があります。アプリの設計により、特定のターゲットに誰が接続しているかを簡単に確認できます。そのため、攻撃者は自分の電話で連絡先を1つ作成し(活動家/情報提供者/テロリスト/犠牲者)、アプリを介して他の多くのユーザーに接続し、ターゲットの連絡先のリストを作成します。たとえば、DVの乱用者がこのアプリを使用して、元にまだ接触している人のリストを作成することができます。 Google でも、これを正しく行うのに問題がありました。

17
user15392

はい、それは(少し)欠陥があります。問題は、スペースが小さすぎることです。そのため、複数のラウンドとソルトがあっても、ブルートフォースするのは比較的簡単です。

Open Whisper Systemsには、ブラインドシグネチャを使用してローカルでクエリできる暗号化されたブルームフィルターを提供する機知に富んだシステムがありました。彼らは https://whispersystems.org/blog/contact-discovery/ でプロセスを説明します(個人情報の取得に関する問題についての適切な議論を提供します)。

悲しいことに、大規模なユーザーベースでの実際的な問題のため、TextSecureでこれを中止する必要がありました。あなたの場合、2人の最終ユーザー間で番号を共有しているので、同じ方法で、または公開されているプロトコルのような別のプロトコルを使用して、それを実現する必要がありますそれらはMoxieによって言及されています。

11
Ángel

暗号化された安全な方法でこれをどのように行うことができ、ユーザーのプライバシーを尊重することができますか(つまり、ユーザー間またはサーバーで平文の数値を共有せずに)?

tldr:できません。

ハッシュは特定の用途に最適ですが、これはおそらくそれらの1つではありません。その理由は、攻撃者は(10桁の電話番号に対して)100億の可能性しかないことを知っているためです。これにより、発見されたハッシュをブルートフォースするのは非常に簡単になります。

代わりに、次の場合に目的を達成できます。

  • 電話の1つは他を信頼して喜んでです。 1台の電話が他の電話と連絡先リストを共有し、もう1台の電話が比較を行います。 2番目の電話は、そのリストを最初の電話と共有する必要はありません。
  • 信頼できるメディエーター、つまりサーバーを喜んで使用します。どちらの当事者も連絡先リストを他方と共有する必要がないため、これはユーザーの観点からは最良のアプローチです。両方ともメディエーターとリストを共有し、一致を報告し、何も保存しないことを約束します。各関係者との通信には、標準の公開/秘密暗号化キーのペアが使用され、誰のデータも危険にさらされることはありません。もちろん、これは、連絡先リストを保持しないことを約束したときに、ユーザーがあなたを信頼することを前提としています。しかし、最初からアプリをインストールするのに十分な信頼をユーザーに与えることができれば、すでにユーザーの信頼を得ています。
7
TTT

いくつかのテストをしましょう!

私はナイーブbashの実装から始め、33秒で10kの数値を計算しました。

#!/bin/bash

phone="2125551212"
salt="abcdefghijklmnopqrstuvwxyz"

shasalt() { echo "$* $phone $salt" | sha512sum; }

for f in {1..10000}
do
    shasalt $(shasalt $(shasalt)) >/dev/null # or write to a file...
    ((phone++))
done
echo $phone>&2

次に オンラインで見つけたSHA512 C++アルゴリズム を使用して、醜いサンプルC++コードを書きました。エリアコード全体のハッシュを84秒で生成しました。ファイルに書き出したときは約160秒です(1.4GiBレインボーテーブルを生成しました)。

#include "sha512.hh"
#include <iostream>
#include <sstream>
#include <string.h>
using namespace std;

int main(int argc, const char* argv[])
{
  char salt[10] = "liviucmg";
  int x = 2120000000; // won't work above 2^31 i.e. past area code 213
  char y[21];
  snprintf(y, 21, "%010d,%s", x, salt);
  const int len = strnlen(y, 21);

  for (int i = 0; i < 10000000; i++, x++) {
      snprintf(y, 21, "%010d,%s", x, salt);
      //cout << y << "," << sw::sha512::calculate(y, len) << endl;
      string FIRST(sw::sha512::calculate(y, len));
      string FIRST_GO(FIRST + y);
      string SECOND(sw::sha512::calculate(&FIRST_GO, 1+FIRST_GO.length()));
      string SECOND_GO(SECOND + y);
      // uncomment this line to generate a hash table:
      //cout << y << "," << sw::sha512::calculate(&SECOND_GO, 1+SECOND_GO.length()) << endl;
  }
  return 0;
}

これはすべて、私の3歳のラップトップにありました。これは、同様の構成で約300ドルでオンラインで見つけることができます。したがって、ごくわずかな費用で、攻撃者は約10桁の数字と一意のソルトの組み合わせを1日(実際の市外局番のみをテストした場合を除く)、またはすべておもしろい数字(ターゲットの市外局番と物理的に隣接する市外局番の数字)約5分。私はテストコードを調整(またはデバッグ)しようとしなかったので、コードを調整することでそれらの時間を半分に削減できると思います。グラフィックカードやFPGAなどのより優れたハードウェアで実行すると、時間も大幅に短縮されます。有能な攻撃者によって、任意の数値と塩が約1時間以内にブルートフォースになる可能性があると思います。

5
user15392

「偽陽性の確率」の Isemis言及 について、私は ゼロ知識証明 について考えました。この回答は、レビューされたことがないため安全であることを主張するものではないため、他の人がレビューしてコメントする必要があります。私もセキュリティの専門家ではありません。電話番号の数が少ないことが問題になる可能性があることを確認する時間もありませんでした。

  1. ユーザーAとユーザーBは(直接またはサーバーを介して)相互に接続し、ランダムな識別子(RI)を各連絡先の連絡先に割り当て、それをリスト(LAおよびLB)に、ナンスが埋め込まれた電話番号とともに、適用後に保存します。 a キー導出関数 。 LAはAに、LBはBに滞在します。
  2. ユーザーAは、リストLAの各番号とRIについて、1ラウンドの知識を証明します( ゼロ知識証明 を参照)。ユーザーBは、Aの各RIがLBのどの連絡先に適合するかを総当たりで調べなければなりません。はめあいのない各接点はLBから削除されます。ブルートフォースの速度を向上させるために、次のラウンドで可能な適合がリストに保存されます。
  3. ユーザーBは、リストLBの各番号について、RIとともに1ラウンドの知識を証明します。ユーザーAは、Bの各RIがLAのどの連絡先に適合するかを総当たりで調べなければなりません。適合しない各コンタクトはLAから削除されます。ブルートフォースの速度を向上させるために、次のラウンドで可能な適合がリストに保存されます。
  4. 手順2と3を頻繁に繰り返して、統計的に安全であり、各番号が他のユーザーに各番号を証明したことを確認します。
  5. オプション:リストLAおよびLBの残りを、他のユーザーが言及した安全な方法で、または 安全なチャネル を介してユーザーAとユーザーBの間で交換し、RIの一致が正しいことを確認します。

このアルゴリズムを使用すると、サーバーまたはオブザーバーは、AとBの連絡先について、それらの数と共通の数を除いて、関連情報を学習することはありません。ユーザーAとBは、サーバーと共通の連絡先と同じ情報のみを学習します。

アルゴリズムの最初のステップは指数関数的であり、リソースを集中的に使用するため、実装にはDOSに対する保護を含める必要があります。

3
H. Idden

このメソッドには次のプロパティが必要です。

  • 誤検知の確率を必要なだけ小さくすることができます
  • サーバーは、各ユーザーの電話帳の(概算)数のみを学習しますが、それ自体には学習せず、総当たりすることはできません。
  • サーバーが攻撃に対してポリシーを実施できるため、クライアント側の総当たり攻撃は不可能
  • 電話は他の電話の連絡先の数を学習しません

推奨される手順:

  1. 電話ABKey-agreement protocol を使用して共有シークレットSを生成します(認証は、ここではMitMから防御するのに適しています)
  2. ブルームフィルターのサイズとハッシュの数はハードコーディングすることができ、プロトコルは最大10.000の電話番号が許可されるように強制することができます/人(ハッシュの衝突と乱用を緩和するため)
  3. Sブルームフィルター のハッシュを適切な方法でソルトするために使用されます
  4. ABは電話番号をブルームフィルターに保存し、サーバーに送信します
  5. サーバーは、フィルターの推定カーディナリティが高すぎないことを確認します。可能な数をすべて知っていると主張することはできません。
  6. サーバーはセットの共通部分を計算し、その結果をABに送信します
  7. これで、ABは、どの番号が交差したビンに移動したかをチェックすることで、共通の番号を(かなり正確に)知っています。

実際、Bloomフィルターは、サーバーに数値のプレーンなSHA-256ハッシュを送信することで置き換えることができ、一致するハッシュをクライアントに送信できます。これにより、ロジックが大幅に簡略化され、誤検知の可能性が低くなります。

1
NikoNyrh

なぜそれらをハッシュするのですか?代わりに暗号化しないで、自分のサーバーに送信してください。この方法では、どちらのクライアントデバイスも互いの連絡先にアクセスする必要がなく、サーバーがほとんどの作業を行います。

ただし、これはサーバーの単一障害点をもたらします。それが侵害された場合、攻撃者はすべての番号にアクセスする可能性があります。これは、実際に何も保存されておらず、すべてがメモリ内で行われている場合に制御できます。

0
Awn