アンチパターン
ソフトウェア開発におけるアンチパターン (英: anti-pattern) とは、必ず否定的な結果に導く、しかも一般的に良く見られる開発方式を記述する文献形式を言う。
はじめに
ローカル環境(開発環境)でRuby on Railsアプリケーションの開発をするとき、エラー(例外)が発生すると以下のようなエラー画面が表示されます。
しかし、本番環境にRailsアプリをデプロイすると、ローカル環境のようなエラー画面は表示されず、デフォルトでは"We're sorry, but something went wrong."というようなエラーメッセージが表示されます。
ローカル環境のエラー画面では、エラーが発生した行やスタックトレース等の情報が豊富に表示されるので、デバッグに役立ちます。
一方、本番環境ではそういった情報がまったく表示されないため、どこでどんなエラーが発生したのかわかりません。
では、こんなときはどうしたらよいでしょうか?
この記事では本番環境でエラーが発生した場合の(よくある)間違った方法と、適切な対処方法についてまとめます。
間違った対処方法☠️
"rails 本番環境 エラー画面"のようなキーワードでネットを検索すると、次のように、「config/environments/production.rb
の config.consider_all_requests_local
を true
に変更してデプロイすればOK」と書いてある記事が上位に表示されます。しかし、 この対応はNG です。(つまりアンチパターン)
# falseからtrueに変更する、はNG!!
config.consider_all_requests_local = true
たしかに、こうすると本番環境でローカル環境と同じエラー画面が表示されます。しかし、これだと本来は開発者以外知る必要がないソースコードや実行環境の情報が不特定多数の人々に公開されてしまいます。これはセキュリティ上の深刻なリスクになります。(この点については本記事の後半で詳しく議論します)
ですので、 安易に consider_all_requests_local
を true
に変更することはやめましょう! (ダメ、ゼッタイ🙅🏻♂️)
適切な対処方法😊
前述の通り、本番環境でローカル環境と同じエラー画面を表示させるのはセキュリティ上のリスクが高いのでいさぎよく諦めましょう。
本番環境で発生したエラーを確認する場合は、以下のような方法が考えられます。
ログを見る
本番環境でエラーを確認する一番の正攻法はログを見ることです。検索するならエラー画面を表示する方法ではなく、ログを確認する方法にしてください。
たとえば、Heroku環境であれば以下のようなコマンドでログを確認できます。
# Heroku上のログを確認する
$ heroku logs
Herokuでログを確認する場合の詳しい情報は公式ドキュメントを参照してください。
もしくは、Papertrailのようなアドオンを入れて、ブラウザ上でログを確認することもできます。以下はPapertrailアドオンを使った場合のログの表示例です。
Heroku以外の環境(AWS等)でもログを確認する方法は必ずあるはずです。
一般的な方法としてはsshなどを使って本番サーバーに接続し、tail
コマンド等でログを確認する方法があります。
# sshでサーバーに接続し、tailコマンドでログを確認する
$ cd /path/to/your/rails/application
$ tail -f log/production.log
ログを見るとエラーが発生したタイミングでバックトレース(スタックトレース)が出力されているはずです。この情報を元にデバッグを進めてください。
以下はエラー発生時のログの出力例です。
Started GET "/" for 124.45.27.199 at 2020-09-15 21:55:25 +0000
Processing by WelcomeController#index as HTML
Rendering welcome/index.html.erb within layouts/application
Rendered welcome/index.html.erb within layouts/application (Duration: 224.8ms | Allocations: 115496)
Completed 500 Internal Server Error in 232ms (Allocations: 116781)
ActionView::Template::Error (undefined local variable or method `secret_mesage' for #<#<Class:0x0000557c00d75308>:0x0000557c00d6f4f8>
Did you mean? secret_message):
1: <h1>Welcome!</h1>
2: <p><%= build_message("3566 0020 2036 0505") %></p>
app/helpers/welcome_helper.rb:4:in `build_message'
app/views/welcome/index.html.erb:2
「英語ばっかりでわけがわかんない!」「何から手を付けていいかさっぱりわからん!」という方は、僕が以前書いたこちらの記事を参考にしてみてください。
プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意(解説動画付き) - Qiita
エラートラッキングツールを導入する
他にもエラートラッキングツールを導入する方法も考えられます。
エラートラッキングツールを導入すると、エラー発生時に自動的にエラーに関する詳細情報がツールの専用サーバーに保存され、さらに開発者にメール等で通知されます。
通知メール等に記載されたリンクを開くと、バックトレースをはじめとした、エラーに関する詳細情報をブラウザ上で確認できます。
以下はBugsnagの画面表示例です。(画像の出典:https://www.bugsnag.com/product)
代表的なエラートラッキングツールには以下のようなものがあります。
ただしエラートラッキングツールは有料のものが多いため、個人で利用するのは若干敷居が高いかもしれません。ですので、まずは本番環境でログを確認する方法を習得するのがベストだと思います。
本番環境でローカル環境と同じエラー画面を表示させるリスクについて、もっと詳しく
まだ実際の業務に就いたことがないプログラミング初心者の方は、「ログを見るなんて面倒くさい!本番環境でローカル環境と同じエラー画面を表示させたって別にいいじゃん!」って思っているかもしれません。
しかし、ソースコードはオープンソースソフトウェアではない限り、開発者以外の目に触れさせるべきではありません。
場合によっては次のように見えてはいけない情報が表示されてしまう可能性があります。
また、Railsのエラー画面ではリンクを使ってスタックトレース上のコードを確認していくことができます。
このリンクを使うと、エラーが発生した行以外のコードも見えてしまいます。
以下の例は、Railsのエラー画面からindex.html.erb
のリンクをクリックした場合の表示例です。
さらに、"Framework Trace"や"Full Trace"といったリンクを開くと、gemのコードも含めたバックトレースが表示されます。このバックトレースを見ると、そのRailsアプリが利用しているgemのバージョンがわかります。セキュリティ上の脆弱性を抱えたバージョンを使っていた場合は、悪意のある第三者によってgemの脆弱性を突いた攻撃が行われるかもしれません。
たとえば、以下の表示例ではこのRailsアプリケーションがRails 6.0.3.3を使っていることがわかります。
本記事の執筆時点ではRails 6.0.3.3は最新バージョンですが、将来的にRailsに新たな脆弱性が発見されると、それがセキュリティ上のリスクになり得ます。
実際に動かしてリスクを確認してみる
上で見せた表示例は、筆者がHeroku上で公開しているデモアプリケーションです。
consider_all_requests_local
を true
に設定したので、みなさんのブラウザ上でもローカル環境と同じエラー画面が表示されます。
以下のURLを開き、ソースコードやRailsのバージョンが任意の第三者に公開されてしまうリスクを確認してみてください。
想定される反論「誰も見に来ないから大丈夫です」「すぐに戻すから大丈夫です」
しかし、プログラミング初心者の方の中には「なるほど」と思いつつ、「まだどこにも公表してないから、自分以外誰もアクセスしてこないはず」とか、「設定はすぐに戻すから大丈夫!」と考えている人がいるかもしれません。ですが、筆者はその考え自体がすでに危険だと考えます。
本番環境でローカル環境と同じエラー画面を表示させることは、技術上の問題だけでなく、 「習慣の問題」 という側面も大きいと思います。
すなわち、「いちいち本番サーバーに接続してログを見るなんて面倒くさい」とか、「何か困ったら本番環境でもローカル環境と同じエラー画面を表示させればいいや」と考えてしまう、その習慣に問題があるということです。
そういう習慣が身体に染みついていると、業務に入ったときも同じような方法でデバッグしたくなるかもしれません。
その誘惑に負けてしまったときに限って、「すぐに戻したはずなのに、悪意のあるユーザーがアクセスしてきてコードを盗み見された!」とか、「問題が解決したらホッとしてしまって、設定を戻し忘れた!」という大問題が発生するかもしれません。
ですから、常日頃から「本番環境では必ずログを見に行く。安易に production.rb
の設定を変えるのはダメ!!」と自分に言い聞かせておくのです。
そういう正しい習慣を身に付けておけば、本番環境のログを見に行くことにも心理的な抵抗がなくなるはずです。
えっ、「それでもエラー画面が見たい」ですって!?
それはちゃんと本番環境でログを見て解決できるスキルを身に付けた上での発言でしょうか?
ラクをしたいから、とか、本番環境でログを見る方法がわからないから、とか、そういう理由ではありませんか??
もしあなたが実務未経験の初心者プログラマだった場合、将来実務に就いたとき、本番環境で発生したエラーはどうやって確認するつもりですか?
新人Aさんは言いました。
🙋🏻♂️「production.rb
の consider_all_requests_local
を true
に変えたので、ちょっとデプロイさせてください」
新人Bさんは言いました。
🙋♀️「本番環境のログを確認したいので、ログの確認方法を教えてください」
さて、あなたから見て、どちらの新人が優秀な新人に見えますか?
(もしくは先輩に褒められる or 怒られる新人はどちらでしょうか?)
ログを読み解くスキルは遅かれ早かれ必要になるスキルなんですから、変にラクしようとせず、誰よりも早く習得しておきましょう!
一般論として:なぜデフォルトの設定値が違うのか?その理由を考えよう
今回の consider_all_requests_local
に限らず、Railsのデフォルト設定がローカル環境( development.rb
)と本番環境( production.rb
)で異なるケースは他にもあります。そして、そこには必ず何かしらの理由(Rails開発チームの意図)があるはずです。
その理由を考察せずに、「えー、この設定だと本番環境が不便!開発環境と同じにしたい〜!それっ」と安易に設定を変えてしまうのは非常に危険です。
本番環境も開発環境と同じ動きにしたい、でもなぜ設定が異なるのかはわからない、本番環境でその設定を変更したときのデメリットやリスクも理解していない、というプログラミング初心者さんは、設定を変える前に必ず上級者に質問・相談するようにしてください。
独学で勉強していて頼れる上級者が身近にいない、という場合はTeratailやQiitaの質問機能といった開発者向けQ&Aサイトで質問するようにしましょう。
参考:情報処理機構(IPA)の資料にも同じ話が載っています
情報処理機構(IPA)が提供している「安全なウェブサイトの作り方」という資料にも、ほぼ同じ話が載っています。
1-(iii)
エラーメッセージをそのままブラウザに表示しない。エラーメッセージの内容に、データベースの種類やエラーの原因、実行エラーを起こしたSQL文等の情報が含まれる場合、これらはSQLインジェクション攻撃につながる有用な情報となりえます。また、エラーメッセージは、攻撃の手がかりを与えるだけでなく、実際に攻撃された結果を表示する情報源として悪用される場合があります。データベースに関連するエラーメッセージは、利用者のブラウザ上に表示させないことをお勧めします。
https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_1.html
上記の資料では議論の対象がデータベースに限定されていますが、「エラーメッセージは、攻撃の手がかりを与える」という点はデータベースに限らず、Webアプリケーションの仕組み全般に適用できるはずです。
まとめ
というわけで、本記事ではRailsの本番環境でローカル環境と同じエラー画面を表示させることのリスクと、本番環境でエラーが発生した場合の適切な対処方法について説明しました。
冒頭にも述べたとおり、ネット上には(というかこのQiita上にも!)「 config/environments/production.rb
の config.consider_all_requests_local
を true
に変更してデプロイすればOK」と説明している記事はとても多いです。
「セキュリティ上のリスクがありますよ」とか「すぐに戻しましょう」と、ひとこと注意書きが添えてあるならまだしも、何の注意書きもなく「これでOKです。以上!」で終わっている記事もよく見かけます。
もしみなさんの周りでそういった記事を鵜呑みにしている初心者さんがいたら、ぜひ本記事のリンクと一緒に「その設定、危険だよ!」と教えてあげてください。