同意鍵、Client IDの取得について #34

Open
opened 2023-10-21 00:12:22 +09:00 by stat2 · 7 comments
Owner

delightlyでは忍法帖に代わるユーザー管理の手段として、フィンガープリントを元に生成した同意鍵を使用しているようです。認証時に複数ID取得の難易度が上がることや、クライアントからcookieで同意鍵を送信しており管理上のメリットがあります。

しかし、フィンガープリントベースなため特定環境下では多数の人の同意鍵が被ってしまい、同意鍵でユーザーを識別する機能や規制は問題がありそうです。

delightlyでは忍法帖に代わるユーザー管理の手段として、フィンガープリントを元に生成した同意鍵を使用しているようです。認証時に複数ID取得の難易度が上がることや、クライアントからcookieで同意鍵を送信しており管理上のメリットがあります。 しかし、フィンガープリントベースなため特定環境下では多数の人の同意鍵が被ってしまい、同意鍵でユーザーを識別する機能や規制は問題がありそうです。
stat2 added reference main 2023-10-21 01:02:17 +09:00
stat2 removed reference main 2023-10-21 01:02:23 +09:00
stat2 added the
enhancement
Priority
Medium
labels 2023-10-21 02:23:40 +09:00
Contributor

delightly-v2ではClientIDが指すものがいくつかありややこしいので整理してみます。

認証時に生成されるClientID (詳しくは/static/client.jsの解読が必要。32桁の文字列だと思われる)
↓(md5hash化等)
$WrtAgreementKey (in /test/auth.php) (いわゆる鍵。メール欄に#付きで入力する8桁の数値)
↓(md5hash化,sha256hash化等)
$clientid (in /test/auth.php) (/HAP/以下に生成される同意鍵ファイルのファイル名。64桁の文字列)
↓(同値)
$clientid (in /test/bbs-main.php)

(同意鍵ファイルのrange,provider,CH_UA,ACCEPTからmd5hash化等)
$WrtAgreementKey (in /test/bbs-main.php) (投稿ログや掲示板内でClientIDと呼称される英数字7桁)

規制に使用されるClientIDは/test/bbs-main.phpにある7桁の$WrtAgreementKeyとなります。
こちらは生成に使用される要素が利用者の8桁IDと同じなため、被りやすさも同等と考えられます。
同意鍵ファイルの被りやすさについては/static/client.jsが解読できていないので何とも言えません。
規制のみについて考えるなら、同意鍵ファイルから使用する要素を増やして生成した$strictClientIDのような新たなIDを定義して使用したほうが巻き添え規制は防げるかもしれません。first(認証時のエポック秒)などを混ぜればかなり被りにくくなると思います。

delightly-v2ではClientIDが指すものがいくつかありややこしいので整理してみます。 認証時に生成される`ClientID` (詳しくは`/static/client.js`の解読が必要。32桁の文字列だと思われる) ↓(md5hash化等) `$WrtAgreementKey (in /test/auth.php)` (いわゆる鍵。メール欄に#付きで入力する8桁の数値) ↓(md5hash化,sha256hash化等) `$clientid (in /test/auth.php)` (`/HAP/`以下に生成される同意鍵ファイルのファイル名。64桁の文字列) ↓(同値) `$clientid (in /test/bbs-main.php)` (同意鍵ファイルの`range`,`provider`,`CH_UA`,`ACCEPT`からmd5hash化等) `$WrtAgreementKey (in /test/bbs-main.php)` (投稿ログや掲示板内でClientIDと呼称される英数字7桁) 規制に使用されるClientIDは`/test/bbs-main.php`にある7桁の`$WrtAgreementKey`となります。 こちらは生成に使用される要素が利用者の8桁IDと同じなため、被りやすさも同等と考えられます。 同意鍵ファイルの被りやすさについては`/static/client.js`が解読できていないので何とも言えません。 規制のみについて考えるなら、同意鍵ファイルから使用する要素を増やして生成した`$strictClientID`のような新たなIDを定義して使用したほうが巻き添え規制は防げるかもしれません。`first`(認証時のエポック秒)などを混ぜればかなり被りにくくなると思います。
First-time contributor

おそらく問題になっているのは次の行でしょう。

if (filemtime($ip_file) + 2592000 > $NOWTIME) $WrtAgreementKey = file_get_contents($ip_file);

8833f2971a/test/auth.php (L195)

これは「$WrtAgreementKey を取得したことがあるIPアドレスの場合、3日以内であれば同じ $WrtAgreementKey を返す」という処理になっています。

たぶん自演防止が目的だと思うんですが、IPアドレスが被りやすいプロバイダの利用者からすれば迷惑極まりない仕様になっているような。

おそらく問題になっているのは次の行でしょう。 ```php if (filemtime($ip_file) + 2592000 > $NOWTIME) $WrtAgreementKey = file_get_contents($ip_file); ``` https://git.3chan.cc/stat2/delightly-v2fork/src/commit/8833f2971a6e3a921dfd6233b145a41fd478e908/test/auth.php#L195 これは「`$WrtAgreementKey` を取得したことがあるIPアドレスの場合、3日以内であれば同じ `$WrtAgreementKey` を返す」という処理になっています。 たぶん自演防止が目的だと思うんですが、IPアドレスが被りやすいプロバイダの利用者からすれば迷惑極まりない仕様になっているような。
Contributor

確かにalice2chさんのご指摘の通り、IPアドレスが被っている複数の利用者が一定期間内に続けて認証すると同じ鍵を返す処理になっていますね。同意鍵自体が被るケースも十分ありえるようです。

確かにalice2chさんのご指摘の通り、IPアドレスが被っている複数の利用者が一定期間内に続けて認証すると同じ鍵を返す処理になっていますね。同意鍵自体が被るケースも十分ありえるようです。
First-time contributor

認証時に生成されるClientID (詳しくは/static/client.jsの解読が必要。32桁の文字列だと思われる)

static/client.js は https://github.com/fingerprintjs/fingerprintjs をビルドしたものだと思いますよ。

FingerprintJS は BSL 1.0 (2023/07以前なら MIT) なので、ファイル中に Copyright とライセンスの表記を行っておかないとライセンス違反になってしまう、、、というのはひとまず置いておいて。

問題は送信されてくる ClientID が、FingerprintJS が生成したものなのか、はたまた人為的に上書きされた文字列なのか検証していないので、実は任意の32桁の文字列ならなんでも受け入れてしまうし、偽造しようと思えばいくらでも偽造できてしまうということです。

ブラウザで test/auth.php を開いて、ブラウザの開発者ツールから input#ClientID の value を 00000000000000000000000000000000 とかに書き換えて同意ボタンをクリックしても普通に通ってしまいます。

ですから static/client.js は32桁の乱数を生成する代わりに使われてるような感じで、実は必要ないし、荒らし対策としての効果もたぶんゼロです。

> 認証時に生成される`ClientID` (詳しくは`/static/client.js`の解読が必要。32桁の文字列だと思われる) static/client.js は https://github.com/fingerprintjs/fingerprintjs をビルドしたものだと思いますよ。 FingerprintJS は BSL 1.0 (2023/07以前なら MIT) なので、ファイル中に Copyright とライセンスの表記を行っておかないとライセンス違反になってしまう、、、というのはひとまず置いておいて。 問題は送信されてくる ClientID が、FingerprintJS が生成したものなのか、はたまた人為的に上書きされた文字列なのか検証していないので、実は任意の32桁の文字列ならなんでも受け入れてしまうし、偽造しようと思えばいくらでも偽造できてしまうということです。 ブラウザで test/auth.php を開いて、ブラウザの開発者ツールから `input#ClientID` の value を `00000000000000000000000000000000` とかに書き換えて同意ボタンをクリックしても普通に通ってしまいます。 ですから static/client.js は32桁の乱数を生成する代わりに使われてるような感じで、実は必要ないし、荒らし対策としての効果もたぶんゼロです。
Contributor

なるほど。ご説明ありがとうございます。

なるほど。ご説明ありがとうございます。
Author
Owner

ありがとうございます。
なるほどそうなっていたんですね〜

8833f2971a/test/auth.php (L195)
この部分のIPアドレスによるものなのか、もう一回ログを見直してみます。3日だとかなり被る人が出てしまいそうですね。

ありがとうございます。 なるほどそうなっていたんですね〜 https://git.3chan.cc/stat2/delightly-v2fork/src/commit/8833f2971a6e3a921dfd6233b145a41fd478e908/test/auth.php#L195 この部分のIPアドレスによるものなのか、もう一回ログを見直してみます。3日だとかなり被る人が出てしまいそうですね。
Contributor

認証時の同意鍵被りについての対策を少し考えました。

認証時にIPアドレス等の一致により同じ鍵を返却する処理(おそらく自演対策)について

該当の処理は/test/auth.phpの以下の部分です。
ca415d001d/test/auth.php (L192-L199)

現在は、「IPアドレス」あるいは「IPアドレスの先頭部、回線、UA、acceptヘッダー」が同じ環境に対して、前回の認証から30日間は同じ鍵を返す処理になっています。
期間が長いほど鍵被りが発生しやすくなるので短くするのが一つの方法かもしれません。3日間の場合は259200、1日間の場合は86400となります。

また、メンテナンス等で現存の同意鍵を全てリセットする場合などは、復帰時に一斉に認証されることが予想されるので一時的に0にする等の処理が望ましいかもしれません。
その場合、#34 (comment) で指摘されているように単純な操作で鍵を量産できてしまうので、生成された鍵の扱いを修正した方が良いかと思います。次項に例があります。

鍵生成をより安全に

#34 (comment) で示されているように、鍵を自由に作成可能な脆弱性があります。
対応策として、/static/clientid.jsにて生成されたidをフォーム送信直前に入力するような修正案を考えました。完全に安全ではないと思いますがマシにはなっているかと思いますがどうでしょうか。(8ae8fe023c)
もっと良い対策や修正案があれば教えていただきたいです。

認証時の同意鍵被りについての対策を少し考えました。 ## 認証時にIPアドレス等の一致により同じ鍵を返却する処理(おそらく自演対策)について 該当の処理は`/test/auth.php`の以下の部分です。 https://git.3chan.cc/stat2/delightly-v2fork/src/commit/ca415d001d2ae5df18edc5385b843b56fbcac88d/test/auth.php#L192-L199 現在は、「IPアドレス」あるいは「IPアドレスの先頭部、回線、UA、acceptヘッダー」が同じ環境に対して、前回の認証から30日間は同じ鍵を返す処理になっています。 期間が長いほど鍵被りが発生しやすくなるので短くするのが一つの方法かもしれません。3日間の場合は`259200`、1日間の場合は`86400`となります。 また、メンテナンス等で現存の同意鍵を全てリセットする場合などは、復帰時に一斉に認証されることが予想されるので一時的に`0`にする等の処理が望ましいかもしれません。 その場合、https://git.3chan.cc/stat2/delightly-v2fork/issues/34#issuecomment-660 で指摘されているように単純な操作で鍵を量産できてしまうので、生成された鍵の扱いを修正した方が良いかと思います。次項に例があります。 ## 鍵生成をより安全に https://git.3chan.cc/stat2/delightly-v2fork/issues/34#issuecomment-660 で示されているように、鍵を自由に作成可能な脆弱性があります。 対応策として、`/static/clientid.js`にて生成されたidをフォーム送信直前に入力するような修正案を考えました。完全に安全ではないと思いますがマシにはなっているかと思いますがどうでしょうか。(https://git.3chan.cc/konkon-fox/delightly-v2fork/commit/8ae8fe023ce42b8672aa2e8cdedb555d8d722472) もっと良い対策や修正案があれば教えていただきたいです。
Sign in to join this conversation.
No description provided.