はじめに
Javaは比較的セキュアなプログラミングである。Javaは文法上のルールが多く、従わなければコンパイルすら通さないため、そういった意味では開発経験が少ない人でも一定の品質を担保することができる。しかしこれはJavaのシステムが必ず安全であると保障したものではない。Javaには粒度の細かなセキュリティ機構が備わっており、取扱いに注意すべきリソースに対するアクセスを制御することができるが、システムの安全性が侵害されることは稀ではない。そこで今回は「Java コーディングスタンダード CERT/Oracle版」を基にいくつかの問題のあるJavaコードとその改善案を中心に紹介していく。また、今回紹介する問題点のはJavaだけに限らないため、自分が普段使用している言語と見比べるとよい。
1,文字列は検査する前に標準化する(IDS01-J)
第三者から値を受けとる場合、入力された値が妥当であるかの検査は必須である。例えばクロスサイトスクリプティング(XSS)はスクリプトを値として与え、攻撃を行う手法であるが、回避手段の方法として<や>の挿入を検査する方法がある。以下はその例である。
このような実装はあまりよろしくない。なぜなら入力値に”\uFE64″や”\uFE65″とあった場合、<>を検知できない可能性があるからだ。そのため、検査を行う前に入力値の標準化が必要になってくる。JavaではNormalizerクラスのstaticメソッドであるnormalizeメソッドで正規化を行っていく。以下は上記コードを改良し、文字を検査する前に標準化を行う例である。(今回は第三者からの入力を表現するためにコンソールオブジェクトを明示しているが実際に標準化を確認するにはソースコードで直接”\uFE64″や”\uFE65″を格納するのが望ましい)
入力値の検査を標準化前に行ってしまうと不正な値が実行されてしまう可能性があるため、上記のように標準化後に検査を行うことがセキュアなシステム開発に繋がる。
2,データメンバはprivate宣言し、それにアクセスするためのラッパーメソッドを提供する(OBJ01-J)
publicなど公開範囲が広いデータメンバ(以下メンバ変数)を定義してしまうとアクセスを制御することが困難となり、攻撃者に想定外の方法で操作される可能性がある。このようなことを考慮し、メンバ変数はprivateで宣言し、制御の手段としてgetterやsetterといったアクセサメソッドを定義するべきである。以下は問題のあるコードの例である。
このようなメンバ変数の定義はやめるべきである。以下のようにprivateで宣言しアクセサメソッドを用意するのが好ましい
このように実装すればアクセサメソッドを介さなければメンバ変数を変更できないのでセキュアなコーディングとなる。また、このような実装をカプセル化という。なお、今回は省略しているがsetterメソッドでは入力された値の無害化を行うべきである。
3,privateかつ可変なクラスメンバへの参照を返す前にそのディフェンシブコピーを作成する(OBJ05-J)
可変なメンバへの参照を返すと悪意ある攻撃者にシステムが侵害されてしまう恐れがある。そのため、可変オブジェクトを返す場合にはディープコピーを作成して返すべきである。これをディフェンシブコピーという。以下に問題のあるコードと改善案を記載する。
上記は不適切なコード例である。この場合、メンバの参照をそのまま返してしまうため攻撃者に変更される恐れがあるためディフェンシブコピーを作成して返すべきである。以下に改善案を記載する。
このように実装することで可変メンバの持つデータを保護することができる。
おわりに
今回はJavaのセキュアなコーディングについて紹介していきました。実際にシステムやアプリケーションを作成する際には、今回紹介した例以上にセキュリティを意識しなければなりません。利用者が安心して利用できるようにするためにはデータを適切に保護し、管理していかなければならないでしょう。