「安全なWebアプリケーションの作り方」を読んだ所感など

# はじめに
本記事は、下記書籍を読んだ所感などをまとめたものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/27dd9a1d-7178-298f-28c2-874095c5e810.png)

https://www.sbcr.jp/product/4797393163/

（以下本書）

# 読み終わった感想

**もっと早く読めばよかった**……:innocent:

**すべてのWebアプリケーション開発者**におすすめできます！

Webアプリケーションにおける脆弱性や攻撃手法、そして対策などを**体系的**に学ぶことができました！

今後、新たにWebアプリケーションを開発する際に役立てたいと思います。

もし未読の方がいらっしゃいましたら、ぜひお手に取っていただくことをおすすめします！

# 学んだこと、心に残った箇所など

それではここからは、本書を読み進める中で学んだことや、心に残った箇所などを記載します。


## なぜ脆弱性があるといけないのか？


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/aeda3df8-bdbe-585e-5243-f0af601fcdb1.png)



そもそも、なぜ脆弱性があるとダメなのでしょうか？

:man_tone1:「脆弱性？」
:man_tone1:「うーん」
:man_tone1:「もし何かあったとしても、**利用者に補償すればいいんでしょ？**」

などと、**軽くとらえてはいけません**！

あなたのWebアプリに**脆弱性があってはならない**理由は、以下の通りです。

:::note alert
**経済的損失**
- 利用者への金銭的補填
- 社会的信頼・売上の減少
- 損害賠償
:::
:::note alert
**法的な要求**
- 個人情報保護法
:::
:::note alert
**利用者への、回復不能なダメージ**
- 一度流出した個人情報は、回復不可能。
- 利用者の不安や苦痛は、お金では解決できない。
:::
:::note alert
**攻撃インフラの構築を助けてしまう**
- 迷惑メール送信やDos攻撃などのインフラになってしまう:scream:
:::


個人的に、最後の「攻撃インフラの構築を助けてしまう」というのが心に刺さりました。

**犯罪行為の片棒を担がない**ように、しっかりと知識をつけようと決意しました。

## クッキーを盗まれるとどうなるのか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/296c6c5d-1b7d-d9e2-609e-d16f7872e887.png)

クロスサイト・スクリプティング攻撃などによってクッキーが盗まれると、どうなるかというと……

**他ユーザーへのなりすまし**が可能になります！！

クッキーには、「セッションID」が含まれます。

セッションIDが盗み出せれば、なりすましができるようになります。

なりすましをされると、例えば以下のような攻撃が想定できます。

:::note warn
- **Twitterなどで勝手にツイートができる**
    - 罠サイトのリンクを貼ったりも可能

- **迷惑メールを送信できる**
    - メールサービスにログインされると、あなたが迷惑メールの送信者となってしまいます
- **通販サイトで買い物される**
    - これほんとこわい

:::

対策としては、**外部からの入力には注意する**ことです！
- まずはエスケープ
- 厳密なバリデーションチェック

## JavaScriptの動的生成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/c878d265-c40a-7da9-5ded-e02a706dface.png)

上記のように、フォームなどで外部からの入力を受け付ける場合は要注意です。

JavaScriptなどにより攻撃されてしまいます！

（というより、攻撃されることを前提として開発しましょう）

入力値は適切なエスケープを行うようにします。

具体的には、以下の記号は必ずエスケープします。

:::note warn
エスケープ必須の記号
- \
- '
- "
- 改行
- <
- \>
:::


## エラーメッセージは画面に表示しないようにする

エラーメッセージとは、Laravelでいう以下のような表示のことです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/514086eb-8697-93da-322e-9577992ad63a.png)

https://codezine.jp/article/detail/12130

なぜエラーメッセージを表示してはいけないかというと、以下の理由からです。

- **エラーメッセージには、アプリケーションの内部情報が含まれるから**
    - 攻撃者に有利な情報を与えてしまう
- **意図的な攻撃として、エラーメッセージに個人情報などを表示させられるから**
    - SQLインジェクションなど

Webアプリごとに専用のエラー画面を用意しておきましょう！

## SQLインジェクションの恐怖

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/1168ffc2-4275-5225-06c2-fc0891ff75b4.png)

https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_1.html

SQLインジェクションとは、「**SQLの不備に関連する脆弱性**」です。

SQLインジェクションがあると、以下のような攻撃を受ける可能性があります。

:::note alert
SQLインジェクションにより発生する攻撃
- データベース内の情報が盗まれる
- データベース内の情報が書き換えられる
- 認証を回避される
- データベースサーバー上のファイルへが読み込み、書き出し、実行される
:::


**おそろしい……！！**:sob:

対策は後ほど記載します。

## SQLの静的プレースホルダーと動的プレースホルダーの違い

**プレースホルダー**とは、SQLに動的な値を組み込む記述方法です。

```php:プレースホルダーの例
// SQLのプレースホルダーを指定
$sql = 'SELECT * FROM users where name = :name';

// SQLをプリコンパイル
$stmt = $dbh->prepare($sql);
 
// プレースホルダーに値をバインド
$stmt->execute([
   ':name' => $name,
]);
```

このバインドを……
- データベースエンジン内で行う → **静的プレースホルダー**（推奨！）
- Webアプリ内で行う → **動的プレースホルダー**（十分安全だが、できれば静的にしたい）

というわけで、まずは**プレースホルダー**を使用しましょう(`must`)。

可能であれば**静的プレースホルダー**を設定しましょう(`want`)。

## SQLインジェクションへの対策

SQLインジェクションへの具体的な対策は、以下の通りです。

:::note info
SQLインジェクションへの対策
- エラーメッセージを詳細に表示しない
    - php.iniの設定を以下のようにします
    - `display_errors = off`
- 入力値の検証
    - 可能な限り、文字列は入力させない
    - プレースホルダーを使用する
- データベースの権限設定
    - Webアプリのデータベースユーザの権限を必要最小限にする
        - 検索ページであれば、**参照権限のみ**にするなど
:::




## CSRFへの対策

クロスサイト・リクエストフォージェリ（CSRF）とは、罠サイトなどを利用して「勝手に**重要な処理**を実行する」ことをいいます。

**重要な処理**の例は以下の通りです。
- 利用者のクレジットカードでの決済
- 利用者の口座からの送金
- パスワード変更
- 個人情報変更

CSRFへの対策は以下の通りです。

:::note info
**利用者の意図したリクエストであることを確認する**
- トークンの埋め込み（フレームワークに搭載されている機能を使用）
- パスワード再入力（正規利用者であることを確認）
- Refererのチェック（直前のページに関する情報を検証する）
:::


なお、CSRF対策はすべての画面に必要というわけではありません。

CSRF対策が必要な画面は以下の通りです。
- 重要な処理を行う画面
    - パスワード変更
    - マイページ
    - 個人情報編集
    - 商品の決済

## セッション管理の重要性

セッションIDは、**第三者が推測または盗み出せないようにする**必要があります。

大抵のフレームワークにはセッション関連の機能があらかじめ備わっているはずなので、できるだけそちらを使用するようにしましょう！

もしもプレーンなPHPでセッションを管理する場合は、php.iniの設定を以下のように見直しましょう。

```ini:php.ini
[Session]
session.entropy_file = /dev/urandom
session.entropy_length = 32
```

## オープンリダイレクトへの対策

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/f5a75e9d-ea7c-b775-2cfb-75616dec4756.png)


オープンリダイレクトとは、**外部からURLを指定してリダイレクトさせる**攻撃方法です。

**フィッシング詐欺**でよく用いられる攻撃方法ですね。

例えば、攻撃者はスパムメールなどで、以下のようなURLをクリックさせます。

```bash:http://trap.comにリダイレクトさせる例
https://example.jp?url=http://trap.com/trap.php
```

オープンリダイレクトへの対策をしていない場合、利用者は罠サイトを訪問していることに気づかずに操作してしまうかもしれません！

対策は以下の通りです。

:::note info
**オープンリダイレクトへの対策**
- リダイレクト先のURLを固定にする
- リダイレクト先のURLをホワイトリスト管理する
- リダイレクト先のドメインをチェックする
:::



## OSコマンドインジェクションへの対策
Webアプリ内で実行されるOSコマンドを悪用する攻撃を**OSコマンドインジェクション**といいます。

OSコマンドを悪用されるということは、OSを乗っ取られるようなものなので、なんでもできてしまいますね……。

本書では、メールアドレスを入力するフォームに関する脆弱性が詳細されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/928c2ef7-be3f-dad8-9a37-613148c6926f.png)

```php:脆弱性のある処理
$mail_address = filter_input(INPUT_POST, 'mail_address');
system("/usr/sbin/sendmail -i < mail_body.txt $mail_address");
```

上記の処理に、↓の文字列を渡すとどうなるでしょうか？

```shell:OSコマンドインジェクションの例
bob@example.jp;cat /etc/passwd
```

そうです。
**catコマンドが実行され、/etc/passwdファイルが画面に表示されてしまいます**:scream:

OSコマンドインジェクションへの対策は以下の通りです。

:::note info
OSコマンドインジェクションへの対策
- OSコマンドを使わない実装を検討する
    - メール送信であれば、mb_send_mail関数を使う
- 外部から入力された文字列をパラメータに渡さない
- 文字列をエスケープする
:::

## DoS攻撃への対策

DoS攻撃（*Denial of Service attack*）とは、**Webアプリケーションに対し大量のデータを送信する**攻撃方法です。

**サイトの応答速度が低下**したり、**サーバーの停止**といった影響があります。

DoS攻撃に対しては、「**アップロードファイルの容量を制限する**」という対策が有効です。

PHPであればphp.iniの設定を見直し、できるだけ小さな値にしておくとよいでしょう。


## クロスサイト・スクリプティングへの対策

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/76747259-b4bf-859e-9e24-b8a5d555780d.png)

クロスサイト・スクリプティングとは、**ファイルをダウンロードした際に、悪意のあるスクリプトを実行させる**攻撃手法です。

簡単な流れは以下の通りです。

#### ① まず攻撃者が、悪意のあるスクリプトファイル`example.pdf`をWebアプリ上にアップロードしておきます。

`example.pdf`は、見た目はPDFですが、中身はスクリプトファイルです。

#### ② 続いて他のユーザーが、`example.pdf`をダウンロードします。
（このとき、ユーザーはスクリプトファイルだと気づいていません）

#### ③ ダウンロードが完了すると、ユーザーのPC上で悪意あるスクリプトが実行されます！

`example.pdf`がPDFではないと気づいた時には、個人情報が抜き取られていた……。

といったイメージです！

このクロスサイト・スクリプティングへの対策は以下の通りです。


:::note info
クロスサイト・スクリプティングへの対策
- ファイルのContent-Typeをチェックする
    - PDFであれば`application/pdf`
    - 拡張子をチェックすることも大事
- WEBサーバーのレスポンスヘッダ設定を見直す
    - Apache
        - `Header always append X-Content-Type-Options: nosniff`
    - nginx
        - `add_header X-Contents-Type-Options: nosniff;`
:::


## JSONPは取扱注意！

**JSONP**(*JSON with padding*)は、他のオリジンサーバーからJSONデータを取得するための仕組みです。

JSONPの使用例としては以下のような記述が挙げられます。

```php: JSONPの使用例
$call_back = $_GET['callback'];

$json = json_encode(['time' => date('G:i')]);

echo "$callback($json)";
```

JSONPはとても便利な仕組みですが、使い方を間違えると**脆弱性となりえます**！

:::note info
JSONP使用時は、JSON文字列を適切にエスケープしましょう！
:::

## APIレスポンスにはMIMEタイプを指定する

MIMEタイプの指定を怠ると、脆弱性となります。

例えば、以下のようなWebAPIがあったとします。

```php:郵便番号から住所を返すAPI
$zip = $_GET['zip'];

echo json_encode(["message" => "郵便番号が見つかりません: " . $zip);
```

本来ならばMIMEタイプとして`application/json`であるべきですが、指定を怠っています。

このとき、レスポンスはPHPデフォルトの`application/html`となります。

MIMEタイプとして`application/html`が指定されているため、下記のリクエストを送るとJavaScriptが実行できてしまいます！

```bash:APIに対してJavaScriptを送信する例
https://example.jp?zip=<img+src=1+onerror=alert(document.domein)>
```

上記リクエストを実行すると、レスポンスでJavaScriptの`alert`メソッドが実行できてしまいます。

:::note info
APIレスポンスには、MIMEタイプを指定しましょう！
:::


## レインボーテーブルの恐怖

現代においては、パスワードのハッシュ値を一覧にまとめた表が存在します。

この表を**レインボーテーブル**といいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/fcc63252-2d50-fd0a-5917-659db66c701f.png)

https://ja.wikipedia.org/wiki/レインボーテーブル

レインボーテーブルにより、8文字までの**MD5**によるハッシュ化は短時間で解読可能です。

もはやMD5は安全ではありません！

:::note info
レインボーテーブルへの対策
- パスワードを長くする（20文字）
    - **ソルト**（*salt*）を使用するとよい
    - ソルトは、元データに追加する文字列のこと
    - ソルトにより、見かけ上はパスワードを長くできる
:::


## データベースの文字コードは統一する

データベースは、以下の箇所で文字コードを指定できます。

:::note warn
文字コードを指定できる箇所
- レコード作成時
- レコード更新時
- データベースエンジンとの接続時
:::

常に正しい文字コードを指定するようにしましょう！

`UTF-8`か`UTF-16`が推奨されています。

データベースの文字コードが統一されているかを判定するために、以下のテストを実行することをおすすめします。

#### 尾骶骨（びていこつ）テスト
`尾骶骨`という3文字をデータベースに登録し、画面に表示するテスト。

きちんと`尾骶骨`と表示されていればOK！

`尾〓骨`、`尾 骨`などと表示された場合は、途中で文字コードがShift-JISなどに変わっている可能性があります。

同様に、「𠮷（つちよし）テスト」もおすすめです。

`𠮷`という文字を登録し、画面に表示するテストです。

画面に`𠮷`が表示されればOKです！

みなさまも、もし機会があればお試しください:muscle:

## 終わりに

今回は、「安全なWebアプリケーションの作り方」を読んだ所感などをお伝えしました。

セキュリティに関して、体系的に学べる良書だと感じました！

もし未読の方がいらっしゃいましたら、ぜひお手に取っていただくとよいと思います！

それでは、ここまで読んでいただきありがとうございました:wave: