npm-package-nodejs-utils-lda
Version:
Este projeto tem como fins de criar e abstrair módulos basicos e utilidades para o node js
146 lines (119 loc) • 4.84 kB
JavaScript
const { decryptAESKey, decryptAESGCM } = require("./crypto.service.cjs");
const path = require("path"); // Mantido para consistência, se necessário para a biblioteca
const recentNonces = new Set();
// 💡 Função auxiliar para validar e coletar erros (JSDoc adicionado)
/**
* @function validatePayloadFields
* @description Verifica a presença de todos os campos criptográficos necessários.
*
* @param {object} body - O corpo da requisição Express (req.body).
* @returns {string[] | null} Uma array de strings com os campos faltantes, ou null se tudo estiver ok.
*/
function validatePayloadFields(body) {
const requiredFields = [
"encryptedData",
"encryptedKey",
"iv",
"authTag",
"timestamp",
"nonce",
];
const missingFields = requiredFields.filter((field) => !body[field]);
return missingFields.length > 0 ? missingFields : null;
}
/**
* Middleware Express para processar payloads criptografados
*
* @param {import('express').Request} req
* @param {import('express').Response} res
* @param {import('express').NextFunction} next
* @returns {void}
*/
function encryptedPayloadMiddleware(req, res, next) {
// Captura o IP de origem e a URL original para logs de segurança
const originIP = req.ip || "N/A";
const originUrl = req.originalUrl;
try {
const { encryptedData, encryptedKey, iv, authTag, timestamp, nonce } =
req.body;
const timeLimit = 30000; // 30 segundos
/* ===== 1. Validação básica (Payload Incompleto) ===== */
const missing = validatePayloadFields(req.body);
if (missing) {
const errorMsg = `Payload criptografado incompleto. Campos faltantes: ${missing.join(
", "
)}. Certifique-se de enviar todos os componentes criptográficos.`;
// ⚠️ Log para o Administrador (Falha de Cliente)
console.warn(
`[CRYPTO WARN | 400] Payload incompleto. Faltando: ${missing.join(
", "
)}. Origem: ${originIP} em ${originUrl}`
);
return res.status(400).json({
error: errorMsg,
missing_fields: missing,
required_format: {
encryptedData: "string (base64)",
encryptedKey: "string (base64)",
iv: "string (base64)",
authTag: "string (base64)",
timestamp: "number (ms)",
nonce: "string",
},
});
}
/* ===== 2. Validação de tempo (Time/Sync Fail) ===== */
const now = Date.now();
const timeDiff = Math.abs(now - timestamp);
if (timeDiff > timeLimit) {
// ⚠️ Log para o Administrador (Falha de Sincronia)
console.warn(
`[CRYPTO WARN | 401] Timestamp inválido. Diferença de ${timeDiff}ms (Limite: ${timeLimit}ms). Origem: ${originIP} em ${originUrl}`
);
return res.status(401).json({
error: "Timestamp inválido ou expirado.",
details: `Diferença de tempo de ${timeDiff}ms, excedendo o limite de ${timeLimit}ms (30 segundos). Verifique a sincronia do seu relógio.`,
});
}
/* ===== 3. Proteção contra replay (Ataque Potencial) ===== */
if (recentNonces.has(nonce)) {
// 🔥 Log de ALERTA DE SEGURANÇA para o Administrador (Ataque de Replay)
console.error(
`[CRYPTO SECURITY ALERT | 401] Nonce Reutilizado (Replay Attack Detectado!). Nonce: ${nonce}. Origem: ${originIP} em ${originUrl}`
);
return res.status(401).json({
error: "Nonce reutilizado (Replay Attack Detected).",
details:
"Este Nonce foi usado recentemente. Gere um novo Nonce único para cada requisição.",
});
}
recentNonces.add(nonce);
setTimeout(() => recentNonces.delete(nonce), timeLimit);
/* ===== 4. Descriptografia ===== */
const aesKey = decryptAESKey(encryptedKey);
const plaintext = decryptAESGCM(encryptedData, aesKey, iv, authTag);
// ✅ Log de SUCESSO
console.log(
`[CRYPTO SUCCESS | 200] Payload descriptografado com sucesso. Origem: ${originIP} em ${originUrl}`
);
/**
* 🔑 Dado descriptografado disponível
* para as próximas rotas/middlewares
*/
req.decryptedBody = JSON.parse(plaintext);
next();
} catch (err) {
// ❌ Log de Erro Grave (Falha de Descriptografia)
console.error(
`[CRYPTO FATAL ERROR | 400] Falha ao descriptografar payload. Causa: ${err.message}. Origem: ${originIP} em ${originUrl}`
);
return res.status(400).json({
error: "Falha ao descriptografar payload.",
details:
"Verifique se a chave de criptografia, IV ou AuthTag estão corretos e se o formato Base64 é válido.",
});
}
}
module.exports = {
encryptedPayloadMiddleware,
};