Java 주의사항 - 비트 연산자와 논리 연산자 비교
Java의 함정 - 비트 연산자 vs 논리 연산자
> "Java의 함정(Java Gotcha)" - 실수로 구현하기 쉬운 흔한 오류 패턴을 의미합니다.
Java에서 실수하기 쉬운 간단한 함정 중 하나는 논리 비교 연산자 대신 비트 연산자를 사용하는 것입니다.
예를 들어, 오타로 인해 "&&"를 써야 할 자리에 "&"를 쓰는 경우가 있습니다.
코드 리뷰 시 흔히 적용하는 휴리스틱은 다음과 같습니다.
> 조건문에서 "&" 또는 "|"가 사용되었다면 의도된 것이 아닐 가능성이 높습니다.
이 블로그 게시물에서는 해당 휴리스틱을 살펴보고, 이 코딩 문제를 식별하고 수정하는 방법을 알아봅니다.
무엇이 문제인가요? 비트 연산은 불리언(Boolean)과도 잘 작동합니다
불리언에 비트 연산자를 사용하는 것은 문법적으로 완벽하게 유효하므로 Java는 구문 오류를 보고하지 않습니다.
JUnit 테스트를 작성하여 비트 OR(|)와 비트 AND(&)의 진리표를 확인해보면, 비트 연산자의 결과값이 진리표와 일치하는 것을 볼 수 있습니다. 이를 보면 비트 연산자 사용이 문제가 되지 않는 것처럼 보일 수 있습니다.
AND 진리표

@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 진리표

@Test
void bitwiseOperatorsOrTruthTable(){
Assertions.assertEquals(true, true | true);
Assertions.assertEquals(true, true | false);
Assertions.assertEquals(true, false | true);
Assertions.assertEquals(false, false | false);
}
이 테스트 역시 통과합니다. 그렇다면 왜 "&&"와 "||"를 더 선호하는 것일까요?
진리표 이미지는 truth table tool (web.standfor.edu)을 사용하여 생성되었습니다.
문제: 단락 평가(Short Circuit Operation)
실제 문제는 비트 연산자(&, |)와 논리 연산자(&&, ||) 간의 동작 차이입니다.
논리 연산자는 단락 평가를 수행하며, 필요한 만큼만 평가합니다.
예:
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로 평가되는 즉시 조건식 평가가 중단됩니다.
우측 항을 평가할 필요가 없습니다.
우측 조건의 결과가 무엇이든, 전체 논리식의 최종 값은 false가 되기 때문입니다.
하지만 이런 일은 실제 운영 코드에서 절대 일어나지 않을까요?
이는 매우 흔히 발생하는 실수이며, 정적 분석 도구에서도 항상 잡아내지는 못합니다.
다음 구글 검색어(Google Dork)를 사용하여 이 패턴이 포함된 공개 사례를 찾아보았습니다:
filetype:java if "!=null & "
이 검색을 통해 Android의 RootWindowContainer에서 다음과 같은 코드를 발견했습니다.
isDocument = intent != null & intent.isDocument()
이런 코드는 코드 리뷰를 통과할 가능성이 있습니다. 값 마스킹을 위해 할당문에서 비트 연산자를 사용하는 경우가 종종 있기 때문입니다. 하지만 이 경우에도 위 if 문 예제와 결과는 동일합니다. 만약 intent가 null이라면 NullPointerException이 발생하게 됩니다.
우리는 종종 방어적으로 코딩하고 중복된 코드를 작성하기 때문에 이러한 구조를 그대로 사용하는 경우가 많습니다. 대부분의 사용 사례에서 != null 확인은 불필요할 수 있습니다.
이는 프로그래머들이 프로덕션 코드에서 저지르는 실수입니다.
검색 결과가 얼마나 최신 상태인지는 알 수 없지만, 제가 검색했을 때 Google, Amazon, Apache, 그리고 저 자신의 코드에서 결과가 나왔습니다.
최근 제 오픈 소스 프로젝트 중 하나에 들어온 풀 리퀘스트(pull request)가 바로 이 오류를 해결하기 위한 것이었습니다.
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: ".* & .*"
이것은 정규식을 사용하여 if 문과 같은 조건식에서 " & "가 사용될 때 이를 매칭합니다.
이를 수정하기 위해 다시 정규식을 사용했습니다. 이번에는 QuickFix의 sed 함수를 사용하여 식 내의 &를 &&로 전역 교체했습니다.
availableFixes:
- name: "Replace bitwise AND operator to logical AND operator"
actions:
- rewrite:
to: "{{#sed}}s/&/&&/g,{{{ . }}}{{/sed}}"
맺음말
이 내용은 비트 연산자의 가장 흔한 오용, 즉 논리 연산자가 의도되었을 때 발생하는 경우를 다룹니다.
할당 예시와 같이 다른 상황에서도 이런 문제가 발생할 수 있지만, 레시피를 작성할 때는 오탐(false-positive)을 피해야 합니다. 그렇지 않으면 레시피가 무시되거나 꺼질 수 있기 때문입니다. 우리는 가장 흔한 사례를 찾기 위해 레시피를 만듭니다. Sensei가 발전함에 따라 더 많은 매칭 조건을 다룰 수 있도록 검색 기능에 추가적인 세부 사항을 더할 예정입니다.
현재 형태의 이 레시피는 많은 실제 사용 사례를 식별할 수 있으며, 가장 중요한 것은 제 프로젝트에서 보고된 사례를 찾아낼 수 있다는 점입니다.
참고: 이 예제와 레시피 검토에는 Charlie Eriksen, Matthieu Calie, Robin Claerhaut, Brysen Ackx, Nathan Desmet, Downey Robersscheuten 등 많은 코드 워리어들이 기여했습니다. 도움을 주셔서 감사합니다.
---
IntelliJ 내에서 "Preferences \ Plugins"(Mac) 또는 "Settings \ Plugins"(Windows)를 통해 Sensei를 설치할 수 있으며, 그 후 "sensei secure code"를 검색하면 됩니다.
Secure Code Warrior GitHub 계정의 `sensei-blog-examples` 저장소에서 이 블로그 게시물들을 포함한 많은 소스 코드와 레시피를 확인할 수 있습니다.
https://github.com/securecodewarrior/sensei-blog-examples
배포 전 AI 기반 개발 거버넌스 확보
AI 기반 위험을 측정하고, 커밋 시점에 보안 코딩 정책을 적용하며, SDLC 전반에 걸쳐 안전한 소프트웨어 제공 속도를 높이세요.
더 많은 블로그 살펴보기
보안 코딩, AI 거버넌스, 소프트웨어 리스크 관리에 관한 전문가 콘텐츠를 확인해 보세요.
%252520%252520(3).avif)
Supercharged Security Awareness: How Tournaments are Inspiring Developers at Erste Group
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Security as culture: How Blue Prism cultivates world-class secure developers
Learn how Blue Prism, the global leader in intelligent automation for the enterprise, used Secure Code Warrior's agile learning platform to create a security-first culture with their developers, achieve their business goals, and ship secure code at speed
배포 전 AI 기반 개발 보안 확보
소프트웨어 개발 수명 주기 전반에서 개발자 리스크를 파악하고, 정책을 시행하며, 취약점을 예방하세요.



.avif)