SCW アイコン
ヒーロー背景(区切りなし)
ブログ

Javaの落とし穴-ビット演算子とブール演算子

アラン・リチャードソン
2021年02月07日 掲載
最終更新日: 2026年3月10日

Java ゴッチャ - ビット演算子とブール演算子

> "Java Gotcha" - 誤って実装してしまいがちなミスパターン。

Javaの簡単な失敗例として、ブール比較演算子の代わりにBitwise演算子を使用することがあります。

例えば、単純なミスタイプで、本当は「&&」と書きたかったのに「&」と書いてしまうことがあります。

コードをレビューするときに学ぶ一般的なヒューリスティックな考え方があります。

条件文の中で「&」や「|」が使われている場合は、おそらく意図したものではないでしょう。

このブログ記事では、ヒューリスティックを探り、このコーディング問題を特定して修正する方法を紹介します。


何が問題なのか?ビット演算はブーリアンでも問題なく使える


ビット演算子をブール値で使用することは完全に有効であり、Javaは構文エラーを報告しません。

JUnit Test でビットワイズ OR (|) とビットワイズ AND (&) の真理値表を調べると、ビットワイズ演算子の出力が真理値表と一致することがわかります。このように考えると、Bitwise 演算子の使用は問題ないと思われるかもしれません。

AND真理値表

aで3列、bで1列、最後に(a^b)で1列。


@Test
    void bitwiseOperatorsAndTruthTable(){
          Assertions.assertEquals(true, true & true);
          Assertions.assertEquals(false, true & false);
          Assertions.assertEquals(false, false & true);
          Assertions.assertEquals(false, false & false);
    }


テストはパス、これは完全に妥当なJavaです。


OR真理値表


aで3列、bで1列、最後に(a v b)で1列。


   @Test
    void bitwiseOperatorsOrTruthTable(){
        Assertions.assertEquals(true, true | true);
        Assertions.assertEquals(true, true | false);
        Assertions.assertEquals(true, false | true);
        Assertions.assertEquals(false, false | false);
    }


このテストもパスしていますが、なぜ「&&'」と「||'」を好むのでしょうか?


真理値表イメージは 真理値表ツールweb.standfor.edu.


問題点短絡動作


実際に問題となるのは、ビット演算子(&, |)とブール演算子(&&, ||)の動作の違いです。

ブール演算子は短絡的な演算子で、必要な分だけ評価します。

e.g.

if (args != null & args.length() > 23) {
    System.out.println(args);
}


上記のコードでは、ビットワイズ演算子が使用されているため、両方のブール条件が評価されます。

  • args != null
  • args.length() > 23

これでは、argsがnullの場合にNullPointerExceptionが発生する可能性があります。なぜなら、argsがnullの場合でも、args.lengthのチェックを常に行うからです。


ブール演算子 短絡評価


&&が使われている場合は、例えば

if (args != null && args.length() > 23) {
    System.out.println(args);
}


args != nullがfalseと評価されたことがわかると、すぐに条件式の評価を停止します。

右辺の評価は必要ありません。

右辺の条件の結果がどうであれ、ブール式の最終的な値は「偽」になります。


しかし、これはプロダクションコードではあり得ないことです。


これは非常に犯しやすいミスで、静的解析ツールでは必ずしも検出されません。

このパターンの公開例がないか、以下のGoogle Dorkを使って調べてみました。

filetype:java if "!=null & "
今回の検索では、AndroidのRootWindowContainerのコードが出てきました
isDocument = intent != null & intent.isDocument()


代入文の中でビットワイズ演算子を使って値をマスクすることはよくあるので、このようなコードはコードレビューを通過する可能性があります。しかし、この例では、上記のif文の例と同じ結果になります。intentがnullの場合は、NullPointerExceptionがスローされます。

このような構造になってしまうのは、防御的なコードを書いてしまい、冗長なコードを書いてしまうことが多いからです。!=nullのチェックは、ほとんどのユースケースでは余計なことかもしれません。

これはプログラマーがプロダクションコードで犯したエラーです。

検索結果がどれくらい最新かはわかりませんが、私が検索したときには、Google、Amazon、Apache...そして私のコードが戻ってきました。

私のオープンソースプロジェクトの一つで、最近行われたプルリクエストは、まさにこのエラーに対処するものでした。

if(type!=null & type.trim().length()>0){
    acceptMediaTypeDefinitionsList.add(type.trim());
}


これを見つけるには


私のサンプルコードをいくつかの静的アナライザーでチェックしたところ、どのアナライザーもこの隠れた自爆コードを拾いませんでした。

Secure Code Warrior のチームとして、これを拾うことができる、かなりシンプルなSensei のレシピを作成し、検討しました。

ビット演算子は完全に有効であり、代入にもよく使用されるため、問題のあるコードを見つけるために、if文の使用例とビット演算子の使用に焦点を当てました。

search:
  expression:
    anyOf:
    - in:
        condition: {}
    value:
      caseSensitive: false
      matches: ".* & .*"


これは、" & " が条件式として使われている場合に、正規表現を使ってマッチさせるものです。

この問題を解決するために、再び正規表現を利用しました。今回はQuickFixのsed関数を使って、式の中の&を&&にグローバルに置き換えました。

availableFixes:
  - name: "Replace bitwise AND operator to logical AND operator"
    actions:
      - rewrite:
          to: "{{#sed}}s/&/&&/g,{{{ . }}}{{/sed}}"


エンドノート

ここでは、ビットワイズ演算子の最も一般的な誤用、つまり、実際にはブール演算子を意図していた場合について説明します。

課題の例のように、このような状況は他にもありますが、レシピを書く際には、誤認識を避けるようにしなければなりません。私たちは、最も一般的な現象に合わせてレシピを作成します。Sensei が進化するにつれ、より多くの一致する条件をカバーするために、検索機能にさらなる特異性を追加する可能性があります。

現在の形では、このレシピは多くのライブユースケースを特定し、最も重要なのは、私のプロジェクトで報告されたものです。

注:この例題とレシピのレビューには、Charlie Eriksen、Matthieu Calie、Robin Claerhaut、Brysen Ackx、Nathan Desmet、Downey Robersscheutenなど、かなりの数のコードウォリアーが協力してくれました。ご協力ありがとうございました。


---


IntelliJの「Preferences ‾ Plugins」(Mac)または「Settings ‾ Plugins」(Windows)から、「sensei secure code」を検索して、「Sensei 」をインストールできます。

Secure Code Warrior GitHub アカウントの `sensei-blog-examples` リポジトリには、これらのブログ記事(今回の記事を含む)のソースコードやレシピが多数用意されています。

https://github.com/securecodewarrior/sensei-blog-examples

についてはこちらをご覧ください。Sensei


リソースを表示
リソースを表示

このブログ記事では、よくあるJavaコーディングの間違い(条件演算子の代わりにビット演算子の使用)、それによってコードが脆弱になるエラー、およびSenseiを使用して問題を修正および検出する方法について見ていきます。

もっと興味がありますか?

アラン・リチャードソンは20年以上にわたり、開発者として、テスターからテスト責任者まで、テスト階層のあらゆるレベルで経験を積んできました。Secure Code Warrior 、チームと直接連携して、高品質で安全なコードの開発を改善しています。アランは、「ディア・イーブル・テスター」と「Java フォー・テスター」を含む4冊の本の著者です。また、アランはテクニカル・ウェブ・テストと Java を使った Selenium WebDriver を学ぶのに役立つオンライン・トレーニング・コースも作成しています。アランは、SeleniumSimplified.com、EvilTester.com、JavaForTesters.com、CompendiumDev.co.uk にライティングとトレーニングのビデオを投稿しています。

もっと詳しく

Secure Code Warriorは、ソフトウェア開発ライフサイクル全体にわたってコードを保護し、サイバーセキュリティを最優先とする文化を築くお手伝いをします。アプリケーションセキュリティマネージャー、開発者、CISO、またはセキュリティ関係者であるかに関わらず、安全でないコードに関連するリスクを軽減するお手伝いをします。

デモを予約
シェア:
リンクトインのブランドソーシャルx ロゴ
著者
アラン・リチャードソン
2021年02月07日掲載

アラン・リチャードソンは20年以上にわたり、開発者として、テスターからテスト責任者まで、テスト階層のあらゆるレベルで経験を積んできました。Secure Code Warrior 、チームと直接連携して、高品質で安全なコードの開発を改善しています。アランは、「ディア・イーブル・テスター」と「Java フォー・テスター」を含む4冊の本の著者です。また、アランはテクニカル・ウェブ・テストと Java を使った Selenium WebDriver を学ぶのに役立つオンライン・トレーニング・コースも作成しています。アランは、SeleniumSimplified.com、EvilTester.com、JavaForTesters.com、CompendiumDev.co.uk にライティングとトレーニングのビデオを投稿しています。

シェア:
リンクトインのブランドソーシャルx ロゴ

Java ゴッチャ - ビット演算子とブール演算子

> "Java Gotcha" - 誤って実装してしまいがちなミスパターン。

Javaの簡単な失敗例として、ブール比較演算子の代わりにBitwise演算子を使用することがあります。

例えば、単純なミスタイプで、本当は「&&」と書きたかったのに「&」と書いてしまうことがあります。

コードをレビューするときに学ぶ一般的なヒューリスティックな考え方があります。

条件文の中で「&」や「|」が使われている場合は、おそらく意図したものではないでしょう。

このブログ記事では、ヒューリスティックを探り、このコーディング問題を特定して修正する方法を紹介します。


何が問題なのか?ビット演算はブーリアンでも問題なく使える


ビット演算子をブール値で使用することは完全に有効であり、Javaは構文エラーを報告しません。

JUnit Test でビットワイズ OR (|) とビットワイズ AND (&) の真理値表を調べると、ビットワイズ演算子の出力が真理値表と一致することがわかります。このように考えると、Bitwise 演算子の使用は問題ないと思われるかもしれません。

AND真理値表

aで3列、bで1列、最後に(a^b)で1列。


@Test
    void bitwiseOperatorsAndTruthTable(){
          Assertions.assertEquals(true, true & true);
          Assertions.assertEquals(false, true & false);
          Assertions.assertEquals(false, false & true);
          Assertions.assertEquals(false, false & false);
    }


テストはパス、これは完全に妥当なJavaです。


OR真理値表


aで3列、bで1列、最後に(a v b)で1列。


   @Test
    void bitwiseOperatorsOrTruthTable(){
        Assertions.assertEquals(true, true | true);
        Assertions.assertEquals(true, true | false);
        Assertions.assertEquals(true, false | true);
        Assertions.assertEquals(false, false | false);
    }


このテストもパスしていますが、なぜ「&&'」と「||'」を好むのでしょうか?


真理値表イメージは 真理値表ツールweb.standfor.edu.


問題点短絡動作


実際に問題となるのは、ビット演算子(&, |)とブール演算子(&&, ||)の動作の違いです。

ブール演算子は短絡的な演算子で、必要な分だけ評価します。

e.g.

if (args != null & args.length() > 23) {
    System.out.println(args);
}


上記のコードでは、ビットワイズ演算子が使用されているため、両方のブール条件が評価されます。

  • args != null
  • args.length() > 23

これでは、argsがnullの場合にNullPointerExceptionが発生する可能性があります。なぜなら、argsがnullの場合でも、args.lengthのチェックを常に行うからです。


ブール演算子 短絡評価


&&が使われている場合は、例えば

if (args != null && args.length() > 23) {
    System.out.println(args);
}


args != nullがfalseと評価されたことがわかると、すぐに条件式の評価を停止します。

右辺の評価は必要ありません。

右辺の条件の結果がどうであれ、ブール式の最終的な値は「偽」になります。


しかし、これはプロダクションコードではあり得ないことです。


これは非常に犯しやすいミスで、静的解析ツールでは必ずしも検出されません。

このパターンの公開例がないか、以下のGoogle Dorkを使って調べてみました。

filetype:java if "!=null & "
今回の検索では、AndroidのRootWindowContainerのコードが出てきました
isDocument = intent != null & intent.isDocument()


代入文の中でビットワイズ演算子を使って値をマスクすることはよくあるので、このようなコードはコードレビューを通過する可能性があります。しかし、この例では、上記のif文の例と同じ結果になります。intentがnullの場合は、NullPointerExceptionがスローされます。

このような構造になってしまうのは、防御的なコードを書いてしまい、冗長なコードを書いてしまうことが多いからです。!=nullのチェックは、ほとんどのユースケースでは余計なことかもしれません。

これはプログラマーがプロダクションコードで犯したエラーです。

検索結果がどれくらい最新かはわかりませんが、私が検索したときには、Google、Amazon、Apache...そして私のコードが戻ってきました。

私のオープンソースプロジェクトの一つで、最近行われたプルリクエストは、まさにこのエラーに対処するものでした。

if(type!=null & type.trim().length()>0){
    acceptMediaTypeDefinitionsList.add(type.trim());
}


これを見つけるには


私のサンプルコードをいくつかの静的アナライザーでチェックしたところ、どのアナライザーもこの隠れた自爆コードを拾いませんでした。

Secure Code Warrior のチームとして、これを拾うことができる、かなりシンプルなSensei のレシピを作成し、検討しました。

ビット演算子は完全に有効であり、代入にもよく使用されるため、問題のあるコードを見つけるために、if文の使用例とビット演算子の使用に焦点を当てました。

search:
  expression:
    anyOf:
    - in:
        condition: {}
    value:
      caseSensitive: false
      matches: ".* & .*"


これは、" & " が条件式として使われている場合に、正規表現を使ってマッチさせるものです。

この問題を解決するために、再び正規表現を利用しました。今回はQuickFixのsed関数を使って、式の中の&を&&にグローバルに置き換えました。

availableFixes:
  - name: "Replace bitwise AND operator to logical AND operator"
    actions:
      - rewrite:
          to: "{{#sed}}s/&/&&/g,{{{ . }}}{{/sed}}"


エンドノート

ここでは、ビットワイズ演算子の最も一般的な誤用、つまり、実際にはブール演算子を意図していた場合について説明します。

課題の例のように、このような状況は他にもありますが、レシピを書く際には、誤認識を避けるようにしなければなりません。私たちは、最も一般的な現象に合わせてレシピを作成します。Sensei が進化するにつれ、より多くの一致する条件をカバーするために、検索機能にさらなる特異性を追加する可能性があります。

現在の形では、このレシピは多くのライブユースケースを特定し、最も重要なのは、私のプロジェクトで報告されたものです。

注:この例題とレシピのレビューには、Charlie Eriksen、Matthieu Calie、Robin Claerhaut、Brysen Ackx、Nathan Desmet、Downey Robersscheutenなど、かなりの数のコードウォリアーが協力してくれました。ご協力ありがとうございました。


---


IntelliJの「Preferences ‾ Plugins」(Mac)または「Settings ‾ Plugins」(Windows)から、「sensei secure code」を検索して、「Sensei 」をインストールできます。

Secure Code Warrior GitHub アカウントの `sensei-blog-examples` リポジトリには、これらのブログ記事(今回の記事を含む)のソースコードやレシピが多数用意されています。

https://github.com/securecodewarrior/sensei-blog-examples

についてはこちらをご覧ください。Sensei


リソースを表示
リソースを表示

レポートをダウンロードするには、以下のフォームに記入してください

当社の製品および/または関連するセキュアコーディングのトピックに関する情報をお送りする許可をお願いします。当社は、お客様の個人情報を常に細心の注意を払って取り扱い、マーケティング目的で他社に販売することは決してありません。

送信
SCW成功アイコン
SCWエラーアイコン
フォームを送信するには、「アナリティクス」クッキーを有効にしてください。設定が完了したら、再度無効にしても構いません。

Java ゴッチャ - ビット演算子とブール演算子

> "Java Gotcha" - 誤って実装してしまいがちなミスパターン。

Javaの簡単な失敗例として、ブール比較演算子の代わりにBitwise演算子を使用することがあります。

例えば、単純なミスタイプで、本当は「&&」と書きたかったのに「&」と書いてしまうことがあります。

コードをレビューするときに学ぶ一般的なヒューリスティックな考え方があります。

条件文の中で「&」や「|」が使われている場合は、おそらく意図したものではないでしょう。

このブログ記事では、ヒューリスティックを探り、このコーディング問題を特定して修正する方法を紹介します。


何が問題なのか?ビット演算はブーリアンでも問題なく使える


ビット演算子をブール値で使用することは完全に有効であり、Javaは構文エラーを報告しません。

JUnit Test でビットワイズ OR (|) とビットワイズ AND (&) の真理値表を調べると、ビットワイズ演算子の出力が真理値表と一致することがわかります。このように考えると、Bitwise 演算子の使用は問題ないと思われるかもしれません。

AND真理値表

aで3列、bで1列、最後に(a^b)で1列。


@Test
    void bitwiseOperatorsAndTruthTable(){
          Assertions.assertEquals(true, true & true);
          Assertions.assertEquals(false, true & false);
          Assertions.assertEquals(false, false & true);
          Assertions.assertEquals(false, false & false);
    }


テストはパス、これは完全に妥当なJavaです。


OR真理値表


aで3列、bで1列、最後に(a v b)で1列。


   @Test
    void bitwiseOperatorsOrTruthTable(){
        Assertions.assertEquals(true, true | true);
        Assertions.assertEquals(true, true | false);
        Assertions.assertEquals(true, false | true);
        Assertions.assertEquals(false, false | false);
    }


このテストもパスしていますが、なぜ「&&'」と「||'」を好むのでしょうか?


真理値表イメージは 真理値表ツールweb.standfor.edu.


問題点短絡動作


実際に問題となるのは、ビット演算子(&, |)とブール演算子(&&, ||)の動作の違いです。

ブール演算子は短絡的な演算子で、必要な分だけ評価します。

e.g.

if (args != null & args.length() > 23) {
    System.out.println(args);
}


上記のコードでは、ビットワイズ演算子が使用されているため、両方のブール条件が評価されます。

  • args != null
  • args.length() > 23

これでは、argsがnullの場合にNullPointerExceptionが発生する可能性があります。なぜなら、argsがnullの場合でも、args.lengthのチェックを常に行うからです。


ブール演算子 短絡評価


&&が使われている場合は、例えば

if (args != null && args.length() > 23) {
    System.out.println(args);
}


args != nullがfalseと評価されたことがわかると、すぐに条件式の評価を停止します。

右辺の評価は必要ありません。

右辺の条件の結果がどうであれ、ブール式の最終的な値は「偽」になります。


しかし、これはプロダクションコードではあり得ないことです。


これは非常に犯しやすいミスで、静的解析ツールでは必ずしも検出されません。

このパターンの公開例がないか、以下のGoogle Dorkを使って調べてみました。

filetype:java if "!=null & "
今回の検索では、AndroidのRootWindowContainerのコードが出てきました
isDocument = intent != null & intent.isDocument()


代入文の中でビットワイズ演算子を使って値をマスクすることはよくあるので、このようなコードはコードレビューを通過する可能性があります。しかし、この例では、上記のif文の例と同じ結果になります。intentがnullの場合は、NullPointerExceptionがスローされます。

このような構造になってしまうのは、防御的なコードを書いてしまい、冗長なコードを書いてしまうことが多いからです。!=nullのチェックは、ほとんどのユースケースでは余計なことかもしれません。

これはプログラマーがプロダクションコードで犯したエラーです。

検索結果がどれくらい最新かはわかりませんが、私が検索したときには、Google、Amazon、Apache...そして私のコードが戻ってきました。

私のオープンソースプロジェクトの一つで、最近行われたプルリクエストは、まさにこのエラーに対処するものでした。

if(type!=null & type.trim().length()>0){
    acceptMediaTypeDefinitionsList.add(type.trim());
}


これを見つけるには


私のサンプルコードをいくつかの静的アナライザーでチェックしたところ、どのアナライザーもこの隠れた自爆コードを拾いませんでした。

Secure Code Warrior のチームとして、これを拾うことができる、かなりシンプルなSensei のレシピを作成し、検討しました。

ビット演算子は完全に有効であり、代入にもよく使用されるため、問題のあるコードを見つけるために、if文の使用例とビット演算子の使用に焦点を当てました。

search:
  expression:
    anyOf:
    - in:
        condition: {}
    value:
      caseSensitive: false
      matches: ".* & .*"


これは、" & " が条件式として使われている場合に、正規表現を使ってマッチさせるものです。

この問題を解決するために、再び正規表現を利用しました。今回はQuickFixのsed関数を使って、式の中の&を&&にグローバルに置き換えました。

availableFixes:
  - name: "Replace bitwise AND operator to logical AND operator"
    actions:
      - rewrite:
          to: "{{#sed}}s/&/&&/g,{{{ . }}}{{/sed}}"


エンドノート

ここでは、ビットワイズ演算子の最も一般的な誤用、つまり、実際にはブール演算子を意図していた場合について説明します。

課題の例のように、このような状況は他にもありますが、レシピを書く際には、誤認識を避けるようにしなければなりません。私たちは、最も一般的な現象に合わせてレシピを作成します。Sensei が進化するにつれ、より多くの一致する条件をカバーするために、検索機能にさらなる特異性を追加する可能性があります。

現在の形では、このレシピは多くのライブユースケースを特定し、最も重要なのは、私のプロジェクトで報告されたものです。

注:この例題とレシピのレビューには、Charlie Eriksen、Matthieu Calie、Robin Claerhaut、Brysen Ackx、Nathan Desmet、Downey Robersscheutenなど、かなりの数のコードウォリアーが協力してくれました。ご協力ありがとうございました。


---


IntelliJの「Preferences ‾ Plugins」(Mac)または「Settings ‾ Plugins」(Windows)から、「sensei secure code」を検索して、「Sensei 」をインストールできます。

Secure Code Warrior GitHub アカウントの `sensei-blog-examples` リポジトリには、これらのブログ記事(今回の記事を含む)のソースコードやレシピが多数用意されています。

https://github.com/securecodewarrior/sensei-blog-examples

についてはこちらをご覧ください。Sensei


オンラインセミナーを見る
始めよう
もっと詳しく

以下のリンクをクリックして、このリソースのPDFをダウンロードしてください。

Secure Code Warriorは、ソフトウェア開発ライフサイクル全体にわたってコードを保護し、サイバーセキュリティを最優先とする文化を築くお手伝いをします。アプリケーションセキュリティマネージャー、開発者、CISO、またはセキュリティ関係者であるかに関わらず、安全でないコードに関連するリスクを軽減するお手伝いをします。

レポートを表示デモを予約
PDFをダウンロード
リソースを表示
シェア:
リンクトインのブランドソーシャルx ロゴ
もっと興味がありますか?

シェア:
リンクトインのブランドソーシャルx ロゴ
著者
アラン・リチャードソン
2021年02月07日掲載

アラン・リチャードソンは20年以上にわたり、開発者として、テスターからテスト責任者まで、テスト階層のあらゆるレベルで経験を積んできました。Secure Code Warrior 、チームと直接連携して、高品質で安全なコードの開発を改善しています。アランは、「ディア・イーブル・テスター」と「Java フォー・テスター」を含む4冊の本の著者です。また、アランはテクニカル・ウェブ・テストと Java を使った Selenium WebDriver を学ぶのに役立つオンライン・トレーニング・コースも作成しています。アランは、SeleniumSimplified.com、EvilTester.com、JavaForTesters.com、CompendiumDev.co.uk にライティングとトレーニングのビデオを投稿しています。

シェア:
リンクトインのブランドソーシャルx ロゴ

Java ゴッチャ - ビット演算子とブール演算子

> "Java Gotcha" - 誤って実装してしまいがちなミスパターン。

Javaの簡単な失敗例として、ブール比較演算子の代わりにBitwise演算子を使用することがあります。

例えば、単純なミスタイプで、本当は「&&」と書きたかったのに「&」と書いてしまうことがあります。

コードをレビューするときに学ぶ一般的なヒューリスティックな考え方があります。

条件文の中で「&」や「|」が使われている場合は、おそらく意図したものではないでしょう。

このブログ記事では、ヒューリスティックを探り、このコーディング問題を特定して修正する方法を紹介します。


何が問題なのか?ビット演算はブーリアンでも問題なく使える


ビット演算子をブール値で使用することは完全に有効であり、Javaは構文エラーを報告しません。

JUnit Test でビットワイズ OR (|) とビットワイズ AND (&) の真理値表を調べると、ビットワイズ演算子の出力が真理値表と一致することがわかります。このように考えると、Bitwise 演算子の使用は問題ないと思われるかもしれません。

AND真理値表

aで3列、bで1列、最後に(a^b)で1列。


@Test
    void bitwiseOperatorsAndTruthTable(){
          Assertions.assertEquals(true, true & true);
          Assertions.assertEquals(false, true & false);
          Assertions.assertEquals(false, false & true);
          Assertions.assertEquals(false, false & false);
    }


テストはパス、これは完全に妥当なJavaです。


OR真理値表


aで3列、bで1列、最後に(a v b)で1列。


   @Test
    void bitwiseOperatorsOrTruthTable(){
        Assertions.assertEquals(true, true | true);
        Assertions.assertEquals(true, true | false);
        Assertions.assertEquals(true, false | true);
        Assertions.assertEquals(false, false | false);
    }


このテストもパスしていますが、なぜ「&&'」と「||'」を好むのでしょうか?


真理値表イメージは 真理値表ツールweb.standfor.edu.


問題点短絡動作


実際に問題となるのは、ビット演算子(&, |)とブール演算子(&&, ||)の動作の違いです。

ブール演算子は短絡的な演算子で、必要な分だけ評価します。

e.g.

if (args != null & args.length() > 23) {
    System.out.println(args);
}


上記のコードでは、ビットワイズ演算子が使用されているため、両方のブール条件が評価されます。

  • args != null
  • args.length() > 23

これでは、argsがnullの場合にNullPointerExceptionが発生する可能性があります。なぜなら、argsがnullの場合でも、args.lengthのチェックを常に行うからです。


ブール演算子 短絡評価


&&が使われている場合は、例えば

if (args != null && args.length() > 23) {
    System.out.println(args);
}


args != nullがfalseと評価されたことがわかると、すぐに条件式の評価を停止します。

右辺の評価は必要ありません。

右辺の条件の結果がどうであれ、ブール式の最終的な値は「偽」になります。


しかし、これはプロダクションコードではあり得ないことです。


これは非常に犯しやすいミスで、静的解析ツールでは必ずしも検出されません。

このパターンの公開例がないか、以下のGoogle Dorkを使って調べてみました。

filetype:java if "!=null & "
今回の検索では、AndroidのRootWindowContainerのコードが出てきました
isDocument = intent != null & intent.isDocument()


代入文の中でビットワイズ演算子を使って値をマスクすることはよくあるので、このようなコードはコードレビューを通過する可能性があります。しかし、この例では、上記のif文の例と同じ結果になります。intentがnullの場合は、NullPointerExceptionがスローされます。

このような構造になってしまうのは、防御的なコードを書いてしまい、冗長なコードを書いてしまうことが多いからです。!=nullのチェックは、ほとんどのユースケースでは余計なことかもしれません。

これはプログラマーがプロダクションコードで犯したエラーです。

検索結果がどれくらい最新かはわかりませんが、私が検索したときには、Google、Amazon、Apache...そして私のコードが戻ってきました。

私のオープンソースプロジェクトの一つで、最近行われたプルリクエストは、まさにこのエラーに対処するものでした。

if(type!=null & type.trim().length()>0){
    acceptMediaTypeDefinitionsList.add(type.trim());
}


これを見つけるには


私のサンプルコードをいくつかの静的アナライザーでチェックしたところ、どのアナライザーもこの隠れた自爆コードを拾いませんでした。

Secure Code Warrior のチームとして、これを拾うことができる、かなりシンプルなSensei のレシピを作成し、検討しました。

ビット演算子は完全に有効であり、代入にもよく使用されるため、問題のあるコードを見つけるために、if文の使用例とビット演算子の使用に焦点を当てました。

search:
  expression:
    anyOf:
    - in:
        condition: {}
    value:
      caseSensitive: false
      matches: ".* & .*"


これは、" & " が条件式として使われている場合に、正規表現を使ってマッチさせるものです。

この問題を解決するために、再び正規表現を利用しました。今回はQuickFixのsed関数を使って、式の中の&を&&にグローバルに置き換えました。

availableFixes:
  - name: "Replace bitwise AND operator to logical AND operator"
    actions:
      - rewrite:
          to: "{{#sed}}s/&/&&/g,{{{ . }}}{{/sed}}"


エンドノート

ここでは、ビットワイズ演算子の最も一般的な誤用、つまり、実際にはブール演算子を意図していた場合について説明します。

課題の例のように、このような状況は他にもありますが、レシピを書く際には、誤認識を避けるようにしなければなりません。私たちは、最も一般的な現象に合わせてレシピを作成します。Sensei が進化するにつれ、より多くの一致する条件をカバーするために、検索機能にさらなる特異性を追加する可能性があります。

現在の形では、このレシピは多くのライブユースケースを特定し、最も重要なのは、私のプロジェクトで報告されたものです。

注:この例題とレシピのレビューには、Charlie Eriksen、Matthieu Calie、Robin Claerhaut、Brysen Ackx、Nathan Desmet、Downey Robersscheutenなど、かなりの数のコードウォリアーが協力してくれました。ご協力ありがとうございました。


---


IntelliJの「Preferences ‾ Plugins」(Mac)または「Settings ‾ Plugins」(Windows)から、「sensei secure code」を検索して、「Sensei 」をインストールできます。

Secure Code Warrior GitHub アカウントの `sensei-blog-examples` リポジトリには、これらのブログ記事(今回の記事を含む)のソースコードやレシピが多数用意されています。

https://github.com/securecodewarrior/sensei-blog-examples

についてはこちらをご覧ください。Sensei


目次

PDFをダウンロード
リソースを表示
もっと興味がありますか?

アラン・リチャードソンは20年以上にわたり、開発者として、テスターからテスト責任者まで、テスト階層のあらゆるレベルで経験を積んできました。Secure Code Warrior 、チームと直接連携して、高品質で安全なコードの開発を改善しています。アランは、「ディア・イーブル・テスター」と「Java フォー・テスター」を含む4冊の本の著者です。また、アランはテクニカル・ウェブ・テストと Java を使った Selenium WebDriver を学ぶのに役立つオンライン・トレーニング・コースも作成しています。アランは、SeleniumSimplified.com、EvilTester.com、JavaForTesters.com、CompendiumDev.co.uk にライティングとトレーニングのビデオを投稿しています。

もっと詳しく

Secure Code Warriorは、ソフトウェア開発ライフサイクル全体にわたってコードを保護し、サイバーセキュリティを最優先とする文化を築くお手伝いをします。アプリケーションセキュリティマネージャー、開発者、CISO、またはセキュリティ関係者であるかに関わらず、安全でないコードに関連するリスクを軽減するお手伝いをします。

デモを予約[ダウンロード]
シェア:
リンクトインのブランドソーシャルx ロゴ
リソースハブ

始めるためのリソース

その他の投稿
リソースハブ

始めるためのリソース

その他の投稿