Migra dal codice salvato in locale al codice condiviso

This commit is contained in:
Giuseppe Raffa
2026-01-06 17:36:58 +01:00
parent 8a88c31c75
commit ff1566d36b
30 changed files with 8985 additions and 0 deletions

258
plugin/tools/crypt.js Normal file
View File

@@ -0,0 +1,258 @@
/**
* Modulo di crittografia centralizzato per MEB Plugin
* Supporta AES-256-GCM per file sensibili e log CSV
*
* BEST PRACTICES SICUREZZA ENTERPRISE:
* 1. La MASTER_KEY dovrebbe essere in variabile d'ambiente (process.env.MEB_MASTER_KEY)
* 2. In produzione usare AWS KMS, HashiCorp Vault, o Azure Key Vault
* 3. Rotazione periodica delle chiavi (ogni 90 giorni)
* 4. Separazione chiavi: una per users, una per logs_references, una per log files
* 5. Audit log di ogni accesso ai file sensibili
*
* GENERAZIONE CHIAVE SICURA:
* Esegui nel terminale: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
* Poi imposta: export MEB_MASTER_KEY="la_chiave_generata"
*/
const crypto = require("crypto");
const fs = require('fs');
const MASTER_KEY_HEX = process.env.CRYPTOKEY || null;
const TOKEN_CHARSET = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
const specialCharset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*_+-=';
/**
* Ottiene la chiave master (32 byte per AES-256)
* @returns {Buffer} Chiave di 32 byte
*/
function getMasterKey() {
if (!MASTER_KEY_HEX) {
throw new Error("MASTER_KEY non definita. Imposta MEB_MASTER_KEY nelle variabili d'ambiente.");
}
const key = Buffer.from(MASTER_KEY_HEX, 'hex');
if (key.length !== 32) {
throw new Error("MASTER_KEY deve essere di 32 byte (64 caratteri hex).");
}
return key;
}
/**
* Normalizza qualsiasi chiave custom a 32 byte Buffer per AES-256.
* Accetta chiavi di qualsiasi lunghezza/formato.
* @param {string|Buffer|null} customKey - Chiave custom o null per usare master key
* @returns {Buffer} Chiave di 32 byte
*/
function normalizeKey(customKey) {
if (!customKey) return getMasterKey();
if (typeof customKey === 'string') {
// Se è hex di 64 caratteri, convertilo direttamente
if (/^[0-9a-fA-F]{64}$/.test(customKey)) {
return Buffer.from(customKey, 'hex');
}
// Altrimenti hash SHA-256 per ottenere 32 byte
return crypto.createHash('sha256').update(customKey, 'utf8').digest();
}
if (Buffer.isBuffer(customKey)) {
if (customKey.length === 32) return customKey;
return crypto.createHash('sha256').update(customKey).digest();
}
throw new Error("customKey deve essere una stringa o un Buffer");
}
// ==================== GENERAZIONE TOKEN ====================
/**
* Genera un token esadecimale casuale UNICO ogni volta
* @param {number} bytes - Numero di byte (default 24 = 48 caratteri hex)
* @returns {string} Token esadecimale unico
*/
function generateToken(bytes = 24) {
return crypto.randomBytes(bytes).toString('hex');
}
/**
* Genera un token leggibile (senza caratteri ambigui come 0/O, 1/l/I)
* Più facile da comunicare verbalmente
* @param {number} length - Lunghezza del token (default 32)
* @returns {string} Token alfanumerico leggibile
*/
function generateReadableToken(length = 32) {
const bytes = crypto.randomBytes(length);
let result = '';
for (let i = 0; i < length; i++) {
result += TOKEN_CHARSET[bytes[i] % TOKEN_CHARSET.length];
}
return result;
}
/**
* Genera un token con caratteri speciali (più sicuro per chiavi sensibili)
* @param {number} length - Lunghezza del token (default 64)
* @returns {string} Token con caratteri speciali
*/
function generateSecureToken(length = 64) {
const bytes = crypto.randomBytes(length);
let result = '';
for (let i = 0; i < length; i++) {
result += specialCharset[bytes[i] % specialCharset.length];
}
return result;
}
// ==================== CRITTOGRAFIA OGGETTI JSON (per file sensibili) ====================
/**
* Cripta un oggetto JSON in Buffer binario (AES-256-GCM)
* Usato per telegram_users.json e logs_references.json
* @param {object} obj - Oggetto da criptare
* @param {string|Buffer|null} customKey - Chiave custom (opzionale)
* @returns {Buffer} Dati criptati [IV(12) + TAG(16) + CIPHERTEXT]
*/
// DISABILITATO: salviamo in chiaro
function encrypt(obj, customKey = null) {
const plaintext = Buffer.from(JSON.stringify(obj), 'utf8');
return plaintext; // ritorna direttamente il contenuto in chiaro
}
/**
* Decripta un Buffer in oggetto JSON
* @param {Buffer} buffer - Dati criptati
* @param {string|Buffer|null} customKey - Chiave custom (opzionale)
* @returns {object} Oggetto decriptato (array vuoto se fallisce)
*/
// DISABILITATO: leggiamo direttamente in chiaro
function decrypt(buffer, customKey = null) {
try {
if (!buffer) return [];
const content = buffer.toString('utf8');
return JSON.parse(content);
} catch (error) {
console.error('[decrypt] Errore:', error.message);
return [];
}
}
// ==================== CRITTOGRAFIA FILE LOG CSV ====================
/**
* Cripta un file CSV/testo sul disco
* @param {string} filePath - Percorso del file
* @param {string|Buffer|null} customKey - Chiave custom (qualsiasi lunghezza)
* @returns {boolean} True se successo
*/
// DISABILITATO: i file log rimangono sempre in chiaro
function encryptLog(filePath, customKey = null) {
try {
// Non fare nulla, lascia il file in chiaro
return true;
} catch (error) {
console.error('[encryptLog] Errore:', error.message);
return false;
}
}
/**
* Decripta un file CSV/testo e lo riscrive sul disco
* @param {string} filePath - Percorso del file criptato
* @param {string|Buffer|null} customKey - Chiave custom
* @returns {string|null} Contenuto decriptato o null se errore
*/
// DISABILITATO: i file sono già in chiaro
function decryptLog(filePath, customKey = null) {
try {
const content = fs.readFileSync(filePath, 'utf8');
return content; // ritorna contenuto in chiaro senza modifiche
} catch (error) {
console.error('[decryptLog] Errore:', error.message);
return null;
}
}
/**
* Decripta un file log e restituisce il contenuto SENZA modificare il file
* @param {string} filePath - Percorso del file criptato
* @param {string|Buffer|null} customKey - Chiave custom
* @returns {string|null} Contenuto decriptato o null se errore
*/
// DISABILITATO: i file sono già in chiaro
function decryptLogToMemory(filePath, customKey = null) {
try {
return fs.readFileSync(filePath, 'utf8');
} catch (error) {
console.error('[decryptLogToMemory] Errore:', error.message);
return null;
}
}
// ==================== GESTIONE FILE SENSIBILI (telegram_users, logs_references) ====================
/**
* Carica e decripta un file JSON sensibile
* Gestisce automaticamente file in chiaro (migrazione) e criptati
* @param {string} filePath - Percorso del file
* @param {object} defaultValue - Valore di default se file non esiste
* @returns {object} Dati decriptati
*/
function loadSecureFile(filePath, defaultValue = {}) {
try {
if (!fs.existsSync(filePath)) {
return defaultValue;
}
const content = fs.readFileSync(filePath, 'utf8').trim();
try {
return JSON.parse(content);
} catch (e) {
console.error(`[loadSecureFile] JSON non valido in ${filePath}:`, e.message);
return defaultValue;
}
} catch (error) {
console.error(`[loadSecureFile] Errore caricamento ${filePath}:`, error.message);
return defaultValue;
}
}
/**
* Cripta e salva un file JSON sensibile
* @param {string} filePath - Percorso del file
* @param {object} data - Dati da salvare
* @returns {boolean} True se successo
*/
function saveSecureFile(filePath, data) {
try {
const content = JSON.stringify(data, null, 2);
fs.writeFileSync(filePath, content, 'utf8');
return true;
} catch (error) {
console.error(`[saveSecureFile] Errore salvataggio ${filePath}:`, error.message);
return false;
}
}
module.exports = {
// Generazione token
generateToken,
generateReadableToken,
generateSecureToken,
// Crittografia oggetti JSON
encrypt,
decrypt,
// Crittografia file log
encryptLog,
decryptLog,
decryptLogToMemory,
// Gestione file sensibili
loadSecureFile,
saveSecureFile,
// Utility
normalizeKey
};