Apex Academy
Módulo 12·04·30 min

Inyección de SOQL y escaping

Si pegas lo que escribe un usuario dentro del texto de una consulta, el usuario puede escribir consulta. Dos reglas lo cierran: los valores se enlazan, y lo que no se puede enlazar se compara con una lista cerrada.

En Admin esUn campo de texto libre donde alguien pega una fórmula en lugar de un nombre

✓ En esta sub-lección

  1. 01Reconocer una consulta vulnerable a inyección.
  2. 02Enlazar los valores en SOQL dinámico con Database.queryWithBinds.
  3. 03Validar con una lista cerrada lo que no se puede enlazar.

La nutria: ¿Te acuerdas? · Repaso de la lección 3

¿Qué hace WITH USER_MODE al final de una consulta?

Cuarto hallazgo de la auditoría, y el más serio. El buscador de renovaciones monta su consulta pegando lo que escribe el comercial. El auditor escribe en la caja de búsqueda un texto que no es un nombre de cuenta, y el buscador le devuelve oportunidades que no son renovaciones. Ha conseguido que su texto se ejecute como parte de la consulta.

Cómo se cuela

Apex
String soql = 'SELECT Id FROM Opportunity WHERE Type = \'Renewal\' AND Account.Name LIKE \'%' + term + '%\'';

// Si term es:   acme
// la consulta:  … AND Account.Name LIKE '%acme%'

// Si term es:   %' OR Name LIKE '
// la consulta:  … AND Account.Name LIKE '%%' OR Name LIKE '%'      ← ya no filtra renovaciones
La comilla que escribe el usuario cierra tu texto antes de tiempo, y lo que viene detrás deja de ser un valor: es consulta.
Escribe:

EN LA CAJA DE BÚSQUEDA

acme

LA CONSULTA QUE SE EJECUTA

SELECT Id, Name FROM Opportunity
WHERE Type = 'Renewal'
  AND Account.Name LIKE '%acme%'
Devuelve:1 renovación de Acme

Con un nombre normal, las dos formas devuelven lo mismo. Por eso el fallo no se nota hasta que alguien lo busca.

Escribe en el buscador como un comercial y como un auditor, y mira qué consulta se ejecuta pegando el texto y enlazándolo.

Dos reglas

  1. Los valores se enlazan. En una consulta entre corchetes, con :variable (Módulo 3). En SOQL dinámico, con Database.queryWithBinds(texto, mapa, AccessLevel.USER_MODE): el texto lleva :pattern y el mapa dice cuánto vale. Un valor enlazado nunca se interpreta como consulta, tenga las comillas que tenga.
  2. Lo que no se puede enlazar, se compara con una lista cerrada. Un nombre de campo para ordenar no es un valor, y no admite los dos puntos. Ahí el usuario solo puede elegir entre opciones que tú has escrito: si lo que llega no está en la lista, se usa una por defecto.

Verás también String.escapeSingleQuotes(term), que pone una barra delante de cada comilla: es la defensa antigua, y sirve cuando no hay más remedio que pegar el texto. Pero enlazar es más seguro, porque no depende de que te acuerdes de escapar. Y si la consulta puede escribirse entre corchetes, escríbela así: es la que no se puede inyectar.

La nutria: ¿Y por qué no un Flow? Aquí Flow es más seguro que tú

Te lo digo sin rodeos: un Get Records no se puede inyectar. Lo que el usuario escribe en una pantalla llega al filtro como valor, nunca como texto de consulta. Este problema solo existe en el código, y solo cuando alguien monta la consulta pegando textos. Es un riesgo que trae Apex consigo, y por eso cae en el examen: la primera pregunta ante un buscador sencillo sigue siendo si hace falta escribirlo en código.