Funciones de hashing — Referencia de SSJS en Marketing Cloud
SHA256, SHA1, MD5, HMAC — cuándo cada uno es la respuesta correcta en MC SSJS, la trampa de output hex-vs-Base64, y por qué MD5 sigue por ahí para tracking pixels pero no para nada que importe.
El hashing en SSJS aparece donde necesitás un fingerprint de longitud fija de datos input: claves de cache, tokens de idempotencia, verificación de firma en webhooks entrantes, identificadores opacos que no revelan los datos subyacentes, y tracking pixels que tienen que verse uniformes. Marketing Cloud expone la biblioteca estándar a través de Platform.Function.*: SHA256, SHA1, MD5, más sus variantes HMAC. Las trampas son sobre qué algoritmo elegir (MD5 está roto para seguridad pero todavía se usa para IDs no-de-seguridad), qué formato de output obtenés (string hex por default, pero las preguntas de equivalencia de bytes aparecen al comparar entre sistemas), y la limitación de datos binarios que ya vimos con Base64 — estas funciones hashean strings, no bytes.
Sintaxis oficial
Platform.Load("Core", "1.1.5");
// === Hashes planos (devuelve string hex en minúscula) ===
Platform.Function.MD5("hola mundo");
// → "94c3aae3e3a728e0fde9b9c0bd80f0c5"
Platform.Function.SHA1("hola mundo");
// → "11d6c8eb5b842205fb1da3c7d6c11d24f10b59ef"
Platform.Function.SHA256("hola mundo");
// → "0b894166d3336435c800bea36ff21b29eaa801a52f584c006c49289a0dcf6e2f"
Platform.Function.SHA512("hola mundo");
// → 128-char hex en minúscula (SHA-512)
// === Hashes con clave (HMAC) — para verificar payloads firmados ===
// HMACSHA256(message, key, returnHex)
// - message: string a firmar
// - key: clave secreta (string)
// - returnHex: true → string hex, false → string Base64
Platform.Function.HMACSHA256("payload", "secret-key", true);
// → "f9320baf0249169e73850cd6156ded0106e2bb6ad8cab01b7bbbebe6d1065317"
Platform.Function.HMACSHA256("payload", "secret-key", false);
// → "+TILqRJSbbXIY1iuzXgZcJsiNJTNl1HG18uPZ7+8DhM="
// HMACSHA1 / HMACMD5 también existen con la misma firma
Platform.Function.HMACSHA1("payload", "secret-key", true);
Platform.Function.HMACMD5("payload", "secret-key", true);
// === Patrón común: token de redirect firmado ===
var payload = subscriberKey + "|" + Now().getTime();
var signature = Platform.Function.HMACSHA256(payload, secretKey, true);
var token = Platform.Function.URLEncode(payload + "|" + signature);
// En el lado receptor: split, recomputar HMACSHA256(payload, secretKey),
// comparar a la firma provista en tiempo constante.El set soportado:
| Función | Firma | Output |
|---|---|---|
| MD5(s) | string → string | hex en minúscula 32-char |
| SHA1(s) | string → string | hex en minúscula 40-char |
| SHA256(s) | string → string | hex en minúscula 64-char |
| SHA512(s) | string → string | hex en minúscula 128-char |
| HMACMD5(msg, key, hex?) | strings + booleano → string | hex (true) o Base64 (false) |
| HMACSHA1(msg, key, hex?) | strings + booleano → string | hex (true) o Base64 (false) |
| HMACSHA256(msg, key, hex?) | strings + booleano → string | hex (true) o Base64 (false) |
Referencia:
- Salesforce Developer — Referencia de Platform Functions (helpers de hashing) ↗
- MDN — Hash digests (referencia conceptual, equivalentes browser-side) ↗
Lo que sobrevive en producción
Elegí el algoritmo correcto — seguridad vs identidad
Las cuatro familias de hash sirven para propósitos distintos. Elegir el equivocado manda una vulnerabilidad que un code review generalmente captura pero un deadline no.
| Algoritmo | Usar para | Evitar para | |---|---|---| | MD5 | Claves de cache, IDs de tracking pixel, claves de deduplicación donde el único requisito es "fingerprint estable" | Cualquier cosa donde colisión importe: passwords, firmas, detección de tampering | | SHA1 | Compatibilidad legacy (ej. APIs de terceros que todavía requieren firmas SHA1) | Código nuevo — andá a SHA256 salvo que tengas razón | | SHA256 | Firmas, checks de integridad, cualquier cosa donde resistencia a colisiones importe. El default para código nuevo. | Truncar a menos de 16 chars — colisiones de hash parcial se vuelven plausibles rápido | | SHA512 | Usá cuando un sistema upstream lo requiera. Si no, SHA256 es suficiente. | Paths sensibles a performance que no necesitan output de 512-bit |
La disciplina: SHA256 es el default. Usá MD5 solo cuando un sistema upstream lo requiera (algunos formatos de tracking pixel de MarTech), y documentá la elección en un comentario así el próximo reviewer no lo flaggea como bug de seguridad.
HMAC* es para firmar, los hashes planos son para fingerprinting
Un hash sin clave es un fingerprint — cualquiera que conozca el input puede computar el mismo output. Eso está bien para claves de cache e IDs, inútil para verificar que un request vino de vos.
HMAC* agrega una clave secreta al cálculo. El lado receptor puede verificar que el mensaje no fue tampereado solo si tiene el mismo secreto. Así funcionan las firmas de webhook, las URLs de redirect firmadas, y la autenticación de requests de API.
// EN RIESGO — usar un hash plano como "firma" no es firma para nada.
// Cualquiera que pueda ver la URL puede computar el mismo valor y forjar requests.
var token = Platform.Function.SHA256(subscriberKey + "|" + Now().getTime());
var url = "https://app.example.com/?sub=" + subscriberKey + "&t=" + token;
// CORRECTO — HMAC con un secreto. La firma no se puede reproducir sin
// conocer la clave, así que podés verificar procedencia en el lado receptor.
var payload = subscriberKey + "|" + Now().getTime();
var sig = Platform.Function.HMACSHA256(payload, sharedSecret, true);
var url = "https://app.example.com/?sub=" + Platform.Function.URLEncode(payload)
+ "&sig=" + Platform.Function.URLEncode(sig);La regla general: si la pregunta es "¿esto vino de nosotros?", necesitás un HMAC. Un hash plano responde "¿es este el mismo input que antes?", que es una pregunta distinta.
Hex vs Base64 output — matcheá el sistema receptor
Las funciones HMAC toman un tercer booleano para formato de output: true → hex, false → Base64. La forma hex es el doble de larga pero predecible; la forma Base64 es más compacta pero tiene la pregunta de URL-safety que vimos en Encoding. Cuando el sistema receptor espera un formato específico, usá ese — no asumas que hex es siempre más seguro.
// El receptor del webhook espera firma Base64 en el header
var sig = Platform.Function.HMACSHA256(body, webhookSecret, false);
HTTP.Post("https://hook.example.com/in", body, {
"X-Signature": sig, // Base64
"Content-Type": "application/json"
});
// API de vendor espera firma hex en query string
var sig = Platform.Function.HMACSHA256(payload, vendorSecret, true);
var url = vendorEndpoint + "?payload=" + Platform.Function.URLEncode(payload)
+ "&sig=" + sig; // hex, sin URL encoding necesarioAnte la duda, chequeá la documentación del sistema receptor por el formato exacto esperado. Mandar Base64 donde se espera hex (o viceversa) no falla visiblemente — falla como "signature does not match", que es el mismo mensaje de error que tendrías si tu secreto estuviera mal.
Verificá firmas HMAC con comparación de tiempo constante
Cuando recibís un request firmado y verificás, la comparación tiene que ser de tiempo constante para evitar timing attacks. El === naive sale temprano en el primer caracter que no matchea y filtra información sobre cuánto de la firma estaba correcta.
// EN RIESGO — sale temprano, filtra progreso de firma vía timing
function verify(provided, expected) {
return provided === expected;
}
// MÁS SEGURO — comparación de tiempo constante; hace el mismo número de operaciones
// sin importar dónde está el mismatch
function constantTimeEqual(a, b) {
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
var diff = 0;
for (var i = 0; i < a.length; i++) {
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return diff === 0;
}
var expected = Platform.Function.HMACSHA256(receivedPayload, sharedSecret, true);
if (!constantTimeEqual(receivedSignature, expected)) {
// rechazá el request
}Para uso interno de bajo volumen el riesgo de timing attack es teórico, pero el código es corto y la disciplina vale la pena llevarse.
Decisión rápida
Usá Platform.Function.SHA256 cuando:
- Necesitás un fingerprint de datos input y el output va a un sistema que vos controlás o firmás. El default para código nuevo.
Usá Platform.Function.MD5 cuando:
- Un sistema upstream específicamente requiere MD5 (tracking pixels legacy, algunas APIs de vendor de MarTech). Documentá la razón legacy en un comentario.
Usá Platform.Function.HMACSHA256(msg, key, hex) cuando:
- Estás produciendo una firma que el sistema receptor va a verificar con el mismo secreto.
- Estás verificando una firma entrante en un webhook o callback firmado.
Elegí el formato de output (hex vs Base64) basado en:
- Lo que el sistema receptor espera. Hex es el default seguro si no hay spec. Base64 es más corto pero necesita URL encoding cuando se usa en URLs.
Usá comparación de tiempo constante cuando:
- Verificás firmas HMAC entrantes. La función arriba (
constantTimeEqual) es el patrón estándar.
No uses hashing cuando:
- En realidad necesitás encriptación (i.e., el dato tiene que ser reversible). El hashing es one-way; para two-way andarías a
Platform.Function.EncryptSymmetric/DecryptSymmetric.
Relacionado
- Basics — superficie de lenguaje soportada
- Platform.Function — el namespace donde estos helpers viven
- Funciones de encoding — patrones de Base64 / URL encoding que seguido se parean con firma HMAC
- WSProxy — flujos de webhook e integraciones outbound donde aparecen firmas HMAC
- MC SSJS gotchas — ver #9 (callouts HTTP) para el contexto más amplio de integración
Próximas páginas de referencia SSJS: Util / Variable · Style Guide.
Más snippets how-to para patrones comunes de producción — DE add/update/upsert, manejo de errores, paginación de callouts, etc.