IPアドレス流出はなぜ起こったのか？(推理)

# TL;DR
- 某サービスに「ソースコードからIPアドレスが流出してしまう」という脆弱性が発見され、世間を騒がせていますね。
- そもそもなぜソースコードからIPアドレス流出したの？という問題について語ってみます。
- 筆者は某サービスの開発に関わっていたわけではないので、当記事で語るパターンでの流出だったとは断言できません。
- そのため、起こりうる一つの「怖いパターン」の事例共有として、ご参考までによろしくお願いいたします。
- 初学者にもわかるように少し噛み砕いてご説明します。

# 前提
まず大前提ですが、統計のためやバグの追跡のためにサービス利用者のIPアドレスを収集すること自体は不思議なことではありません。
問題は、__なぜそれがフロントから見える状態になっていたのか__です。

統計を取るにせよ、サービスに嫌がらせをしていた投稿者を特定してそのIPアドレスをブロックするにせよ、DBに値が残っていれば十分で、それをフロントに持ってくる意味ってありませんよね？
少なくとも私はパッと思いつきません。
それにフロントに持ってくるということは、今回のような「流出のリスク」があるということでもあります。

要は、__「DBにさえあれば良いはずのIPアドレスがなぜフロントから見えてしまったのか？」__が今回理解すべきことになります。

# サーバサイドの動きを考えてみる
その前提の上でDBに思いを馳せてみましょう。

例えば__「ブログ投稿サービスで、記事の情報を保存する『Article』テーブルが存在していた」__と考えてみます。
正規化がどうのこうのという難しいことは考えなくて良いです。
以下の要件で考えてみましょう。

- Articleテーブルは1記事の情報を1レコードで保存している
- Articleテーブルには「記事タイトル」「本文」「投稿日付」などのカラムがある
- そのカラムの中に「IPアドレス」もあった

不思議な仮定ではないはずです。

さて、お話が変わって現在三大フルスタックWebフレームワークといえば

- Rails
- Laravel
- Django

です。本題ではないのでなぜこれが三大なのかという議論は避けますが、少なくともよく使われているフレームワークであることには間違いないでしょう。
参考：[どれ使うべき？3大WebフレームワークRails・Django・Laravelを徹底比較してみた](https://paiza.hatenablog.com/entry/2018/05/07/paizacloud_web_application_frameworks)

その中で、RailsとLaravelのORM(DBへのアクセスを簡単にしてくれる仕組みのことですね) には共通の特徴があります。

__Active Record__という特徴です。
ざっくりといえば、__「DBの1レコードをひとつのオブジェクトとして扱おう！」__という考え方です。
賛否両論、メリットデメリットがある仕組みではあるのですが、個人的には大好きな仕組みです。
詳しくはこちらもどうぞ (宣伝)
[開発への異常な愛情 または私は如何にして嫉妬を止めてActive Recordを愛するようになったか](https://qiita.com/KizashiTakata/items/a0b5f456836308c0dc8a)

閑話休題。

さて、例えばLaravelの場合、その"Active Record"を利用すると、このようなことができます。

```TestController.php
// ログインしているユーザーの最新の記事のIDを特定
$currentArticleId = $this->getCurrentArticleIdByLoginUser();
// 最新の記事(1レコード ≒ 1オブジェクト)を取得
$currentArticle = Article::find($currentArticleId);
// オブジェクトをビューに渡す
return view('current_article.index',compact('currentArticle'));
```

<font color="Gray">分かる人は「コントローラーでそんな処理するな」とか「なんでコントローラーで明らかに特定のModel専用の独自処理を実装してるの？怖……」とか言いたくなったかもしれませんが、とりあえず今は無視してください。</font>

要するに、__「Articleテーブルの、ログインユーザーによる最新の1レコード ≒ 1オブジェクト」を取得して、フロントにパスしている__ということだけ伝われば十分です。`compact('currentArticle')` はそのためのおまじないです。

さて、このオブジェクトが格納されている変数`$currentArticle` は、当たり前ですが1レコードの情報全てが格納されています。
フロント側……Laravelは`.blade.php`というファイルにHTMLとPHPが融合したようなものを記載していくのですが……このbladeファイルの中では、`$currentArticle` が持っている情報全てにアクセスすることができます。

```index.blade.php
<h1>{{ $currentArticle->title }}</h1>
```
こう書くだけでWebページにタイトルが表示されます。とても便利です。

話を戻しましょう。なぜフロントでIPアドレスが見えてしまったのでしょうか？

仮説1。
bladeファイル(あるいは他のフレームワークにおける同様の機能)の中で

```index.blade.php
{{ $currentArticle->ipaddress }}
```

と書いてしまったのでしょうか？
その可能性は低いと思われます。意味がありませんし、レビュー文化があればすぐ気付くでしょう。

仮説2。

```index.blade.php
{{ $currentArticle }}
```
と書いてしまった？これもあまり意味がなく、すぐ気付くため考えにくいです。
ちなみに実際にこれをやってみると確かに流出はするのですが、そもそもフロントに`$currentArticle` の中身を展開した文字列がズラーッと表示されますw ソースコード上の流出どころの騒ぎではありませんね。

仮説3。

```index.blade.php
<h1>{{ $currentArticle->title }}</h1>
```
と書くだけでもフロントに`$currentArticle` の情報は引き渡っているから、`$currentArticle`のメンバ変数は全て可視化されてしまっていた。
こう考えた人、ほぼ正解！
ただ少なくともLaravelくんはとても賢いので、そのようなことにはなりません。


仮説4。
__フロント専用のフレームワークを使っていたことが原因。__
このパターンについてご説明いたします。

# JSフレームワークを使っていたとしたら？
フロントの三大フレームワーク、これは議論を待たずに

- React
- Vue
- Angular

ですね。これらはいわゆる「三大フロントフレームワーク」と呼ばれるものですが、言い換えると「三大JavaScriptフレームワークです」
JavaScriptのフレームワークなんです。

さて、JavaScriptがPHPのオブジェクトを使う方法はあるでしょうか？
PHPでなくても、RubyでもPythonでも同じです。
使えるように変換する必要がありますよね？

そう、__JSON(JavaScript Object Notation)__ です。
……厳密に言えば、JavaScriptのオブジェクトに変換するために、一度JSONに変換する必要があるというお話です。他にも色々方法はありますが、一番良く使われるのがJSONでしょう。

そういうわけで、

```TestController.php
$currentArticle = Article::find($currentArticleId);
$currentArticleJson = $currentArticle->toJson();
```

のようにしてあげるとJSON(文字列)に変換できます。

あとはこのJSON文字列`$currentArticleJson` をフロントにそのまま引き渡せば、JavaScriptがその文字列を処理する際に流出してしまう……というオチです。

ちなみにLaravelではJSONに変換する際に「この項目はJSONに変換した際に変換の対象にしない！」と指定するためのフィールドがあります。

https://readouble.com/laravel/5.5/ja/eloquent-serialization.html
> モデルから変換する配列やJSONに、パスワードのような属性を含めたくない場合があります。それにはモデルの$hiddenプロパティに定義を追加してください

本題ではないので詳細は省きますが、この記載から属性："ipaddress"の指定が漏れてしまうということは十分にあり得ることです。

# 結論
繰り返しますが、この記事で書いたのはあくまでもあり得るパターンの一つであり、今回世間を騒がせた件は別の理由である可能性もあります。例えばそれこそ5chではIPアドレスをフロントで表示する機能もあるので、そのような機能がどこかにあったのかもしれません。

ただ、ここで書いた事例は他のIT企業でも十分に「起こりうる」ことです。
ここで紹介したパターンでは、そのリスクはフレームワークを他のフレームワークに適合させるために = 汎化させるために、「JSONへの明示的な変換」をしたことで発生するものでした。
「こうするとリスクは防げる」という銀の弾丸ははなかなかないものなのですが、少なくともJSONへの変換 = いわゆるシリアライズした上でのデータの受け渡しはときにリスクを伴う、ということは覚えておいても良いかもしれません。

お読み頂きありがとうございました。
ではまたどこかで。