新卒のセキュリティ研修用に使用したアプリケーションを公開します！【2020年度】

普段はWeb系の会社でアプリケーションの開発をしている [@oliver_diary](https://twitter.com/oliver_diary) です。
2020年度も始まり、新卒研修が各場所で行われていると思います。
私の所属する会社では、リモート上での研修が始まり、初めての環境で慣れないながらも、みんなで知恵を絞って協力しています。

私は、2020年4月から情報処理安全確保支援士になったこともあり、今回社内のセキュリティ新卒研修を担当しました。
そこで、今回使用した研修内容が参考になればと思い、実際に使用した自作のCTFみたいなアプリケーションを公開しようと思います。

# 自作アプリケーションについて
今回使用したアプリケーションは少し手を加えていますが、下記にて公開しています。是非みなさんも解いてみてください。
https://sec-kensyu-2020.web.app/

<img width="1287" alt="Screen Shot 2020-04-15 at 21.10.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/34488da9-46c2-962c-1d64-6f8ba4b4a93e.png">

問題は8問あり、**Firebase**をメインの題材にしています。
SQLインジェクションなどの、とても有名な攻撃手法ではなく、Firebaseで起こるかもしれない穴を複数個用意しました。
様々な技の組み合わせにより、答えへと導けるような意図も組み込んでいます。

そして、問題を解くと、ある文字列が得られ、全て解いてそれらを組み合わせると、正解の言葉が得られるという内容になっています。

また、今回用意したアプリケーションはソースコードも公開しています。どういう風に作っているのか参考になれば幸いです。

https://github.com/minakawa-daiki/sec-kensyu-2020

# このアプリケーションの作成意図について
今回私が行った研修の対象はIT系技術職の新卒10人ですが、デザイナーの人も含むので、**技術力は正直バラバラ**です。
またリモート上なので、どのように連携すればいいのかも考えなければいけませんでした。

そんな中、研修の時間も限られているので、今回の研修では一つ一つの技術に絞って解説するというよりかは**「セキュリティとは何か」**や**「なぜセキュリティは大事なのか」**などの**Why**の部分に重点をおき、またセキュリティに関して**「全員関係していること。誰一人部外者ではない」**と言う意識付け、そして**「セキュリティを楽しんでもらう」**ことを盛り込もうと思いました。

そうすることで**「セキュリティに対する苦手意識をなくし、普段から心掛けることができるようになる」**というのが、今回の研修で目指したゴールです。

その中で**「セキュリティを楽しんでもらう」**を達成するために、この自作アプリケーションを作成しました。

また、実際に研修の中で解いてもらう時にはチームになってもらい、**技術力の差**を埋めるようにしました。
そうすることで、自分では当たり前だと思っていた技術や暗黙知が、それを実際には知らない人に伝わるので、コミュニケーションも活発化します。そしてその結果、全体としてのレベルも上がるように企てました。

また、詰まっていた場合に積極的にヒントを与え、会話や手が止まってしまわないようにも意識しました。
実際リモートで、相手が近くにいないと、**詰まったときにすぐに会話が止まってしまいます。**それを防ぐのはメンターの役目だと思っています。

忘れてはいけないのは**「セキュリティを楽しんでもらう」**です。セキュリティは**「難しいからわからない」**ではなく、**「興味を持ってもらう」**ところから始めることこそ、今回の研修では大事にしました。

興味を持ってもらえれば、あとは各々で日々意識してもらえます。難しい用語を語り、苦手意識を植えつけてしまうのではなく、**「興味を持ってもらうための架け橋」**や**「そのスタートダッシュを切れること」**こそ、今回行った研修の目的です。

# アプリケーションの解説
ここからはアプリケーションの解説で、ネタバレになってしまうので、実際に解きたい人は後でご覧ください。

## 問題1（Debug消し忘れちゃったかも？）
問題1は、**ブラウザのコンソール**に答えが出力されるという問題です。
デバッグを残した状態のままリリースされてしまった場合に、それが攻撃者の材料になってしまうという想定です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/79256829-7b61-ac3a-7812-2a90a52f0406.png)


## 問題2（HTMLに情報残したままにしちゃったかも？）
問題2は、**HTML上**に答えが埋め込まれています。
これも、デバッグを残したままリリースされてしまった想定の問題になっています。

<img width="1287" alt="Screen Shot 2020-04-15 at 21.13.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/311b10c0-15bb-ba37-6ea9-f0633920d1e5.png">


問題1と合わせて、ブラウザの機能で様々な情報が確認できること伝えています。

## 問題3（何かをダウンロードしているかも？）
問題3は、**ブラウザのネットワークタブ**の機能を利用しています。
実際には画面に`iframe`を仕込んでいて、答えを含むテキストファイルがダウンロードされていることが分かります。

<img width="1287" alt="Screen Shot 2020-04-15 at 21.15.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/d51fb259-17ef-bdd6-23eb-1ee1e237e632.png">

これは機密情報などが、実際には画面に表示されてはいないが、バックグラウンドではダウンロードされている場合に、読み取ることができるという想定の問題になっています。

## 問題4（FirestoreのRuleが設定されてないかも？）
問題4からは具体的に**Firebase**に関する問題になっていきます。
実際に研修でやってもらった時も、ここら辺から難易度が上がったとフィードバックを受けました。

内容は読み取り可能になってしまっている**Firestore**のコレクションのドキュメントを読み取ると答えが現れるという内容です。

このページでは、**Firebase**がwindowオブジェクトに生えています。そして、問題文が**Firestore**から取得されてますので、そこのコレクション名が取得できれば、あとはドキュメントを読み取るだけです。

実際にどのコレクションにアクセスしているかは、ブラウザのネットワークタブから発見することができます。

<img width="1287" alt="Screen Shot 2020-04-15 at 21.17.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/08827ce9-d6bc-11e7-178e-9ccdd4c5909e.png">

そして取得したコレクション名をもとに、以下のコマンドをブラウザのコンソールで実行すると、答えが得られます。

```js
(await firebase.firestore().collection('items').get()).docs[1].data();
```

実際に**Firestore**のルールを書かずにリリースされてしまっているアプリケーションは多く、問題になることがしばしあります。
ユーザーから直接触れるDBは「しっかりと権限を絞らないと大変なことになるよ」ということをこの問題で知ってもらうのが目的です。

## 問題5（JWTから情報が漏れてしまうかも？）
問題5は**Firebase Authentication**に関する問題です。
Firebase Authenticationのユーザーには、`CustomClaims`というユーザーアカウントのカスタム属性の定義が可能です。今回、Firebaseが発行する**JWT**のデコード経由で取得できる、そのカスタム属性に答えを持たせました。

実際に問題5のページでは、アクセスするとユーザー一覧が表示されています。問題4と同じ風に`users`コレクションのデーターを取得すると、ユーザーの`email`と`password`が取得できます。

```js
(await firebase.firestore().collection('users').get()).docs.map(u => u.data());
```

この`email`と`password`をもとに、Firebaseのログインを実行します。

```js
await firebase.auth().signInWithEmailAndPassword(process.env.Q5_EMAIL, process.env.Q5_PASSWORD)
```

このコードをコンソール上で実行すると、バックグラウンドでログインが完了するので、以下のコマンドを経由することで**JWT**を取得することができます。

```js
await firebase.auth().currentUser.getIdToken(true)
```

ここから得られたトークンを https://jwt.io/ でデコードすると、答えを得られます。

<img width="1287" alt="Screen Shot 2020-04-15 at 21.22.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/735ede0d-e612-a20b-d8ab-e51878b8b17f.png">

この問題では、Firebase経由で得られるJWTをデコードすることによって、`CustomClaims`で独自に持たせていたデータが漏れてしまう可能性があることを実際に感じてもらう内容でした。

## 問題6（画像に情報が埋め込まれているかも？）
問題6は**Firebase Storage**に関する問題です。
Firebase Storageの画像には`Metadata`が存在しています。データとして`CustomMetadata`を持たせることができ、答えがそこから取得できるという問題になっています。

取得の流れとしては、まず画像のURLを見てみれば、ファイル名を取得できます。

<img width="1287" alt="Screen Shot 2020-04-15 at 21.30.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/a26293c8-a846-54ea-ffe5-078521c7c79e.png">

それをもとに以下のコマンドを実行すると、答えが得られます。

```js
(await firebase.storage().ref().child('icon.png').getMetadata()).customMetadata;
```

この問題では、Firebase Storageにデータを含めることができるが、「それを取得することも簡単にできてしまうよ」ということを伝えたかった問題になります。

## 問題7（XSSができてしまうかも？）
問題7は**XSS**に関する問題を用意しました。
今回用意したXSSは**alert**が発火したら答えが出るように工夫されています。
また、alertの中身は**base64エンコーディング**して、一応検索で引っかからないようにはしています。
用意したフォームにXSSが可能な文字列を入れたら、JavaScriptのコードが発火します。

発火方法は今回とても簡単にしました。以下のようなSVGのコードをフォームに入力した上で、**ブラウザをリロード**すると、答えが得られます。

```html
<svg/onload=alert("aaa") />
```

この問題では、フォームに入力した内容がそのままJavaScriptのコードとして実行されてしまうことを題材にしました。
最近ではフレームワークにより、XSSを塞がれてるケースは多いものの、XSSの種類は複数あり、とある起因で発火してしまうケースもあるので、**知識としては必ず抑えておく**べきでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/4bfd4c86-b667-4d9e-870d-9f65583c03b4.png)

## 問題8（Cookieが漏れちゃっているかも？）
最後の問題は**Cookie**に関する問題です。Cookieの内容を書き換えることで、答えが得られる内容になっています。

問題8のページにアクセスすると、`__session=koko-ga-session-id-dayo`がCookieに書き込まれていることが分かります。

<img width="1287" alt="Screen Shot 2020-04-15 at 21.29.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213538/9154b486-baa5-09b6-0146-722f726b7bee.png">

ヒントにある通り、問題5で得られたFirestoreの情報には`__session`も含まれています。そのセッションIDを下記のコードでCookieに書き込んだ上で、ブラウザを更新すると答えが得られます。

```js
document.cookie = "__session=d811774b-bbc3-4cd7-a0d8-a7043e63dfe2";
```

また、curlで叩いても同様の結果が得られます。

```sh
curl -i -b '__session=d811774b-bbc3-4cd7-a0d8-a7043e63dfe2' https://{デプロイ先のURL}/q8cookie
```

この問題では、セッションIDが何らかの原因で盗まれてしまった場合に、それを自分でCookieをセットしてリクエストできてしまうという一連の流れをやってみることを目的にしています。

**セッションハイジャック**などの攻撃手法などでもセッションIDを盗み取って攻撃に利用されたりするケースがあるので、それを少しでも感じてもらおうと思って作成しました。

# 終わりに
普段はベンチャーやスタートアップでアプリケーションを開発することが多く、セキュリティに中々携わる機会はなかったのですが、情報処理安全確保支援士になったこともあり、これからは意識的にセキュリティに関わっていこうと思っています。
今回の新卒研修もその一環で、今後も積極的にインプットやアウトプットをしていければと思います。
また、是非今回のアプリケーションに興味を持ってくれた人は実際に解いてもらって、フィードバックをくださると幸いです。