静的コード解析ツールはバグや脆弱性をどの程度検出できるのでしょうか?
前回、FindBugsでバグだらけのWebアプリケーションを解析してみましたが、今回はSonarQubeで解析してみました。
検証内容
バグだらけのWebアプリケーション(EasyBuggy 1.3.3)が、現在実装済みのバグや脆弱性は以下の80種類です。
障害
- デッドロック (Java)
- デッドロック (SQL)
- 完了しないプロセスの待機
- 無限ループ
- メモリリーク (Javaヒープ領域)
- メモリリーク (Permanent領域)
- メモリリーク (Cヒープ領域)
- ネットワークソケットリーク
- データベースコネクションリーク
- ファイルディスクリプタリーク
- スレッドリーク
- 文字化け
- 整数オーバーフロー
- 丸め誤差
- 打ち切り誤差
- 情報落ち
脆弱性
- XSS (クロスサイトスクリプティング)
- SQLインジェクション
- LDAPインジェクション
- コードインジェクション
- OSコマンドインジェクション
- メールヘッダーインジェクション
- Nullバイトインジェクション
- サイズ制限の無いファイルアップロード
- 拡張子制限の無いファイルアップロード
- オープンリダイレクト可能なログイン画面
- ブルートフォース攻撃可能なログイン画面
- セッション固定攻撃可能なログイン画面
- 親切過ぎる認証エラーメッセージ
- 危険なファイルインクルード
- パストラバーサル
- 意図しないファイル公開
- CSRF (クロスサイトリクエストフォージェリ)
- クリックジャッキング
- XEE (XMLエンティティ拡張)
- XXE (XML外部エンティティ)
- 正規表現解析による遅延
- プラス演算子による文字列結合の遅延
- 不必要なオブジェクト生成による遅延
エラー
- AssertionError
- ExceptionInInitializerError
- FactoryConfigurationError
- GenericSignatureFormatError
- NoClassDefFoundError
- OutOfMemoryError (Java heap space)
- OutOfMemoryError (Requested array size exceeds VM limit)
- OutOfMemoryError (unable to create new native thread)
- OutOfMemoryError (GC overhead limit exceeded)
- OutOfMemoryError (PermGen space)
- OutOfMemoryError (Direct buffer memory)
- StackOverflowError
- TransformerFactoryConfigurationError
- UnsatisfiedLinkError
例外
- ArithmeticException
- ArrayIndexOutOfBoundsException
- ArrayStoreException
- BufferOverflowException
- BufferUnderflowException
- CannotRedoException
- CannotUndoException
- ClassCastException
- ConcurrentModificationException
- EmptyStackException
- IllegalArgumentException
- IllegalMonitorStateException
- IllegalPathStateException
- IllegalStateException
- IllegalThreadStateException
- ImagingOpException
- IndexOutOfBoundsException
- InputMismatchException
- MalformedParameterizedTypeException
- MissingResourceException
- NegativeArraySizeException
- NoSuchElementException
- NullPointerException
- NumberFormatException
- SecurityException
- UnsupportedCharsetException
- UnsupportedOperationException
SonarQube 6.3はどの程度の問題を検出できるのでしょうか?
結果
結果は以下のようになりました(SonarQubeでの検証結果とともに、同じソースコードをFindBugsで検証した結果も載せています)。
バグ | SonarQube | FindBugs(分析力最大) |
---|---|---|
デッドロック (Java) | × | × |
デッドロック (SQL) | × | × |
完了しないプロセスの待機 | × | × |
無限ループ | ○ | × |
メモリリーク (Javaヒープ領域) | × | × |
メモリリーク (Permanent領域) | × | × |
メモリリーク (Cヒープ領域) | × | × |
ネットワークソケットリーク | × | × |
データベースコネクションリーク | ○ | ○ |
ファイルディスクリプタリーク | ○ | ○ |
スレッドリーク | × | × |
文字化け | × | × |
整数オーバーフロー | × | × |
丸め誤差 | × | × |
打ち切り誤差 | × | × |
情報落ち | × | × |
XSS (クロスサイトスクリプティング) | × | × |
SQLインジェクション | ○ | ○ |
LDAPインジェクション | × | × |
コードインジェクション | × | × |
OSコマンドインジェクション | × | × |
メールヘッダーインジェクション | × | × |
Nullバイトインジェクション | × | × |
サイズ制限の無いファイルアップロード | × | × |
拡張子制限の無いファイルアップロード | × | × |
オープンリダイレクト可能なログイン画面 | × | × |
ブルートフォース攻撃可能なログイン画面 | × | × |
セッション固定攻撃可能なログイン画面 | × | × |
親切過ぎる認証エラーメッセージ | × | × |
危険なファイルインクルード | × | × |
パストラバーサル | × | × |
意図しないファイル公開 | × | × |
CSRF (クロスサイトリクエストフォージェリ) | × | × |
クリックジャッキング | × | × |
XEE (XMLエンティティ拡張) | × | × |
XXE (XML外部エンティティ) | × | × |
正規表現解析による遅延 | × | × |
プラス演算子による文字列結合の遅延 | ○ | ○ |
不必要なオブジェクト生成による遅延 | × | × |
AssertionError | × | × |
ExceptionInInitializerError / NoClassDefFoundError | × | × |
FactoryConfigurationError | × | × |
GenericSignatureFormatError | × | × |
NoClassDefFoundError | × | × |
OutOfMemoryError (Java heap space) | × | × |
OutOfMemoryError (Requested array size exceeds VM limit) | × | × |
OutOfMemoryError (unable to create new native thread) | × | × |
OutOfMemoryError (GC overhead limit exceeded) | × | × |
OutOfMemoryError (PermGen space) | × | × |
OutOfMemoryError (Direct buffer memory) | × | × |
StackOverflowError | × | × |
TransformerFactoryConfigurationError | × | × |
UnsatisfiedLinkError | × | × |
ArithmeticException | ○ | × |
ArrayIndexOutOfBoundsException | × | × |
ArrayStoreException | × | × |
BufferOverflowException | × | × |
BufferUnderflowException | × | × |
CannotRedoException | × | × |
CannotUndoException | × | × |
ClassCastException | × | × |
ConcurrentModificationException | × | × |
EmptyStackException | × | × |
IllegalArgumentException | × | × |
IllegalMonitorStateException | × | × |
IllegalPathStateException | × | × |
IllegalStateException | × | × |
IllegalThreadStateException | × | × |
ImagingOpException | × | × |
IndexOutOfBoundsException | × | × |
InputMismatchException | × | × |
MalformedParameterizedTypeException | × | × |
MissingResourceException | × | × |
NegativeArraySizeException | × | × |
NoSuchElementException | × | × |
NullPointerException | × | ○ |
NumberFormatException | × | × |
SecurityException | × | × |
UnsupportedCharsetException | × | × |
UnsupportedOperationException | × | × |
結果は、80種類のバグに対して6種類しか検出できませんでした。見ての通り、若干の違いはありますが、検出能力はFindBugsと同程度という印象です。とはいえ、静的解析ツールを使うことで簡単なミスやコーディングのバッドパターンなどを把握できるので、とても有益なツールであることは確かです。ちなみにコードの行数約4,700行に対して、指摘の数は176件でした。
※ 注意:今回の検証で検出できたバグであっても、実装方法が異なれば検出できない可能性があります。逆に今回の検証で検出できなかったバグであっても、実装方法が異なれば検出できる可能性があります。100%の確率でNullPointerExceptionが発生するロジックでも、実装の仕方によってはNullPointerExceptionを検出できる場合も、できない場合もあります。
その他気付いたこと
SonarQubeを使っていて少し気になったのは、誤検知の多さです。
例えば、次のコードは未使用のメソッドがあることを指摘していますが、実際にこのメソッドは使用されており、削除すればコンパイルエラーになります。
次のコードはSQLインジェクションを指摘していますが、「asc
」または「desc
」以外の値はSQL文に付加されないので、問題はありません。
次のコードはNPEの可能性を指摘していますが、org.apache.commons.lang.StringUtils.isBlank(String)
でnullチェックしているため、変数fileName
がnullとなって、NPEになることはありえません(「might be thrown」なので、まあ、いいのかな...)。
備考
SonarQubeの使用方法などについては、簡単ですが、ブログにまとめていますので、そちらを参照下さい。