界: Code Quality

コードの質が低いと、予測できない動作につながります。ユーザーの視点には、それがしばしば使い勝手の悪さとなって現れます。攻撃者にとっては、予期せぬ方法でシステムにストレスを与える機会となります。

4 見つかった項目
脆弱性
Abstract
バイト配列を String に変換することで、データ損失につながる可能性があります。
Explanation
バイト配列からのデータが String に変換された場合、適用可能な文字セットに含まれないデータに何が起こるかは不明です。これによって、データが失われる可能性があります。また、適切なセキュリティ体制を保証するためにバイナリ データが必要な場合は、セキュリティ レベルの低下につながります。

例 1: 次のコードは、ハッシュを生成するためにデータを文字列に変換します。


...
FileInputStream fis = new FileInputStream(myFile);
byte[] byteArr = byte[BUFSIZE];
...
int count = fis.read(byteArr);
...
String fileString = new String(byteArr);
String fileSHA256Hex = DigestUtils.sha256Hex(fileString);
// use fileSHA256Hex to validate file
...


ファイルのサイズが BUFSIZE より小さいと仮定すると、myFile の情報がデフォルトの文字セットと同様にエンコードされている限り問題はありません。ただし、別のエンコーディングが使用されていたり、ファイルがバイナリ ファイルである場合は、情報を失います。その結果、生成された SHA ハッシュの信頼性が損なわれ、特にデフォルトの文字セットに含まれないデータが疑問符などの同じ値で表されている場合、衝突が発生しやすくなった可能性があることを意味します。
References
[1] STR03-J. Do not encode noncharacter data as a string CERT
[2] When 'EFBFBD' and Friends Come Knocking: Observations of Byte Array to String Conversions GDS Security
[3] Standards Mapping - Common Weakness Enumeration CWE ID 486
desc.semantic.java.code_correctness_byte_array_to_string_conversion
Abstract
NaN との比較は常にエラーです。
Explanation
NaN と比較した場合、常に false と評価されます。例外は != 演算子で、NaN は非順序型であるため常に true と評価されます。

例 1: 次では、変数が NaN ではないことを確認しています。


...
if (result == Double.NaN){
//something went wrong
throw new RuntimeException("Something went wrong, NaN found");
}
...


ここでは、resultNaN ではないことの検証を試みています。しかし、NaN に演算子 == を使用すると、常に結果は false の値になり、このチェックで例外がスローされることはありません。
References
[1] NUM07-J. Do not attempt comparisons with NaN CERT
[2] Java Language Specification Chapter 4. Types, Values, and Variables Oracle
[3] INJECT-9: Prevent injection of exceptional floating point values Oracle
[4] Standards Mapping - Common Weakness Enumeration CWE ID 486
desc.structural.java.code_correctness_comparison_with_nan
Abstract
クラス名を基準にしてオブジェクトのタイプを決定すると、予期しない動作が引き起こされたり、攻撃者が悪意あるクラスを挿入できたりする場合があります。
Explanation
攻撃者は、プログラムで悪意あるコードが実行されるようにするために、クラス名を故意に複製する可能性があります。この理由から、クラス名はタイプを表す識別子として適切ではないため、特定のオブジェクトを信頼する基準として使用しないでください。

例 1: 次のコードは、そのクラス名を基準にして inputReader オブジェクトからの入力を信頼するかどうかを決定しています。攻撃者が悪意あるコマンドを実行する inputReader の実装を提供できる場合、このコードはオブジェクトが無害であるか悪意あるものであるかを区別することができなくなります。


if (inputReader.GetType().FullName == "CompanyX.Transaction.Monetary")
{
processTransaction(inputReader);
}
References
[1] Standards Mapping - Common Weakness Enumeration CWE ID 486
desc.dataflow.dotnet.code_correctness_erroneous_class_compare
Abstract
クラス名を基準にしてオブジェクトのタイプを決定すると、予期しない動作が引き起こされたり、攻撃者が悪意あるクラスを挿入できたりする場合があります。
Explanation
攻撃者は、プログラムで悪意あるコードが実行されるようにするために、クラス名を故意に複製する可能性があります。この理由から、クラス名はタイプを表す識別子として適切ではないため、特定のオブジェクトを信頼する基準として使用しないでください。

例 1: 次のコードは、そのクラス名を基準にして inputReader オブジェクトからの入力を信頼するかどうかを決定しています。攻撃者が悪意あるコマンドを実行する inputReader の実装を提供できる場合、このコードはオブジェクトが無害であるか悪意あるものであるかを区別することができなくなります。


if (inputReader.getClass().getName().equals("com.example.TrustedClass")) {
input = inputReader.getInput();
...
}
References
[1] OBJ09-J. Compare classes and not class names CERT
[2] Standards Mapping - Common Weakness Enumeration CWE ID 486
desc.dataflow.java.code_correctness_erroneous_class_compare
Abstract
クラス名を基準にしてオブジェクトのタイプを特定することが原因で、予期せぬ動作が発生したり、攻撃者が悪意あるクラスを挿入できるようになる可能性があります。
Explanation
攻撃者は、プログラムで悪意あるコードが実行されるようにするために、クラス名を故意に複製する可能性があります。この理由から、クラス名はタイプを表す識別子として適切ではないため、特定のオブジェクトを信頼する基準として使用しないでください。

例 1: 次のコードは、そのクラス名を基準にして inputReader オブジェクトからの入力を信頼するかどうかを決定しています。攻撃者が悪意あるコマンドを実行する inputReader の実装を提供できる場合、このコードはオブジェクトが無害であるか悪意あるものであるかを区別することができなくなります。


if (inputReader::class.qualifiedName == "com.example.TrustedClass") {
input = inputReader.getInput()
...
}
References
[1] OBJ09-J. Compare classes and not class names CERT
[2] Standards Mapping - Common Weakness Enumeration CWE ID 486
desc.dataflow.kotlin.code_correctness_erroneous_class_compare
Abstract
静的メソッドはオーバーライドできませんが、インスタンス メソッドとして呼び出されると非表示に見える場合があります。
Explanation
静的メソッドは、クラスのインスタンスではなくクラスに属しているため、定義で上書きすることはできません。静的メソッドがサブクラスでオーバーライドされているように見える場合もあるため、混乱を引き起こし、誤ったバージョンのメソッドがコールされることもあります。

例 1: 次の例では、ユーザーを認証する API を定義しています。


class AccessLevel{
public static final int ROOT = 0;
//...
public static final int NONE = 9;
}
//...
class User {
private static int access;
public User(){
access = AccessLevel.ROOT;
}
public static int getAccessLevel(){
return access;
}
//...
}
class RegularUser extends User {
private static int access;
public RegularUser(){
access = AccessLevel.NONE;
}
public static int getAccessLevel(){
return access;
}
public static void escalatePrivilege(){
access = AccessLevel.ROOT;
}
//...
}
//...
class SecureArea {
//...
public static void doRestrictedOperation(User user){
if (user instanceof RegularUser){
if (user.getAccessLevel() == AccessLevel.ROOT){
System.out.println("doing a privileged operation");
}else{
throw new RuntimeException();
}
}
}
}


このコードは、一見問題がないように見えます。しかし、メソッド getAccessLevel() をインスタンス user にコールしているのであって、クラス UserRegularUser にコールしているのではないため、この場合は常に true が返され、instanceof が使用される場合でも、if/else ブロックのこの部分に到達するために操作が制限されます。
References
[1] MET07-J. Never declare a class method that hides a method declared in a superclass or superinterface CERT
[2] Java Language Specification Chapter 8. Classes Oracle
[3] Standards Mapping - Common Weakness Enumeration CWE ID 486
desc.structural.java.code_correctness_hidden_method