自分用メモ: ウェブ設計チェックリスト

コードレビュー時に使う自分用のチェックリストを作りました。

## データベースチェックシート

出典元:
[SQLアンチパターン](https://www.amazon.co.jp/dp/4873115892)
[「SQLアンチパターン」を避けるためのチェックリスト](https://www.ketancho.net/entry/2018/04/25/075500)

### データベース論理設計チェックシート

□ カンマ区切りフォーマットのリストを格納していないか？
□ 3世代以上の階層があるデータ構造を扱う場合に、直接の親子関係のみを参照する形にしていないか？
□ 主キー名を何も考えず `id` にしていないか？
□ 必要な外部キーは設定しているか？
□ 柔軟に「列」を増やすために、Attribute とその Value を表す列を作成してしまっていないか？
□ 複数の異なるテーブルに紐付く可能性があるテーブルを作成する場合に、xx_type といった列を見て紐付く先のテーブルを判断する設計になっていないか？
□ 1対Nで保持したいデータがある場合に、列を row1、row2、、、rowN のように増殖させる設計にしていないか？
□ 時間経過によってテーブルや列を増やすことを前提とした設計になっていないか？

### データベース物理設計チェックシート

□ 精度が求められる値の型に FLOAT 型を採用していないか
□ CHECK や ENUM などの列定義の制約で、列に入る値を限定してしまっていないか
□ 「何が何でもファイルはDBの外側で保持しなくちゃ！」と考えていないか
□ インデックスは正しく定義されているか、あるいは、インデックスを活かした検索をしているか

### SQLクエリ設計をする際のチェックポイント

□ NULLを一般値として使用していないか？一般値をNULLに相当するものとして扱っていないか？
□ GROUP BY句を使う場合、単一値の原則を満たすクエリとなっているか
□ ランダムにレコードを取得する場合、性能劣化しない方式を採用できているか
□ データ量が増える可能性のあるテーブルに、あいまい検索していないか
□ 複雑すぎるクエリを作っていないか
□ 必要のない列まで SELECT していないか

### アプリケーション設計をする際のチェックポイント

□ パスワードを平文でテーブルに格納していないか
□ 動的に SQL 文を作成するときに SQL インジェクションへの対策をしているか
□ 連番主キーの欠番を頑張って埋めようとしていないか
□ DB接続している部分で例外処理しないプログラムはないか
□ バージョン管理されてない DB 関連リソースはないか
□ モデルがアクティブレコード化していないか
□ 発生しうる問題やそれに対する対応はまとまっているか

## Web セキュリティ チェックリスト

出典元:
[安全なウェブサイトの作り方](https://www.ipa.go.jp/security/vuln/websecurity.html)

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

□ [根本的解決] SQL文の組み立ては全てプレースホルダで実装する
□ [根本的解決] SQL文の構成を文字列連結により行う場合は、アプリケーションの変数をSQL文のリテラルとして正しく構成する
□ [根本的解決] ウェブアプリケーションに渡されるパラメータにSQL文を直接指定しない
□ [保険的解決] エラーメッセージをそのままブラウザに表示しない
□ [保険的解決] データベースアカウントに適切な権限を与える

### OSコマンド・インジェクション対策

□ [根本的解決] シェルを起動できる言語機能の利用を避ける
□ [保険的解決] シェルを起動できる言語機能を利用する場合は、その引数を構成する全ての変数に対してチェックを行い、あらかじめ許可した処理のみを実行する。

### パス名パラメータの未チェック／ディレクトリ・トラバーサル対策

□ [根本的解決] 外部からのパラメータでウェブサーバ内のファイル名を直接指定する実装を避ける
□ [根本的解決] ファイルを開く際は、固定のディレクトリを指定し、かつファイル名にディレクトリ名が含まれないようにする
□ [保険的解決] ウェブサーバ内のファイルへのアクセス権限の設定を正しく管理する
□ [保険的解決] ファイル名のチェックを行う

### セッション管理の不備対策

□ [根本的解決] セッションIDを推測が困難なものにする
□ [根本的解決] セッションIDをURLパラメータに格納しない
□ [根本的解決] HTTPS通信で利用するCookieにはsecure属性を加える
□ [根本的解決] ログイン成功後に、新しくセッションを開始する
□ [根本的解決] ログイン成功後に、既存のセッションIDとは別に秘密情報を発行し、ページの遷移ごとにその値を確認する。
□ [保険的解決] セッションIDを固定値にしない
□ [保険的解決] セッションIDをCookieにセットする場合、有効期限の設定に注意する

### クロスサイト・スクリプティング対策(HTMLテキストの入力を許可しない場合の対策)

□ [根本的解決] ウェブページに出力する全ての要素に対して、エスケープ処理を施す
□ [根本的解決] URLを出力するときは、「http://」や 「https://」で始まるURLのみを許可する
□ [根本的解決] <script>...</script> 要素の内容を動的に生成しない
□ [根本的解決] スタイルシートを任意のサイトから取り込めるようにしない
□ [保険的解決] 入力値の内容チェックを行う

### クロスサイト・スクリプティング対策(HTMLテキストの入力を許可する場合の対策)

□ [根本的解決] 入力されたHTMLテキストから構文解析木を作成し、スクリプトを含まない必要な要素のみを抽出する
□ [保険的解決] 入力されたHTMLテキストから、スクリプトに該当する文字列を排除する

### クロスサイト・スクリプティング対策(全てのウェブアプリケーションに共通の対策)

□ [根本的解決] HTTPレスポンスヘッダのContent-Typeフィールドに文字コード（charset）の指定を行う
□ [保険的解決] Cookie情報の漏えい対策として、発行するCookieにHttpOnly属性を加え、TRACEメソッドを無効化する
□ [保険的解決] クロスサイト・スクリプティングの潜在的な脆弱性対策として有効なブラウザの機能を有効にするレスポンスヘッダを返す

### CSRF（クロスサイト・リクエスト・フォージェリ）対策

□ [根本的解決] 処理を実行するページを POST メソッドでアクセスするようにし、その「hidden パラメータ」に秘密情報が挿入されるよう、前のページを自動生成して、実行ページではその値が正しい場合のみ処理を実行する
□ [根本的解決] 処理を実行する直前のページで再度パスワードの入力を求め、実行ページでは、再度入力されたパスワードが正しい場合のみ処理を実行する
□ [根本的解決] Refererが正しいリンク元かを確認し、正しい場合のみ処理を実行する
□ [保険的解決] 重要な操作を行った際に、その旨を登録済みのメールアドレスに自動送信する

### HTTPヘッダ・インジェクション対策

□ [根本的解決] ヘッダの出力を直接行わず、ウェブアプリケーションの実行環境や言語に用意されているヘッダ出力用APIを使用する
□ [根本的解決] 改行コードを適切に処理するヘッダ出力用APIを利用できない場合は、改行を許可しないよう、開発者自身で適切な処理を実装する
□ [保険的解決] 外部からの入力の全てについて、改行コードを削除する

### メールヘッダ・インジェクション対策

□ [根本的解決] メールヘッダを固定値にして、外部からの入力はすべてメール本文に出力する
□ [根本的解決] ウェブアプリケーションの実行環境や言語に用意されているメール送信用APIを使用する（8-(i) を採用できない場合）
□ [根本的解決] HTMLで宛先を指定しない
□ [保険的解決] 外部からの入力の全てについて、改行コードを削除する

### クリックジャッキング対策

□ [根本的解決] HTTPレスポンスヘッダに、X-Frame-Optionsヘッダフィールドを出力し、他ドメインのサイトからのframe要素やiframe要素による読み込みを制限する
□ [根本的解決] 処理を実行する直前のページで再度パスワードの入力を求め、実行ページでは、再度入力されたパスワードが正しい場合のみ処理を実行する
□ [保険的解決] 重要な処理は、一連の操作をマウスのみで実行できないようにする

### バッファオーバーフロー対策

□ [根本的解決] 直接メモリにアクセスできない言語で記述する
□ [根本的解決] 直接メモリにアクセスできる言語で記述する部分を最小限にする
□ [根本的解決] 脆弱性が修正されたバージョンのライブラリを使用する

### アクセス制御や認可制御の欠落対策

□ [根本的解決] アクセス制御機能による防御措置が必要とされるウェブサイトには、パスワード等の秘密情報の入力を必要とする認証機能を設ける
□ [根本的解決] 認証機能に加えて認可制御の処理を実装し、ログイン中の利用者が他人になりすましてアクセスできないようにする

## Web API チェックリスト

出典元: [Web API: The Good Parts](https://www.oreilly.co.jp/books/9784873116860)

□ URIが短く入力しやすくなっているか
□ URIが人間が読んで理解できるようになっているか
□ URIが小文字のみで構成されているか
□ URIが改造しやすくなっているか
□ URIにサーバ側のアーキテクチャが反映されていないか
□ URIのルールは統一されているか
□ 適切なHTTPメソッドを利用しているか
□ URIで利用する単語は多くのAPIで同い意味に利用されているものを選んでいるか
□ URIで使われている名詞は複数形になっているか
□ URI中にスペースやエンコードを必要とする文字列が入っていないか
□ URI中の単語はハイフンでつないでいるか
□ ページネーションは適切に設定されているか
□ ログインには Auth 2.0 を利用しているか
□ レスポンスのデータ形式はJSONがデフォルトになっているか
□ データ形式の指定にはクエリパラメータを使う方法をサポートしているか
□ 不要なJSONPに対応していないか
□ レスポンスのデータ内容はクライアントから指定できるようになっているか
□ レスポンスデータに不要なエンベロープが入っていないか
□ レスポンスデータの構造は可能なかぎりフラットになっているか
□ レスポンスデータが配列ではなくオブジェクトになっているか
□ レスポンスのデータ名として多くのAPIで同じ意味に利用されている一般的な単語を選んでいるか
□ レスポンスのデータ名はなるべく少ない単語数で表現しているか
□ レスポンスのデータ名として複数の単語を連結する場合、その連結方法はAPI全体を統一してあるか
□ レスポンスのデータ名として変な省略形を使用していないか
□ レスポンスのデータ名の単数形／複数形はデータの内容と合っているか
□ エラー時のレスポンスはクライアントが原因を切り分けられるような情報を含んでいるか
□ エラーの際にHTMLが返っていないか
□ 適切なステータスコードが返るようになっているか
□ メンテナンス時には503をお返すようになっているか
□ 適切なメディアタイプを返しているか
□ 必要な場合はCORSに対応しているか
□ クライアントが適切にキャッシュを行えるようにCache-Cntrol、ETag、Last-Modified、Varyなどのレスポンスヘッダを返しているか
□ キャッシュをさせたくないデータにはCache-Control: no-cacheが付けられているか
□ APIはバージョンで管理されているか
□ APIのバージョンはセマンティックバージョニングに沿ったものになっているか
□ メジャーバージョン番号がURLに入っており、ひと目でわかるようになっているか
□ APIの提供を終了する際のことを考慮に入れているか
□ APIの最低提供期間をドキュメントに明記しているか
□ HTTPSでAPIを提供しているか
□ jSONのエスケープはきちんと行っているか
□ JSONはX-Regueested-Withヘッダを認識するなど、SCRIPT要素では
読み込まないようになっているか
□ ブラウザからアクセスさせるAPIではXSRFトークンを利用しているか
□ APIが受け取るパラメータはきちんと不正値(マイナスの値など)チェックしているか
□ レスポンスにセキュリティ対策用の各種ヘッダをきちんと付けているか
□ レートリミットによる制限を行っているか
□ レートリミットの制限回数は想定されるユースケースに対して少なすぎないか

## リーダブルコードチェックリスト

出典元:
[リーダブルコード ――より良いコードを書くためのシンプルで実践的なテクニック](https://www.oreilly.co.jp/books/9784873115658)
[リーダブルコード適用 チェックリスト](https://qiita.com/AKB428/items/574f94695de51fa1fa19)

□ コードは理解しやすくなければならない。
□ コードは他の人が最短期間で理解できるように書かなければならない。
□ tmp,retval,resultなどの汎用的すぎる変数を使っていないか。
□ 変数名の英単語を省略して理解しずらいものになっていないか。
□ 変数名が長くなるのを恐れて短くしすぎて理解しずらいものになっていないか。
□ 限界値を示す変数に min_ max_を使っているか。
□ 範囲を示す変数に first_ last_を使っているか。
□ getXXXXメソッドをアクセッサー以外で定義していないか。
□ インデントが整っており見やすいコードになっているか。
□ 意味のないコメントを書いていないか。
□ コードから読み取れる事をコメントに書いていないか。
□ 定数にはコメントが書かれているか。
□ クラスの使用方法など高レベルのコメントが必要なファイルには、ファイルの先頭にコメントが書かれているか。
□ ヨーダ記法を使用していないか。
□ 三項演算子を使った結果、コードが理解しずらいものになっていないか。
□ do~whileループを使用していないか。
□ 関数内で早めに処理が抜けられるものはreturnで適切にガード節が作られているか。
□ ifのネストが深くなっていないか。
□ 説明変数を使って改良できるコードが残っていないか。
□ 要約変数を使って改良できるコードが残っていないか。
□ 短絡評価を悪用、乱用していないか。
□ ループ処理の中で不要な中間結果を保持する変数を使用していないか。
□ ループ処理の中で不要な制御変数を定義して使用していないか。
□ グローバルなスコープが必要ないグローバル変数を定義していないか。
□ クラス変数のスコープが必要ないクラス変数を定義していないか。
□ JavaScriptで変数がグローバル領域にはみ出していないか。
□ 古いC言語のルールに沿って意味もなく変数の定義を全て関数の先頭に書いていないか。
□ 変更の必要ない変数には積極的にfinalがついているか。(C++, java)
□ メソッドの抽出が必要なコードは残っていないか。
□ ひとつの関数内に複数のタスクが書かれていないか。
□ 関数名を逸脱した複数のタスクがひとつの関数内に書かれていないか。
□ 標準APIにある機能をオレオレ実装していないか。
□ 広く使われている外部ライブラリ(Apache CommonsやBoost)にあるような機能をオレオレ実装していないか。
□ テストコードが理解しづらいものになっていないか。

## 設計・アンチパターン資料

- [SQLアンチパターン 幻の第26章「とりあえず削除フラグ」](https://www.slideshare.net/t_wada/ronsakucasual)
- [これだけ抑えればOK!権限管理のDB設計デザインパターン](https://waterlow2013.hatenablog.com/entry/2017/01/27/233405)
- [マリオカートのER図について考える](https://qiita.com/assu_mz/items/9d80320e6f778d83c61f)
- [ユーザ情報を保存する時のテーブル設計](https://soudai.hatenablog.com/entry/2018/05/01/204442)
- [RESTful APIのHTTPステータスコード設計](https://qiita.com/NagaokaKenichi/items/eb85b5fbb719d60c6627)
- [DBの寿命はアプリより長い！ 長生きするDBに必要な設計とリファクタリングを実践から学ぶ](https://employment.en-japan.com/engineerhub/entry/2018/12/11/110000)
- [DBリファクタリングをやっているって話](https://soudai.hatenablog.com/entry/2018/05/25/121801)
- [本当にあったやらかしDB設計シリーズ一覧](https://qiita.com/bouitengineer12/items/e63e5f7d6f628da11362)
- [データベース物理設計のアンチパターン](https://kyabatalian.hatenablog.com/entry/2016/12/23/172700)

## 書籍

- [失敗から学ぶRDBの正しい歩き方 (Software Design plus)](https://www.amazon.co.jp/dp/4297104083)

## さいごに

オススメの書籍やスライド資料などありましたらコメントで教えてもらえると嬉しいです！