04.質問形式で気になる設計を見ていこう!
概要
ここでは、初級者が引っかかりそうな、設計上の良くない点を見ていこうと思います。
情報が多いと読む気がしないと思いますので、質問形式にしてみました。
質問に答えながら、気になる回答から読んでみてもらえればと思います。
ただ、設計方法は1つではありませんので、あくまで一つの考え方として読んでいただければと思います。
質問
複数のサーバでロードバランスすることを考えた設計にしていますか?
一般的には、負荷やサーバダウン時にもWEBが動作するように、ロードバランサなどを導入します。
ただ、ロードバランサを導入しただけではだめです。
アプリもそれに対応するように設計しないといけません。
以下を読んでみてください。
参考: 信頼性設計
セッションに何でも保存していませんか?
セッションは便利で、いろいろ保存できます。
しかし、何のポリシーもなく保存してしまうとすぐにメモリが足りなくなります。
また、ロードバランスすることなどを考えても、しっかりと設計しないといけません。
以下を読んでみてください。
参考: 信頼性設計
CSRF対策していますか?
最近ではCSRF対策は必須です。
設計に含めずに納品して、もし、お客様から対策していないことを指摘を受けた場合、
当然対応しなければなりません。
参考: CSRF対策
例えば、社内システムで、外部に公開せず、お客様にも合意をとってコスト削減のために
あえて対策を実装しないのであれば問題ないとは思います。
XSS対策していますか?
クロスサイトスクリプティングを使った攻撃は古典的な攻撃で、これもCSRFと同様に対策必須です。
設計に含めずに納品して、もし、お客様から対策していないことを指摘を受けた場合、
当然対応しなければなりません。
参考: XSS対策
参照や編集画面で閲覧して良いIDかをチェックしていますか?
ログインしたユーザが扱ってよいIDかをチェックしていますか?
もしチェックしない場合、成りすましができてしまうかもしれません。
参考: なりすまし対策
画面ごとにDaoを作っていませんか?
初級者が良くやってしまう間違いに、画面ごとにビジネスロジックを作る、画面ごとにDaoを作る、
ということがあります。
そうするのであれば、ビジネスロジックやDaoを作る意味はありません。
SQLの実行なども、すべてControllerの中で実装しても同じです。
そもそもビジネスロジック、Daoを作る理由は何でしょうか?
それは、さまざまな機能で再利用できるようにするためです。
つまり、画面ごとに機能を考えるのではなく、ユーザ情報を扱うサービス、とか、商品購入を
扱うサービス、のように、サービスごとに機能を整理して、共通的に使用できるようにして、
開発の効率を良くするのが目的のはずです。
ただ、新規作成するときは将来も考えて設計しなければならないため、返って稼働はかかるかもしれません。
しかし、機能追加や故障対応の時は稼働は減るはずです。
監査ログは出していますか?
最近では必須と言ってもいいログです。
監査ログとは、あるログインユーザが操作した内容を追跡できるようにしたログです。
具体的には、あるユーザが情報漏えいをした疑いがあるときに、どんな情報を持ち出したかを分かるようにします。
それによって、警察などは漏えいした情報と持ち出した情報が一致すれば疑義を固められるでしょう。
ではどんな情報を出せばよいでしょうか?
それはお客様によっても違うので何とも言えないですが、例えば、表示した画面と表示した情報です。
画面はURLのパス名を出力すればよいかもしれませんが、表示した情報は量が多すぎます。
そこで、キーになる情報、例えばユーザ情報の表示画面であればそのIDを出力したり、検索画面であれば
検索条件を出力しておけばまずまずというところだと思います。
場合によっては、情報変更時に変更点だけログ出力を要望される場合もあると思います。
参考: ログ設計
ログは問題のあるメッセージだけを拾えるように出力していますか?
アプリのログを監視することがあります。
監視して異常があったときに素早く対処するためです。
ログに異常が見つかればメールが飛び、場合によっては人が駆けつけるでしょう。
しかし、ログの監視はツールなどで自動で行われるため、ある文字が入っていたら
異常メールを出すといった感じになります。
このとき拾うべき文字列が多すぎたり、複雑だったりすると拾いにくくなります。
通常はログのある位置にERRORという文字列があったら、などとすると思います。
つまり、エラーでないのにログレベルがERRORで出力されると迷惑をかけることになります。
ですので、どういったときにどんなログレベルを出力するのかをしっかり設計しなければなりません。
参考: ログ設計
ControllerやビジネスロジックでConnectionやDataSourceを扱っていませんか?
ビジネスロジックは、先ほどの質問の回答通り、再利用性を高める目的で作成します。
データアクセス(Dao)と分離していることで、DBを別の種類にしても、Daoだけを編集すれば対応できるように
なります。
このように分離させた部分が独立して動くようにします。
そうすると、Daoは背後のDBとの仲立ちをして、背後の存在を隠すようにしないといけませんので
基本的にはConnectionやDataSourceをビジネスロジックに表出するようにしてはいけません。
Springトランザクションを使用すると簡単にこれを実装できます。
どこでどのエラーを補足するか設計していますか?
エラーの補足とエラー捕捉後の処理は重要な課題です
例えば、楽観的ロックをしてエラーになった場合、1秒後にリトライする、などが考えられます。
そして、こういった処理は最初に設計に組み込んでおかないと、設計が大きく変わることが多いため、
後で実装しようとするとかなり大きな稼働ロスになります。
参考: 05.設計2: フレームワークの組み合わせとエラートラップ
tomcatのデフォルトのエラー画面を出していませんか?
あまり気にしていないかも知れませんが、WEBサーバの種類や、バージョンが分かってしまうのは危険です。
なぜなら、「あるWEBサーバのあるバージョンにはこういったセキュリティホールがある」といった情報は
WEB上に流通しているからです。
攻撃者はバージョンなどが分かれば、それに応じた攻撃を必ず試すでしょう。
これに対処するためには、エラーをトラップしてバージョンなどが出力されない独自の画面を出すことです。
参考: 05.設計2: フレームワークの組み合わせとエラートラップ
URLの拡張子が、jspやdoなど、使用しているWEBサーバやフレームワークがバレる拡張子にになっていませんか?
これも理由は、上記の回答と同じです。
WEBサーバだけでなく、フレームワークが分かるようにするのも避けるべきです。
例えば、.jspという拡張子は、Javaであることが分かってしまいます。
また、.doはstrutsであるなども分かってしまいます。
本サンプルでは拡張子は、「.html」 にしています。
エラー画面にスタックトレースが出るようになっていませんか?
スタックトレースも出力を避けた方がいいです。
この対策も上記と同じで、エラーをトラップして、独自の画面を出すことになります。
参考: 05.設計2: フレームワークの組み合わせとエラートラップ
tomcatを停止せずにWARファイルを再配置できる設計にしていますか?
tomcat7から、tomcatを停止せずにWARファイルを配置できるようになりました。
無停止の動作としては、現在のWARを動作させながら、新しいWARを配置していくようです。
このときもし、現在配置されているWARで処理途中のリクエストがあれば現在のWARで最後まで処理し、
配置後にきたリクエストは新しいWARで処理されるといった具合です。
この機能を有効にする配置のやり方は簡単で、WARファイル名に##001のような番号をつけるだけです。
しかし、アプリの設計が悪いと再配置時にエラーになって、配置ができない可能性があります。
せっかく良い機能があるのに、そんな事態は避けた方がいいかと思います。
そんなケースになりうるのは、例えば、必要になるたびに設定ファイルを読み込む設計にしていて、
次のバージョンのWARで設定ファイル内の項目名が変わったり、削除されたりした場合です。
その場合、設定ファイルもWAR配置と共に置き替えると思いますが、
古いバージョンのWARが新しい設定ファイルを読み込んだ場合、項目が見つからないので
当然ながらエラーになると思います。
他にもtomcatのこの機能がうまく行かないケースはあると思います。
気を付けて設計すべきかと思います。
tomcat内の他のアプリと、DBドライバーのバージョンが違っていませんか?
tomcat7以降はDBドライバーの扱いが変わりました。
気を付けて、ファイルやディレクトリ構成の設計をしましょう。
ファイル構成を失敗するだけで、メモリリークが発生する可能性があります。
また、1つのtomcatには、1つのバージョンのドライバーしか使えません。
ですので既存のtomcatに新規アプリを追加する場合は、既存のアプリのドライバーのバージョンと
同じかを確認する必要があると思います。
参考: DBドライバーについて
URLのパス名のルールは決めていますか?
昨今の開発では、URLのパスは重要な役割を担っています。
RESTなどは顕著な例で、RESTでは、URLが/member/edit/1などとなっていた場合、
ID=1のユーザを編集するなどの意味を持ちます。
RESTでなくても、SpringSecurityなど様々な場面で重要です。
SpringSecurityでは、URLのパスごとにしか認可のルールを決められません。
また、ルールを記述するときもパスのルールがしっかり決まっていないと
たくさんルールを記述しないといけなくなります。
一定のルールで決まったパスであれば、ContorllerやViewの設定もしやすくなります。
参考: URLパス
DBのテーブルのカラムの値に、NULLを許可していませんか?
よくやってしまうことに、NULLに意味を持たせるということがあります。
例えばHDDレコーダの状態を表すカラムがあり、1:再生中、2:録画中、のように決め、
NULLを再生も録画もしていない状態、というようにNULLに意味を持たせるやり方です。
決めの問題なので、必ずしも間違いではないのですが、SQL文が難しくなるのは確かです。
参考: DA設計を参照
Controllerのデバッグをeclipseでtomcatを起動して行っていませんか?
Spring3.2以降では、JUnitでControllerをtomcatで起動したときのようにテストできます。
tomcat起動は時間がかかるうえ、目的の画面に遷移するのも大変です。
Springのテスト機能を使った方が良いと思います。
ある程度デバッグを実施して安定してきたら、eclipseでtomcatの起動してJSPなどの
デバッグをするのが効率が良いかなと個人的には思っています。
参考: 単体テスト
Created Date: 2015/04/01