安全なJavaアプリケーションを開発するための13のルール

セキュリティは、ソフトウェア開発の最も複雑で、幅広く、重要な側面の1つです。ソフトウェアのセキュリティも見過ごされたり、開発サイクルの最後にわずかな調整を加えるだけで単純化しすぎたりすることがよくあります。結果は、2019年に30億件を超える公開されたレコードに達した主要なデータセキュリティ違反の年次リストで確認できます。それがキャピタルワンに起こる可能性がある場合、それはあなたに起こる可能性があります。

幸いなことに、Javaは、多くのセキュリティ機能が組み込まれた長年の開発プラットフォームです。Java Securityパッケージは徹底的な戦闘テストを受けており、新しいセキュリティの脆弱性のために頻繁に更新されています。2017年9月にリリースされた新しいJavaEE Security APIは、クラウドおよびマイクロサービスアーキテクチャの脆弱性に対処します。Javaエコシステムには、セキュリティ問題をプロファイリングおよびレポートするためのさまざまなツールも含まれています。 

しかし、堅固な開発プラットフォームがあっても、警戒を怠らないことが重要です。アプリケーション開発は複雑な作業であり、脆弱性はバックグラウンドノイズに隠れている可能性があります。クラスレベルの言語機能からAPIエンドポイントの承認まで、アプリケーション開発のすべての段階でセキュリティについて検討する必要があります。

次の基本ルールは、より安全なJavaアプリケーションを構築するための優れた基盤を提供します。

Javaセキュリティルール#1:クリーンで強力なJavaコードを書く

脆弱性は複雑さに隠れることが大好きなので、機能を犠牲にすることなくコードをできるだけシンプルに保ちます。DRY(自分で繰り返さないでください)のような実績のある設計原則を使用すると、問題を確認しやすいコードを作成するのに役立ちます。  

コードで公開する情報は常にできるだけ少なくしてください。実装の詳細を非表示にすると、保守性と安全性の両方を備えたコードがサポートされます。これらの3つのヒントは、安全なJavaコードの記述に大いに役立ちます。

  • Javaのアクセス修飾子をうまく利用してください。クラス、メソッド、およびそれらの属性に対してさまざまなアクセスレベルを宣言する方法を知ることは、コードを保護するのに大いに役立ちます。プライベートにすることができるものはすべてプライベートにする必要があります。 
  • 反省や内省を避けてください。このような高度な技術にメリットがある場合もありますが、ほとんどの場合、それらを避ける必要があります。リフレクションを使用すると、コードに弱点や不安定性をもたらす可能性のある強い型付けが排除されます。クラス名を文字列として比較するとエラーが発生しやすく、名前空間の衝突が発生しやすくなります。
  • 常に可能な限り最小のAPIおよびインターフェースサーフェスを定義します。コンポーネントを切り離し、可能な限り最小の領域で相互作用させます。アプリケーションの1つの領域が侵害に感染している場合でも、他の領域は安全です。 

Javaセキュリティルール#2:シリアル化を回避する

これは別のコーディングのヒントですが、独自のルールであることが十分に重要です。シリアル化はリモート入力を受け取り、それを完全に付与されたオブジェクトに変換します。コンストラクターとアクセス修飾子が不要になり、未知のデータのストリームがJVMで実行中のコードになることができます。その結果、Javaのシリアル化は深く本質的に安全ではありません。

Javaシリアル化の終了

聞いたことがない場合、OracleはJavaからシリアル化を削除する長期計画を立てています。OracleのJavaプラットフォームグループのチーフアーキテクトであるMarkReinholdは、Javaのすべての脆弱性の3分の1以上がシリアル化に関係していると考えていると述べています。

可能な限り、Javaコードでのシリアル化/逆シリアル化は避けてください。代わりに、JSONやYAMLなどのシリアル化形式の使用を検討してください。シリアル化ストリームを受信して​​動作する保護されていないネットワークエンドポイントを公開しないでください。これは騒乱のウェルカムマットに他なりません。

Javaセキュリティルール#3:暗号化されていない資格情報またはPIIを公開しないでください

信じがたいことですが、この避けられない間違いは年々痛みを引き起こします。

ユーザーがブラウザにパスワードを入力すると、パスワードはプレーンテキストとしてサーバーに送信されます。それが日の目を見る最後の時間になるはずです。パスワードをデータベースに永続化する前に、一方向暗号を介してパスワードを暗号化し、その値と比較するたびに再度暗号化する必要あります。

パスワードの規則は、クレジットカード、社会保障番号など、すべての個人情報(PII)に適用されます。アプリケーションに委託された個人情報は、最高レベルの注意を払って取り扱う必要があります。

データベース内の暗号化されていない資格情報またはPIIは、攻撃者が発見するのを待っている、ギャップのあるセキュリティホールです。同様に、生の資格情報をログに書き込んだり、ファイルやネットワークに送信したりしないでください。代わりに、パスワードのソルトハッシュを作成してください。必ず調査を行い、推奨されるハッシュアルゴリズムを使用してください。

ルール4にジャンプします。暗号化には常にライブラリを使用します。自分で転がさないでください。

Javaセキュリティルール#4:既知のテスト済みライブラリを使用する

独自のセキュリティアルゴリズムのローリングに関するこの質問と回答に注目してください。tl; drのレッスンは次のとおりです。可能な限り、既知の信頼できるライブラリとフレームワークを使用します。これは、パスワードのハッシュからRESTAPI認証までの範囲全体に適用されます。

幸いなことに、Javaとそのエコシステムはここに戻ってきます。アプリケーションセキュリティに関しては、SpringSecurityが事実上の標準です。幅広いオプションとあらゆるアプリアーキテクチャに適合する柔軟性を提供し、さまざまなセキュリティアプローチを組み込んでいます。

セキュリティに取り組む最初の本能は、調査を行うことです。ベストプラクティスを調査してから、どのライブラリがそれらのプラクティスを実装するかを調査します。たとえば、JSON Web Tokenを使用して認証と承認を管理する場合は、JWTをカプセル化するJavaライブラリを確認し、それをSpringSecurityに統合する方法を学びます。

信頼性の高いツールを使用しても、承認と認証を組み合わせるのはかなり簡単です。ゆっくりと移動し、すべてを再確認してください。

Javaセキュリティルール#5:外部入力について妄想する

ユーザーがフォーム、データストア、またはリモートAPIに入力する場合でも、外部入力を信頼しないでください。

SQLインジェクションとクロスサイトスクリプティング(XSS)は、外部入力の誤った処理から生じる可能性のある最も一般的に知られている攻撃です。あまり知られていない例(多くの例の1つ)は「10億笑い攻撃」です。これにより、XMLエンティティの拡張によりサービス拒否攻撃が発生する可能性があります。

入力を受け取ったときはいつでも、サニティチェックとサニタイズを行う必要があります。これは、処理のために別のツールまたはシステムに提示される可能性のあるものすべてに特に当てはまります。たとえば、OSコマンドラインの引数として何かが発生する可能性がある場合は、注意してください。

特別でよく知られているインスタンスはSQLインジェクションです。これについては、次のルールで説明します。

Javaセキュリティルール#6:SQLパラメータを処理するために常にプリペアドステートメントを使用する

SQLステートメントを作成するときはいつでも、実行可能コードのフラグメントを補間するリスクがあります。

これを知っていると、SQLを作成するために常にjava.sql.PreparedStatementクラスを使用することをお勧めします。MongoDBのようなNoSQLストアにも同様の機能があります。ORMレイヤーを使用している場合、実装は内部でPreparedStatementsを使用します。

Javaセキュリティルール#7:エラーメッセージで実装を明らかにしないでください

本番環境でのエラーメッセージは、攻撃者にとって有益な情報源になる可能性があります。特にスタックトレースは、使用しているテクノロジーとその使用方法に関する情報を明らかにすることができます。スタックトレースをエンドユーザーに公開しないでください。

ログイン失敗アラートもこのカテゴリに分類されます。エラーメッセージは、「ログインに失敗しました」と「そのユーザーが見つかりませんでした」または「パスワードが正しくありません」と表示されることが一般的に認められています。悪意のある可能性のあるユーザーには、できる限り支援を提供しないでください。

理想的には、エラーメッセージは、アプリケーションの基盤となるテクノロジースタックを明らかにするべきではありません。その情報をできるだけ不透明にしてください。

Javaセキュリティルール#8:セキュリティリリースを最新の状態に保つ

2019年の時点で、オラクルはJavaの新しいライセンススキームとリリーススケジュールを実装しています。開発者にとって残念なことに、新しいリリースのリズムは物事を容易にしません。それでも、セキュリティ更新プログラムを頻繁にチェックし、それらをJREおよびJDKに適用する責任はユーザーにあります。

Oracleホームページでセキュリティアラートを定期的にチェックして、利用可能な重要なパッチを確認してください。オラクルは四半期ごとに、Javaの現在のLTS(長期サポート)リリースの自動パッチ更新を提供しています。問題は、そのパッチは、Javaサポートライセンスを購入している場合にのみ利用できるということです。

組織がそのようなライセンスの料金を支払っている場合は、自動更新ルートに従ってください。そうでない場合は、おそらくOpenJDKを使用しているので、自分でパッチを適用する必要があります。この場合、バイナリパッチを適用するか、既存のOpenJDKインストールを最新バージョンに置き換えることができます。または、AzulのZuluEnterpriseなどの市販のOpenJDKを使用することもできます。

すべてのセキュリティパッチが必要ですか?

セキュリティアラートを注意深く観察すると、特定の更新セットは必要ない場合があります。例えば、2020年1月リリースが表示され、重要なのJavaアップデートします。ただし、よく読んでみると、この更新プログラムはJavaアプレットのセキュリティの穴にのみパッチを適用し、Javaサーバーには影響を与えないことがわかります。

Javaセキュリティルール#9:依存関係の脆弱性を探す

コードベースと依存関係を自動的にスキャンして脆弱性を見つけるために利用できるツールはたくさんあります。あなたがしなければならないのはそれらを使うことだけです。

オープンWebアプリケーションセキュリティプロジェクトであるOWASPは、コードセキュリティの向上に取り組んでいる組織です。OWASPの信頼できる高品質の自動コードスキャンツールのリストには、いくつかのJava指向のツールが含まれています。

コードベースを定期的にチェックしますが、サードパーティの依存関係にも注意してください。攻撃者は、オープンソースライブラリとクローズドソースライブラリの両方を標的にします。依存関係の更新を監視し、新しいセキュリティ修正がリリースされたらシステムを更新します。

Javaセキュリティルール#10:ユーザーアクティビティを監視およびログに記録する

アプリケーションを積極的に監視していなければ、単純なブルートフォース攻撃でも成功する可能性があります。監視ツールとログツールを使用して、アプリの状態を監視します。

監視が重要である理由を確信したい場合は、アプリケーションのリスニングポートでTCPパケットを座って監視するだけです。単純なユーザー操作をはるかに超えて、あらゆる種類のアクティビティが表示されます。その活動の一部は、脆弱性をスキャンするボットや悪者です。

ログイン試行の失敗をログに記録して監視し、リモートクライアントが免責で攻撃するのを防ぐための対策を講じる必要があります。

監視は原因不明のスパイクを警告することができ、ログは攻撃後に何が悪かったのかを解明するのに役立ちます。Javaエコシステムには、ロギングとモニタリングのための商用およびオープンソースソリューションが豊富に含まれています。

Javaセキュリティルール#11:サービス拒否(DoS)攻撃に注意してください

潜在的に高価なリソースを処理しているとき、または潜在的に高価な操作を実行しているときはいつでも、リソースの暴走を防ぐ必要があります。

オラクルは、「サービス拒否」の見出しの下にあるJava SEドキュメントのセキュアコーディングガイドラインで、このタイプの問題の潜在的なベクトルのリストを維持しています。

基本的に、圧縮ファイルの解凍など、コストのかかる操作を実行する場合は常に、リソース使用量の急増を監視する必要があります。ファイルマニフェストを信頼しないでください。実際のディスク上またはメモリ内の消費のみを信頼し、それを監視し、サーバーをひざまずく過剰な状態から保護します。

同様に、一部の処理では、予期しない永久ループに注意することが重要です。ループが疑わしい場合は、ループが進行していることを確認するガードを追加し、ゾンビになっているように見える場合はループを短絡します。

Javaセキュリティルール#12:Javaセキュリティマネージャの使用を検討してください

Javaには、実行中のプロセスがアクセスできるリソースを制限するために使用できるセキュリティマネージャがあります。ディスク、メモリ、ネットワーク、およびJVMアクセスに関してプログラムを分離できます。アプリのこれらの要件を絞り込むと、攻撃による危害の可能性のフットプリントが減少します。このような分離も不便な場合がありSecurityManagerます。そのため、デフォルトでは有効になっていません。

SecurityManagerの強い意見を回避することが、アプリケーションの保護をさらに強化する価値があるかどうかを自分で判断する必要があります。Java Security Managerの構文と機能の詳細については、Oracleのドキュメントを参照してください。

Javaセキュリティルール#13:外部クラウド認証サービスの使用を検討してください

一部のアプリケーションは、単にユーザーデータを所有する必要があります。残りの部分については、クラウドサービスプロバイダーが理にかなっている可能性があります。

周りを検索すると、さまざまなクラウド認証プロバイダーが見つかります。このようなサービスの利点は、プロバイダーがあなたではなく機密ユーザーデータを保護する責任があることです。一方、認証サービスを追加すると、エンタープライズアーキテクチャが複雑になります。FireBase認証などの一部のソリューションには、スタック全体で統合するためのSDKが含まれています。

結論

より安全なJavaアプリケーションを開発するための13のルールを紹介しました。これらのルールは実証済みですが、すべての最大のルールはこれです:疑ってください。常に警戒心とセキュリティ志向の見通しを持ってソフトウェア開発に取り組みます。コードの脆弱性を探し、JavaセキュリティAPIとパッケージを利用し、サードパーティのツールを使用してコードのセキュリティ問題を監視およびログに記録します。

絶えず変化するJavaセキュリティランドスケープに遅れないようにするための3つの優れた高レベルのリソースを次に示します。

  • OWASPトップ10
  • CWEトップ25
  • オラクルのセキュアコードガイドライン

このストーリー「安全なJavaアプリケーションを開発するための13のルール」は、もともとJavaWorldによって公開されました。