staging: neutralizar credenciales OAuth vivas, nombres reales y tokens de sesion
La primera version solo invalidaba emails, fireBaseToken, campos telegram* y services.google.email. Auditando el staging aparecieron, sin neutralizar: - 42 users con services.google.accessToken/refreshToken/idToken -> credenciales OAuth VIVAS que dan acceso a la cuenta de Google REAL del usuario - 42 users con services.google.name/given_name/family_name/picture - 99 users con profile.name.first/last (nombre y apellidos reales) - tokens de sesion (services.resume.loginTokens) y verificationTokens Pasaron desapercibidos porque el bloque de verificacion tampoco los comprobaba. Se anaden los $unset y se amplia la verificacion para que falle si reaparecen. services.password.bcrypt se CONSERVA a proposito (hashes, no reversibles directamente, y son la unica forma de hacer QA de login: el volcado real no trae la cuenta de fixtures). Documentado en el propio script.
This commit is contained in:
parent
2e298f9ee5
commit
07993ffb91
1 changed files with 70 additions and 4 deletions
|
|
@ -58,6 +58,47 @@ const rGoogle = dbx.users.updateMany(
|
|||
[{ $set: { 'services.google.email': { $concat: [{ $toString: '$_id' }, '@invalid.test'] } } }]
|
||||
);
|
||||
|
||||
// 3b) users.services.google: purgar CREDENCIALES VIVAS y nombre/foto reales.
|
||||
// accessToken/refreshToken/idToken son credenciales OAuth que dan acceso a la cuenta
|
||||
// de Google REAL del usuario — lo más grave del volcado. name/given_name/family_name/
|
||||
// picture/id son PII directa. (El paso 3 solo invalidaba el email.)
|
||||
const rGoogleCreds = dbx.users.updateMany(
|
||||
{ 'services.google': { $exists: true } },
|
||||
{ $unset: {
|
||||
'services.google.accessToken': '',
|
||||
'services.google.refreshToken': '',
|
||||
'services.google.idToken': '',
|
||||
'services.google.expiresAt': '',
|
||||
'services.google.scope': '',
|
||||
'services.google.id': '',
|
||||
'services.google.name': '',
|
||||
'services.google.given_name': '',
|
||||
'services.google.family_name': '',
|
||||
'services.google.picture': '',
|
||||
'services.google.locale': '',
|
||||
'services.google.gender': ''
|
||||
}}
|
||||
);
|
||||
|
||||
// 3c) users.profile.name -> nombres y apellidos REALES (first/last). Se vacía el subdoc.
|
||||
const rProfile = dbx.users.updateMany(
|
||||
{ 'profile.name': { $exists: true } },
|
||||
{ $unset: { 'profile.name.first': '', 'profile.name.last': '', 'profile.email': '' } }
|
||||
);
|
||||
|
||||
// 3d) users.services.resume.loginTokens -> tokens de SESIÓN vivos (permiten suplantar
|
||||
// al usuario sin contraseña). Se vacía el array. Idem verificationTokens de email.
|
||||
const rTokens = dbx.users.updateMany(
|
||||
{ $or: [ { 'services.resume.loginTokens.0': { $exists: true } },
|
||||
{ 'services.email.verificationTokens.0': { $exists: true } } ] },
|
||||
{ $set: { 'services.resume.loginTokens': [], 'services.email.verificationTokens': [] } }
|
||||
);
|
||||
|
||||
// NOTA (decisión consciente, NO es un olvido): `services.password.bcrypt` se CONSERVA.
|
||||
// Son hashes bcrypt (no reversibles directamente) y son la única forma de hacer QA de
|
||||
// login en staging — no hay cuenta de fixtures en el volcado real. Si se quisiera cerrar
|
||||
// también ese vector, hay que crear antes una cuenta de pruebas dedicada.
|
||||
|
||||
// 4) subscriptions: quitar el destino de Telegram (chatId) de las subs de ese tipo
|
||||
const rSubs = dbx.subscriptions.updateMany(
|
||||
{ chatId: { $exists: true } }, { $unset: { chatId: '' } }
|
||||
|
|
@ -75,6 +116,9 @@ printjson({
|
|||
usersEmailsNeutralizados: rEmails.modifiedCount,
|
||||
usersTokensPurgados: rUserTokens.modifiedCount,
|
||||
googleEmailsInvalidados: rGoogle.modifiedCount,
|
||||
googleCredsPurgadas: rGoogleCreds.modifiedCount,
|
||||
profileNombresPurgados: rProfile.modifiedCount,
|
||||
tokensSesionPurgados: rTokens.modifiedCount,
|
||||
subsChatIdEliminados: rSubs.modifiedCount,
|
||||
colaCorreoBorrada: rMailQueue.deletedCount,
|
||||
colaJobsBorrada: rMailJobs.deletedCount
|
||||
|
|
@ -87,9 +131,31 @@ const emailMalo = dbx.users.countDocuments({ 'emails.address': { $exists: true
|
|||
const googleMalo = dbx.users.countDocuments({ 'services.google.email': { $exists: true, $not: /@invalid\.test$/ } });
|
||||
const subsConChat = dbx.subscriptions.countDocuments({ chatId: { $exists: true } });
|
||||
const colaCorreo = dbx.getCollection('__mailTimeQueue__').countDocuments({});
|
||||
printjson({ verificacion: { usersConFireBaseToken: conToken, usersConEmailRealVisible: emailMalo, usersConGoogleEmailReal: googleMalo, subsConChatId: subsConChat, correosEnCola: colaCorreo } });
|
||||
if (conToken === 0 && emailMalo === 0 && googleMalo === 0 && subsConChat === 0 && colaCorreo === 0) {
|
||||
print('OK: contactos neutralizados. Ninguna notificación puede alcanzar a un usuario real.');
|
||||
// Campos que la primera versión del script NO comprobaba — por eso la fuga pasó
|
||||
// desapercibida (42 users con credenciales OAuth vivas + 99 con nombre real).
|
||||
const googleCreds = dbx.users.countDocuments({ $or: [
|
||||
{ 'services.google.accessToken': { $exists: true } },
|
||||
{ 'services.google.refreshToken': { $exists: true } },
|
||||
{ 'services.google.idToken': { $exists: true } }
|
||||
] });
|
||||
const googleNom = dbx.users.countDocuments({ 'services.google.name': { $exists: true } });
|
||||
const profileNom = dbx.users.countDocuments({ $or: [
|
||||
{ 'profile.name.first': { $exists: true } },
|
||||
{ 'profile.name.last': { $exists: true } }
|
||||
] });
|
||||
const sesiones = dbx.users.countDocuments({ 'services.resume.loginTokens.0': { $exists: true } });
|
||||
printjson({ verificacion: {
|
||||
usersConFireBaseToken: conToken, usersConEmailRealVisible: emailMalo,
|
||||
usersConGoogleEmailReal: googleMalo, usersConGoogleCredsVivas: googleCreds,
|
||||
usersConGoogleNombreReal: googleNom, usersConProfileNombreReal: profileNom,
|
||||
usersConTokenSesion: sesiones, subsConChatId: subsConChat, correosEnCola: colaCorreo
|
||||
} });
|
||||
const pendientes = conToken + emailMalo + googleMalo + googleCreds + googleNom +
|
||||
profileNom + sesiones + subsConChat + colaCorreo;
|
||||
if (pendientes === 0) {
|
||||
print('OK: contactos neutralizados. Ninguna notificación puede alcanzar a un usuario real,');
|
||||
print(' y no quedan credenciales OAuth/sesión ni nombres reales.');
|
||||
print('NOTA: services.password.bcrypt se conserva a propósito (QA de login) — ver comentario arriba.');
|
||||
} else {
|
||||
print('AVISO: quedan campos sin neutralizar — revisar antes de exponer el staging.');
|
||||
print('AVISO: quedan ' + pendientes + ' campos sin neutralizar — revisar antes de exponer el staging.');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue