エンジニアを目指す初学者に向けて、わかりやすく解説したブログです。

クライアントサイドとサーバーサイドのバリデーション設計

前提

  • クライアントサイド:html,jsなどのブラウザ上で発生する処理
  • サーバーサイド:SSRにおけるレンダリング前の処理や、クライアントサイドからのAPIリクエストによってサーバー上で行われる処理

結論

この図が筆者の結論である。

Image in a image block
  • サーバーサイド:想定される全てのバリデーションを行う
  • クライアントサイド:ユーザー体験のためのバリデーションを行う

サーバーサイドでは全てのバリデーションを実装し、
クライアントサイドはUX、費用対効果を考えつつ必要だと判断したもののみを実装する。

バリデーションの目的

この議論に関しては、バリデーションの目的から考えることが重要である。

バリデーションの目的は主に3つ。

  1. 悪意のある攻撃から守るため
  2. 不正なデータを混入させないため
  3. ユーザー体験を向上させるため

1. 悪意のある攻撃から守るため

例えば以下のようなケースである。

  • SQLインジェクション
  • XSS
  • CSRF

この目的に関しては、今回悩むようなことではない。

なぜならこれは「クライアントとサーバーどちらで実装すべきか?」というよりは
想定される攻撃に応じて、以下のどれかに自然と着地するからだ。

  • クライアントサイドで対策すべきこと
  • サーバーサイドで対策すべきこと
  • クライアントサイドとサーバーサイド両方で対策すべきこと

2. 不正なデータを混入させないため:サーバーサイドで処理

例えば以下のようなケースである。

  • 存在しないメールアドレス
  • (ユーザーが任意にユーザーIDを決められる前提で)使いたいユーザーIDが他ユーザーに既に使われていた
  • メールアドレスの形式が不正(例: @がないなど)
  • 親チケットに紐づく子チケットを作ろうと思ったが、親チケットが存在しない
  • 必須項目が不足している
  • 金額を入れるはずのところに文字列が入った
  • 金額を入れるはずのところにマイナスの値など不正な値が入った
  • 初回パスワード設定において、「1」という明らかに脆弱なパスワードを利用しようとした

このようなデータは、データベースに登録したり、後続処理を行おうとしたときにエラーになる。

これらは全てサーバーサイドで処理すべきである。

なぜなら、クライアントサイドの処理は信用できないからである。

ブラウザの開発者ツールで値をいじったり、buttonタグのdisabledを外して押せるようにしたり、curlのリクエストを作成して送信されたりすることは
「想定内のこと」として考えなければならない。

つまり、究極的にサーバーサイドしか信用できないため
サーバーサイドでは思いつく限りのバリデーションをしておかなければならない。

3. ユーザー体験を向上させるため:クライアントサイドで処理

とは言え「サーバーサイドでバリデーションしているからクライアントサイドではバリデーションをする必要がない」としてしまうと
ユーザー体験が悪いという不都合が発生する。

  • フォームにたくさんの情報を入力して送信ボタンを押したのに、最初からやり直しになった
  • パスワードのルールはサイトによって違うので、入力時点でよくないパスワードだったら教えてほしい(◯文字以上、英数字、記号ありなど)

クライアントサイドでは、このような悩みを解消するためのバリデーションを実装すべきである。それがサーバーサイドと重複していたとしても。

例:商品購入時に「クーポンを適用する」場合

クライアントサイド サーバーサイド 理由
クーポンのフォーマット(文字数、英数字など)が正しいか? チェックする チェックする このルールはクライアントサイドでチェックしたほうがユーザーのやり直しが減る。
難しいロジックではないので、htmlの正規表現チェックでも十分。
開発者ツールなどで不正なリクエストが可能なため、サーバーサイドでもチェックが必要。
そのクーポンコードが存在するか? チェックしない チェックする これはデータベースに問い合わせる必要がある。
このチェックをクライアントサイドで入力文字列が変わるたびにチェックするのは非現実的である。
また、クライアントサイドで判定しようとすると判定ロジックやホワイトリストがブラウザ上に存在することになり、クーポンコードが漏洩する。
そのクーポンコードは有効期限内か? チェックしない チェックする クーポンコードが存在しないと判定できないので
上記に依存してサーバーサイドで判定しなければならない。

全てサーバーサイドで判定しつつ、
簡易的なフォーマットチェック程度であればクライアントサイドで判定しておいたほうがUXが良いだろう。

ただし「フォーマットもビジネスルールである」と言えるレベルで複雑だったとしたら、
フォーマットチェックは以下のようにする可能性もある。

  • クライアントサイド:必要な文字で構成されている、文字数が適切な範囲内など
  • サーバーサイド:ハイフン区切りかつ前の文字列が大文字で、後ろの文字列が小文字、など

→基本的にクーポンコードはコピペするようなものなので、
クライアントサイドで厳密なフォーマットチェックを行ってもムダ努力になると考えられる。

まとめ

  • サーバーサイドでは全てのバリデーションチェックを行い、その中からUX向上に必要なものだけクライアントサイドで判定する
  • クライアントサイドの処理は信用してはならない