コードの質が低いと、予測できない動作につながります。ユーザーの視点には、それがしばしば使い勝手の悪さとなって現れます。攻撃者にとっては、予期せぬ方法でシステムにストレスを与える機会となります。
NaN
との比較は常にエラーです。NaN
と比較した場合、常に false
と評価されます。例外は !=
演算子で、NaN
は非順序型であるため常に true
と評価されます。NaN
ではないことを確認しています。
...
if (result == Double.NaN){
//something went wrong
throw new RuntimeException("Something went wrong, NaN found");
}
...
result
が NaN
ではないことの検証を試みています。しかし、NaN
に演算子 ==
を使用すると、常に結果は false
の値になり、このチェックで例外がスローされることはありません。this
参照にアクセスすることを許可する可能性があるため、脆弱性につながります。
...
class User {
private String username;
private boolean valid;
public User(String username, String password){
this.username = username;
this.valid = validateUser(username, password);
}
public boolean validateUser(String username, String password){
//validate user is real and can authenticate
...
}
public final boolean isValid(){
return valid;
}
}
validateUser
とクラスは final
ではないため、オーバーライドされる可能性があります。また、この関数をオーバーライドするサブクラスへの変数を初期化することで、validateUser
関数の回避を可能にしてしまいます。例:
...
class Attacker extends User{
public Attacker(String username, String password){
super(username, password);
}
public boolean validateUser(String username, String password){
return true;
}
}
...
class MainClass{
public static void main(String[] args){
User hacker = new Attacker("Evil", "Hacker");
if (hacker.isValid()){
System.out.println("Attack successful!");
}else{
System.out.println("Attack failed");
}
}
}
Example 1
のコードは "Attack successful!" を出力します。これは、Attacker
クラスがスーパークラス User
のコンストラクターからコールされる validateUser()
関数をオーバーライドすることで、Java は最初にコンストラクターからコールされた関数のサブクラスを見るためです。inputReader
オブジェクトからの入力を信頼するかどうかを決定しています。攻撃者が悪意あるコマンドを実行する inputReader
の実装を提供できる場合、このコードはオブジェクトが無害であるか悪意あるものであるかを区別することができなくなります。
if (inputReader.GetType().FullName == "CompanyX.Transaction.Monetary")
{
processTransaction(inputReader);
}
inputReader
オブジェクトからの入力を信頼するかどうかを決定しています。攻撃者が悪意あるコマンドを実行する inputReader
の実装を提供できる場合、このコードはオブジェクトが無害であるか悪意あるものであるかを区別することができなくなります。
if (inputReader.getClass().getName().equals("com.example.TrustedClass")) {
input = inputReader.getInput();
...
}
inputReader
オブジェクトからの入力を信頼するかどうかを決定しています。攻撃者が悪意あるコマンドを実行する inputReader
の実装を提供できる場合、このコードはオブジェクトが無害であるか悪意あるものであるかを区別することができなくなります。
if (inputReader::class.qualifiedName == "com.example.TrustedClass") {
input = inputReader.getInput()
...
}
x = NULL
および x != NULL
は常に false となります。NULL
の値は確定できません。どのような値とも等しくならず、別の NULL
値と同じになることもありません。また、null
値が他の値と同じになることはありません。例 2: 以下のステートメントは必ず false になります。
checkNull BOOLEAN := x = NULL;
checkNotNull BOOLEAN := x != NULL;
==
または !=
ではなく、equals()
メソッドと比較してください。==
または !=
を使用し、2 つのオブジェクトの値ではなく、オブジェクトが等価かどうかを比較します。このような場合、2 つの参照が等価でない可能性が高いのです。
if (args[0] == STRING_CONSTANT) {
logger.info("miracle");
}
==
および !=
演算子は、等価であるオブジェクトに含まれる文字列を比較するために使用されるときにのみ、予期されるとおりに動作します。これが発生する一般的な状況は、文字列が抑留され、String
クラスにより保持されるオブジェクトのプールにこの文字列が追加される場合です。文字列が一度抑留されると、同じオブジェクトと等価演算子を使用する文字列のすべての使用は、予期されるとおりに動作します。すべての文字列リテラルおよび文字列値の定数は、自動的に抑留されます。その他の文字列は String.intern()
を呼び出して手動で抑留できます。これにより、現在の文字列の正準な (規則に沿った) インスタンスが返されます (必要な場合には、正準なインスタンスが作成されます)。