JavaでSecurityManagerが削除へ:開発者への影響
2023年6月15日
0 分で読めますJava Development Kit(JDK)ライブラリのjava.securityパッケージは最も重要なパッケージの1つですが、継続的にアップデートされているにもかかわらず、ほとんど活用されていません。
ゼロトラストを含むサイバーセキュリティフレームワークの重要性が高まる中、Java開発者はJava SEのセキュリティライブラリについて理解しておく必要があります。
情報技術の他の分野と同様に、サイバーセキュリティは移り変わりが激しい分野です。サイバー犯罪の最新動向に対応し続ける必要があるからです。Javaのセキュリティが進化した(あるいは、誰に聞くかによっては後退した)一例が、SecurityManagerの削除です。なぜ重要なのでしょうか。本当に懸念すべきことなのでしょうか。この記事では、こうした疑問にお答えします。
Javaセキュリティの必要性
Javaは、Java仮想マシン(JVM)、メモリ管理、強力な例外処理、明示的なポインター(*つまり*参照)がないことなどの機能により、本質的に安全な言語と考えられています。こうした組み込み機能があるにもかかわらず、Log4jのような大きく報道された脆弱性があるため、Javaの利用をためらう人もいるかもしれません。
ただし、こうした脆弱性の多くは、製品やサードパーティ、あるいは開発者に起因するもの(*つまり*開発者が安全なコードを書いていないこと)である点に注意が必要です。Javaには、機密データの漏えいやデシリアライゼーションの問題など、JDK固有の脆弱性もありましたが、その多くはすでに対処済みか、現在対応が進められています。たとえば、JDK 20では、特定のプラットフォーム向けに暗号化処理の組み込み関数が最適化されました。
もちろん、Javaがセキュリティをすべて自動的に処理してくれるという意味ではありません。安全なコードを書くのは、依然として開発者の役割です。そのためには、Javaに組み込まれたセキュリティパッケージの機能と制限を理解する必要があります。また、多くの場合、サードパーティ製のセキュリティAPIやライブラリを導入する必要はありません。
JDKには、プラットフォームセキュリティ、暗号化、アクセス制御と認証、通信、公開鍵基盤の機能が含まれています。これらの機能については、公式のSecurity Developer's Guideで確認できます。
プラットフォームやツールを改善するには、安全でない機能や不要な機能を削除することも必要です。その一つがSecurityManagerです。
SecurityManagerとは
Java SecurityManagerは、アプリケーションセキュリティを統括する権限機構として設計されました。Javaアプリケーションで特定の操作が安全に実行されるよう、セキュリティポリシーやルールを指定するために使われます(または使われていました)。Java.lang.SecurityManagerクラスは、JDK 1.0から存在しています。
当初、SecurityManagerは抽象クラスでしたが、後にアクセスや利用を容易にするため、非抽象クラスになりました。新しいSecurityManagerオブジェクトをクラスごとにインスタンス化することもできましたが、これはJavaのベストプラクティスに沿った方法ではありませんでした。代わりに、アプリケーションの実行時にシステムプロパティを設定する必要がありました。
あるいは、SecurityManagerオブジェクトを直接インスタンス化し、コードからシステムプロパティとして設定する方法もあります。
その後、別のクラスで次のようにして、システムのSecurityManagerを取得できます。
SecurityManagerクラスは、主に一連のチェックメソッドで構成されています。これらのメソッドは、特定のアプリケーション操作が特定のリソースにアクセスできるかどうかを検証します。特に有用なのがcheckPermissionメソッドで、操作によるリソースの使用が許可されているかを確認します。権限ポリシーは、java.security.Permissionクラスの(直接および継承された)サブクラスを使って定義・制御します。
つまり、SecurityManagerはJavaアプリケーションのセキュリティを簡素化し、使いやすくするものです。では、なぜ非推奨になったのでしょうか。
SecurityManagerの歴史
Java SEの新しいリリースには、数多くの改善が盛り込まれています。その多くは、Java Community Process(JCP)やJDK Enhancement Proposalによるものです。SecurityManagerが削除予定として非推奨になった主な理由は、後者にあります。
SecurityManagerクラスは、初期には主にアプレットのセキュリティポリシーを定めるために実装されていましたが、現在ほとんどのWebプラットフォームはアプレットをサポートしていません。各アプレットのSecurityManagerは、Webブラウザーによって提供されていました。
Java Applet APIは、JEP289に従い、2017年のJava 9リリースで非推奨になりました。ただし、この時点では削除予定ではありませんでした。Java SE 17のリリースでは、JEP398で定められたとおり、Applet APIと関連クラスも削除予定として非推奨になりました。
これは、プログラミング言語とAPIを前進させるための取り組みでした。こうしたレガシーAPIは、今後のJDKリリースで不要となるほど重要性を失っています。SecurityManagerはアプレットのアーキテクチャと密接に結び付いているため、同様の理由で削除されることになりました。
なぜSecurityManagerは削除されるのか
現在、SecurityManagerはクライアント側とサーバー側のJavaコードを保護するうえで、最も効率的な方法ではありません。実際、Javaプラットフォームには、これらのタスクに使えるより効果的な仕組みがあります。
JDKの新しいリリースは、それぞれ前のリリースより本質的に安全であることを目指しています。そのため、SecurityManagerが対処しようとしていた偶発的な脆弱性の多くは、もはや問題ではありません。
SecurityManagerの機能はこの10年間見直されていないため、プログラマーが現在直面している一般的な脅威、弱点、脆弱性の多くに対処できません。実際、今日のセキュリティ問題に対応することを目的に設計されたものではありません。それでも存在する以上、関連するすべてのクラスとともにテストする必要があります。
その結果、不要な保守負担が生じます。SecurityManagerを維持するための時間と労力は、開発者にとって今より重要なクラスや機能のテストに充てられます。SecurityManagerのアーキテクチャは直感的に使えることが大きな利点の一つですが、設定は難しい場合があります。
おさらいすると、SecurityManagerは主に一連のチェックメソッドで構成されています。たとえば、有用なセキュリティ機能のCheckPermissionを使うと、Javaポリシーファイルを通じてアプリケーションやシステムの権限を検証できます。アプリケーションのコードにディレクトリへの読み取りアクセスを許可するポリシーは、次のようになります。
この設定では、file:/home/source/にあるコードベース(すべてのクラスファイル)に、相対パスのfilesディレクトリ内のすべてのリソースへのアクセスを許可します。特定の権限を確認するには、`Permission`クラスまたは`BasicPermission`クラスのサブクラスをインスタンス化する必要があります。これは最終的に、ポリシーファイルで設定された権限を表します。この場合はFilePermissionオブジェクトです。
ポリシーは多くの場合、状況に応じて適用されます。そのため、実行中のコードの現在のアクセス制御コンテキストを表すオブジェクトを取得する必要があります。次にSecurityManagerを使って、コンテキストに対する権限を確認できます。
ファイル読み取りポリシーで、コードベースからリソースファイルへの読み取りアクセスが許可されていない場合、このコードはSecurityException(`AccessControlException`)をスローします。
この手順は単純に見えますが、いくつかの欠点があります。細かく調整した権限やポリシーを設定するのは困難です。たとえば、ポリシーでアクセスが禁止されているディレクトリ内の特定のファイルについて、特定の操作やクラスに書き込みアクセスを許可したい場合はどうすればよいでしょうか。
回避策として、ポリシー、ソース、リソースの各ファイル用にディレクトリを分ける方法があります。ただし、各ポリシーと権限の関係を慎重に照合し、追跡する必要があります。細かなポリシーや権限を用いてアプリケーションを構築する場合、これは非常に非効率です。
同様に、SecurityManagerを使うライブラリやAPIの開発も容易ではありません。SecurityManagerのアーキテクチャを使うライブラリでは、ライブラリと同じ権限の一部を自分のコードにも付与する必要があります。つまり、ライブラリのドキュメントを調べ、どの権限が付与されるのか確認しなければなりません。詳細なドキュメントがあればの話ですが。
あるいは、ライブラリ依存関係のファイルにすべての権限を付与することもできます。しかし、これは現代のゼロトラストセキュリティの原則(*つまり*最小権限)に反します。
アプリケーションには、移植性を確保するために、機能性と安全性を備え、リソースを効率よく利用できることが求められます。SecurityManagerは多くのリソースを消費します。特に、複雑な権限やポリシーの構造を持つアプリケーションを利用・開発する場合は顕著です。実際、JVMではSecurityManagerは常にデフォルトで無効になっています。
SecurityManagerは機能的に時代遅れなだけでなく、アーキテクチャ上も役割を終えています。つまり、直接置き換えるものはありません。では、これからどうすればよいのでしょうか。
SecurityManagerの今後
この記事の執筆時点では、JDK 20が一般提供されたばかりで、SecurityManagerのアーキテクチャの多くはまだ残っています。SecurityManagerの削除が提案されてから、すでに3つのエディションがリリースされています。多くのJavaクラスやライブラリと関係しているため、Java API全体を壊さずに削除するのは困難です。Jakarta EEにも、SecurityManagerのアーキテクチャに依存する機能が数多くあります。
つまり、完全に削除するのは容易ではありません。そのため、安全な段階を踏んで進める計画であり、JDK 17でSecurityManagerを削除予定として指定したことから、すでに開始されています。また、JVMはSecurityManagerが動的に作成または許可された場合に警告を表示します。
JDK 18では、SecurityManagerはデフォルトで無効になりました。開発者がSecurityManagerを作成して設定しようとすると(setSecurityManger)、UnsupportedOperationExceptionエラーがスローされます。SecurityManagerを使うレガシーコードをサポートする機能はまだ残っていますが、このアーキテクチャを使い続けている場合は、移行を検討する必要があります。
JDKには、SecurityManagerの多くの機能に代わる手段があります。たとえば、ネイティブコードへのアクセス権を制御したい場合は、JDK 20で導入されたForeign Function and Memory API(FFM)を利用できます。
SecurityManagerに代わる方法
Java Security Managerからコードを切り離すには、まず、どこで使用されているかを特定します。JDKの最新の長期サポート(LTS)リリース(17および21)でも利用できますが、すでに非推奨です。JDK 11や8など以前のLTSバージョンからアップグレードする組織は、この点を把握しておく必要があります。非推奨のコードは十分に保守されず、セキュリティリスクとなる可能性があるため、最終的には削除するか更新する必要があります。jdeprscanを使えば、クラスファイル内の非推奨コードをすべて検出できます。
すぐに使えるSecurityManagerの唯一の真の代替手段は、JDKのSecurity Libraryです。また、開発者はOracleのJava SE向けSecure Coding Guidelinesを理解しておく必要があります。結局のところ、プロジェクトの脆弱性を最小限に抑える第一歩は、最初から安全なコードを書くことです。
非推奨APIのスキャンは重要ですが、スキャンすべきセキュリティ上の弱点はそれだけではありません。プロジェクトの依存関係やライブラリも確認することが重要です。開発者は、アプリケーションがアクセスするリソースを監視し、ログに記録するために、SecurityManagerをよく使用します。この目的には、Snykのようなサードパーティ製ソリューションを利用できます。
Snykは開発環境に統合できる開発者向けセキュリティツールで、コードやコンテナのセキュリティ分析を支援します。プロジェクトの設定ファイルやマニフェストをスキャンし、既知の脆弱性を含む直接・間接の依存関係を検出して警告します。Snykは設定ファイルにパッチを適用し、すべての依存関係を更新することで、脆弱性を自動的に修正します。さまざまな開発ツールやワークフローに統合できます。
Snykは、開発者がターミナルや継続的インテグレーション/継続的デリバリー(CI/CD)のワークフローで利用できるコマンドラインインターフェース(CLI)を提供しています。スタンドアロンのSnyk CLIツールをダウンロードしてインストールし、設定とアカウント認証を済ませれば、snyk testコマンドでローカルプロジェクトをテストできます。
さらに、snyk monitorコマンドを使えば、新たに発生する脆弱性を常に把握できます。このコマンドを実行すると、Snykがプロジェクトのスナップショットを取得し(Webアプリケーションで確認できます)、コンポーネントに関する新たな問題が明らかになった際にメールで通知します。
snyk testコマンドとsnyk monitorコマンドの簡単なデモをご覧ください。

とはいえ、Snykで脆弱性を検出できても、Javaの基本的なセキュアコーディングの原則を守ることは依然として重要です。「治療より予防」という古い格言のとおりです。健全なDevSecOpsの実践の一環として、CIツールと併せて開発にSnyk CLIを活用しましょう。
ここ数年、業界ではオープンソースの脆弱性が大幅に増加しています。攻撃を軽減・防止するために、開発者はプロジェクトやワークフローのセキュリティ状況を定期的に見直す必要があります。Snykのようなツールは定期的に更新され、CI/CDパイプラインに統合することで、品質とセキュリティのチェックを自動化できます。
SecurityManagerのサポート終了が迫るなか、似たアーキテクチャを持つ、あるいは類似のアプローチを採用するサードパーティ製のJavaライブラリやAPIを使いたくなるかもしれません。しかし、サードパーティ製ライブラリは常に最も重大なセキュリティリスクの一つです。この方法を選ぶ場合は、プロジェクトで安全な依存関係を使用できるよう、Snyk Open Sourceの利用を検討してください。
まとめ
結局のところ、SecurityManagerは時代遅れです。正式に削除されるまで、これほど長い時間がかかったのは驚きです。これは悪い知らせではありません。むしろ、Javaプラットフォームの安全性が大幅に高まり、不要な要素も減るでしょう。SecurityManagerは、サイバーセキュリティ全般に使うツールとして依存されることを想定したものではありませんでした。
プロジェクト全体のセキュリティ状況をより効果的に改善したいなら、Snykのようなツールの導入を検討してください。開発ワークフローにゼロトラストを組み込むのに役立ちます。さらに、IntelliJ用Snyk Code IDEプラグインを追加すれば、コードを保護し、品質をリアルタイムで確保できます。
Capture the Flagを始めよう
オンデマンドのバーチャル入門ワークショップを見て、Capture the Flagの課題の解き方を学びましょう。



