Blog

Errores comunes en Java: operadores a nivel de bits frente a operadores booleanos

February 7, 2021
Alan Richardson
Manos escribiendo en un portátil que muestra código con el texto «domina la programación segura con Sensei» y el logotipo de Secure Code Warrior.

Trampas de Java: operadores bit a bit frente a booleanos

> "Trampa de Java": un patrón de error común que es fácil de implementar por accidente.

Una trampa de Java bastante sencilla en la que caer por accidente es: usar un operador bit a bit en lugar de un operador de comparación booleano.

Por ejemplo, un simple error de escritura puede hacer que escribas "&" cuando en realidad querías escribir "&&".

Una heurística común que aprendemos al revisar código es:

> "&" o "|" cuando se usan en una sentencia condicional probablemente no sean intencionados.

En esta entrada del blog, exploraremos esta heurística e identificaremos formas de detectar y corregir este problema de programación.


¿Cuál es el problema? Las operaciones bit a bit funcionan bien con booleanos


Usar operadores bit a bit con booleanos es perfectamente válido, por lo que Java no informará de un error de sintaxis.

Si construyo una prueba JUnit para explorar una tabla de verdad tanto para OR bit a bit (|) como para AND bit a bit (&), veremos que los resultados del operador bit a bit coinciden con la tabla de verdad. Dado esto, podríamos pensar que el uso de operadores bit a bit no es un problema.

Tabla de verdad AND

Three columns on with a, one with b, and the last with (a^b)


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


La prueba pasa; esto es Java perfectamente válido.


Tabla de verdad OR


Three columns on with a, one with b, and the last with (a v b)


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


Esta prueba también pasa, ¿por qué preferimos "&&" y "||"?


Las imágenes de las tablas de verdad se crearon utilizando la herramienta de tablas de verdad de web.standfor.edu.


Problema: Operación de cortocircuito


El verdadero problema es la diferencia de comportamiento entre los operadores a nivel de bits (&, |) y los operadores booleanos (&&, ||).

Un operador booleano es un operador de cortocircuito y solo evalúa lo estrictamente necesario.

p. ej.

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


En el código anterior, ambas condiciones booleanas se evaluarán porque se ha utilizado el operador a nivel de bits:

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

Esto deja mi código expuesto a una NullPointerException si args es nulo, ya que siempre realizaremos la comprobación de args.length, incluso cuando args sea nulo, porque ambas condiciones booleanas deben evaluarse.


Evaluación de cortocircuito de operadores booleanos


Cuando se utiliza &&, p. ej.

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


Tan pronto como sabemos que args != null se evalúa como falso, la evaluación de la expresión condicional se detiene.

No necesitamos evaluar el lado derecho.

Independientemente del resultado de la condición del lado derecho, el valor final de la expresión booleana será falso.


Pero esto nunca sucedería en código de producción


Es un error bastante fácil de cometer y no siempre es detectado por las herramientas de análisis estático.

Utilicé el siguiente Google Dork para ver si podía encontrar ejemplos públicos de este patrón:

filetype:java if "!=null & "
Esta búsqueda arrojó algo de código de Android en RootWindowContainer
isDocument = intent != null & intent.isDocument()


Este es el tipo de código que podría pasar una revisión, ya que a menudo usamos operadores a nivel de bits en sentencias de asignación para enmascarar valores. Pero en este caso, el resultado es el mismo que en el ejemplo de la sentencia if anterior. Si intent es nulo, se producirá una NullPointerException.

A menudo nos libramos de esta construcción porque solemos programar de forma defensiva y escribir código redundante. La comprobación != null bien podría ser redundante en la mayoría de los casos de uso.

Este es un error cometido por programadores en código de producción.

No sé qué tan actuales son los resultados de la búsqueda, pero cuando la realicé, obtuve resultados con código de: Google, Amazon, Apache... y mío.

Una solicitud de extracción (pull request) reciente en uno de mis proyectos de código abierto abordaba exactamente este error.

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


Cómo encontrar esto


Cuando comprobé mi código de ejemplo en varios analizadores estáticos, ninguno detectó este código de autodestrucción oculto.

Como equipo en Secure Code Warrior, creamos y revisamos una receta de Sensei bastante sencilla que podría detectarlo.

Debido a que los operadores a nivel de bits son perfectamente válidos y se utilizan a menudo en asignaciones, nos centramos en el caso de uso de las sentencias if y el uso del operador & a nivel de bits para encontrar el código problemático.

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


Esto utiliza una expresión regular para buscar " & " cuando se utiliza como expresión condicional, por ejemplo, en una sentencia if.

Para solucionarlo, volvimos a recurrir a las expresiones regulares. Esta vez utilizamos la función sed en el QuickFix para reemplazar globalmente el & en la expresión por &&.

availableFixes:
  - name: "Reemplazar el operador AND a nivel de bits por el operador AND lógico"
    actions:
      - rewrite:
          to: "{{#sed}}s/&/&&/g,{{{ . }}}{{/sed}}"


Notas finales

Esto cubre el uso indebido más común de un operador a nivel de bits, es decir, cuando en realidad se pretendía utilizar un operador booleano.

Existen otras situaciones en las que esto podría surgir, como en el ejemplo de asignación, pero al escribir recetas debemos intentar evitar la identificación de falsos positivos; de lo contrario, las recetas serán ignoradas o desactivadas. Creamos recetas para detectar las incidencias más comunes. A medida que Sensei evolucione, es muy probable que añadamos mayor especificidad a la funcionalidad de búsqueda para cubrir más condiciones de coincidencia.

En su forma actual, esta receta identificaría muchos de los casos de uso reales y, lo que es más importante, el que se informó en mi proyecto.

NOTA: Un buen número de "code warriors" contribuyeron a este ejemplo y a la revisión de la receta: Charlie Eriksen, Matthieu Calie, Robin Claerhaut, Brysen Ackx, Nathan Desmet, Downey Robersscheuten. Gracias por su ayuda.


---


Puedes instalar Sensei desde IntelliJ usando "Preferences \ Plugins" (Mac) o "Settings \ Plugins" (Windows) y luego simplemente buscar "sensei secure code"

Tenemos una gran cantidad de código fuente y recetas para estas publicaciones de blog (incluida esta) en el repositorio `sensei-blog-examples` en la cuenta de GitHub de Secure Code Warrior.

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

Más información sobre Sensei


Eslogan

Controle el desarrollo impulsado por IA antes de su lanzamiento

Mida el riesgo asistido por IA, aplique políticas de codificación segura en el momento de la confirmación y acelere la entrega segura en todo su ciclo de vida de desarrollo de software (SDLC).

Reservar una demostración
Biblioteca de recursos

Explorar más blogs

Acceda a contenido especializado sobre programación segura, gobernanza de IA y gestión de riesgos de software.

ver todo
Caso de estudio
Etiqueta de filtro
Este es un texto dentro de un bloque div.

Una cultura de seguridad: cómo Sage creó su programa de defensores

Descubre cómo Sage reforzó su seguridad con un enfoque flexible y centrado en las relaciones, logrando formar a más de 200 expertos en seguridad y alcanzar una reducción de riesgos cuantificable.

Más información
Icono de ventana de navegador con las letras KVK en negrita y dos líneas horizontales azules debajo sobre un fondo azul degradado.
Caso de estudio
Etiqueta de filtro
Este es un texto dentro de un bloque div.

Kamer van Koophandel: Seguridad a escala impulsada por los desarrolladores

La Cámara de Comercio (Kamer van Koophandel) comparte cómo integró la programación segura en el desarrollo diario mediante certificaciones basadas en roles, evaluaciones comparativas de puntuación de confianza y una cultura de responsabilidad compartida en seguridad.

Más información
Logotipo de Insurance Australia Group con texto y un icono en azul y gris dentro del diseño de una ventana de navegador web.
Caso de estudio
Etiqueta de filtro
Este es un texto dentro de un bloque div.

Cómo un «Juego de Códigos» guía a IAG Group hacia un futuro de programación más seguro

IAG Group es la entidad detrás de muchas de las principales compañías de seguros en la región de Asia-Pacífico, que suscribe pólizas para millones de clientes por un valor aproximado de 11.400 millones de dólares australianos en primas anuales.

Más información
Blog
Etiqueta de filtro
Este es un texto dentro de un bloque div.

Mes de la Concienciación sobre Ciberseguridad 2026: Comprender la seguridad de la IA es fundamental para la defensa empresarial

Hemos llegado a un punto en el que la mayoría de las principales empresas de IA han pedido públicamente una ralentización global en el desarrollo de la IA para que las medidas de seguridad puedan ponerse al día, pero la verdad es que ya se han materializado graves problemas de seguridad. Esto deja a innumerables líderes de seguridad y equipos ejecutivos en una situación urgente, a medida que los costes monetarios y de riesgo de la implementación de la IA siguen acumulándose.

Más información
Blog
Etiqueta de filtro
Este es un texto dentro de un bloque div.

Habilitador 8: Branding de su programa

Un programa de codificación segura necesita algo más que un buen contenido para tener éxito. El Habilitador 8 muestra cómo el branding transforma la participación en una experiencia codiciada basada en el estatus.

Más información
Blog
Etiqueta de filtro
Este es un texto dentro de un bloque div.

Cada empleado está ahora en la primera línea de la ciberseguridad de la IA

El panorama tecnológico empresarial está cambiando a una velocidad que pocos previeron hasta que fue demasiado tarde. Hemos cruzado oficialmente el umbral del código escrito por humanos y la asistencia básica de copilotos hacia la era del Ciclo de Vida de Desarrollo Agéntico (ADLC). Si bien los agentes de IA autónomos prometen una eficiencia sin precedentes en múltiples funciones, también traen consigo una clase completamente nueva de riesgos regulatorios y de seguridad.

Más información

Proteja el desarrollo basado en IA antes de su lanzamiento

Identifique riesgos de desarrollo, aplique políticas y prevenga vulnerabilidades en todo el ciclo de vida de su software.

Reservar una demostración
No se encontraron elementos.