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 esta sub-lección
- 01Reconocer una consulta vulnerable a inyección.
- 02Enlazar los valores en SOQL dinámico con Database.queryWithBinds.
- 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
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 renovacionesEN LA CAJA DE BÚSQUEDA
acme
LA CONSULTA QUE SE EJECUTA
SELECT Id, Name FROM Opportunity WHERE Type = 'Renewal' AND Account.Name LIKE '%acme%'
Con un nombre normal, las dos formas devuelven lo mismo. Por eso el fallo no se nota hasta que alguien lo busca.
Dos reglas
- 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.
- 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ú