Skip to content

4. Trazabilidad en MongoDB Community

Temas del programa: 3.1 y 3.2

La auditoría nativa de MongoDB (--auditDestination) es exclusiva de MongoDB Enterprise y MongoDB Atlas (M10 o superior). No existe en MongoDB Community Edition, que es la edición de esta materia.

No se simula

Si una pregunta no puede responderse con Community, la respuesta es "No hay evidencia con esta edición" y su razón técnica

Se documenta

Un DBA reconoce y comunica la limitación, por ejemplo al evaluar si conviene licenciar Enterprise

Fuente: MongoDB Manual, “Auditing”

Según la documentación oficial (sin practicarlo en esta materia):

Eventos

Cambios de esquema (DDL), eventos de réplica y fragmentación, autenticación y autorización, y operaciones CRUD (con auditAuthorizationSuccess: true)

Destinos

Consola, syslog, archivo JSON o archivo BSON

Comprueba tu edición: db.adminCommand({ buildInfo: 1 }).modules. En Community devuelve []. En Enterprise incluiría "enterprise".

Database Profiler

Registra operaciones en la colección system.profile de cada base. Diseñado para desempeño, no para seguridad.

Bitácora de mongod

Documentos JSON con conexiones, autenticaciones, errores y cambios de configuración. Se consulta con getLog o en el archivo de systemLog.

Nivel del perfiladorComportamiento
0Apagado (por defecto)
1Más lentas que slowms (100 ms) o que cumplan un filtro
2Todas las operaciones

Fuente: MongoDB Manual, “Database Profiler”

Ejemplo guiado: preparar MongoDB con autenticación

Section titled “Ejemplo guiado: preparar MongoDB con autenticación”
use admin
db.createUser({ user: "admin", pwd: "Admin_Practica_2026", roles: ["root"] })
db.auth("admin", "Admin_Practica_2026")
use comercial
db.createUser({ user: "app_ventas", pwd: "Ventas_Practica_2026",
roles: [{ role: "readWrite", db: "comercial" }] })
db.clientes.insertMany([ /* 5 clientes ficticios de la Unidad 2 */ ])

Mismo orden que en la Unidad 2: use admin, createUser, db.auth. Sin db.auth la sesión no queda autenticada.

Ejemplo guiado: el incidente sin perfilado

Section titled “Ejemplo guiado: el incidente sin perfilado”
db.getProfilingStatus()
{ was: 0, slowms: 100, sampleRate: 1, ok: 1 }

La aplicación cambia el RFC:

Terminal window
mongosh "mongodb://app_ventas:Ventas_Practica_2026@127.0.0.1:27017/comercial?appName=app-ventas-web" --quiet --eval 'db.clientes.updateOne({ _id: 3 }, { $set: { rfc: "GAMA780912XXX" } })'

system.profile está vacía y la bitácora solo muestra la autenticación de app_ventas, no la operación.

mongodb://app_ventas:Ventas_Practica_2026@127.0.0.1:27017/comercial?appName=app-ventas-web
ParteSignificado
mongodb://Protocolo de MongoDB
app_ventas:Ventas_Practica_2026@Usuario y contraseña
127.0.0.1:27017Servidor y puerto
/comercialBase de datos donde se autentica y trabaja
?authSource=admin(Para el DBA) base donde está definido el usuario
?appName=app-ventas-webNombre de la aplicación. Aparece como appName en el perfilador
--quiet --eval '...'Ejecuta ese código y termina, sin mensaje de bienvenida

Usa comillas simples alrededor del código de --eval. Dentro de comillas dobles, la terminal interpreta $set como una variable vacía.

db.setProfilingLevel(2) // { was: 0, ... } ← devuelve el nivel ANTERIOR
db.getProfilingStatus() // { was: 2, slowms: 100, sampleRate: 1, ok: 1 }
{
op: 'update',
ns: 'comercial.clientes',
command: { q: { _id: 3 }, u: { '$set': { rfc: 'GAMA780912XYZ' } }, multi: false, upsert: false },
docsExamined: 1,
nModified: 1,
ts: ISODate('2026-10-25T22:52:45.909Z'),
client: '127.0.0.1',
appName: 'app-ventas-web',
user: 'app_ventas@comercial'
}

Fuente: MongoDB Manual, “Database Profiler Output”

CampoValor observadoResponde
opquery, update, remove¿Qué tipo de operación?
nscomercial.clientes¿Sobre qué colección?
commandFiltro y cambio aplicados¿Qué se pidió?
userapp_ventas@comercial¿Quién? (usuario y base de autenticación)
client / appName127.0.0.1 / app-ventas-web¿Desde dónde y con qué aplicación?
ts2026-10-25T22:52:45.909Z¿Cuándo?

No hay valor anterior del RFC. Tampoco los datos de un documento eliminado. Y el nivel 2 registra los filtros de las consultas, que pueden contener datos personales.

Ejemplo guiado: reducir volumen con un filtro

Section titled “Ejemplo guiado: reducir volumen con un filtro”
db.setProfilingLevel(0)
db.system.profile.drop() // exporta antes lo que necesites
db.setProfilingLevel(1, { filter: { op: { $in: ["insert", "update", "remove"] } } })
{
was: 1, slowms: 100, sampleRate: 1,
filter: { op: { '$in': [ 'insert', 'remove', 'update' ] } },
note: 'When a filter expression is set, slowms and sampleRate are not used ...',
ok: 1
}

Tras una consulta y una actualización de la aplicación, system.profile contiene solo el update.

1 MB

system.profile es una colección limitada. Al llenarse, sobrescribe lo más antiguo: la evidencia desaparece sola.

No persiste

Tras reiniciar el servicio, el nivel volvió a 0. Para que sea permanente: operationProfiling.mode en el archivo de configuración de mongod.

Desempeño

La documentación advierte que puede degradar el rendimiento, sobre todo en nivel 2.

Propósito

Analizar operaciones lentas. No fue diseñado como auditoría de seguridad.

Fuente: MongoDB Manual, “Database Profiler”

Cada línea es un documento JSON: t (fecha), s (severidad), c (componente: NETWORK, ACCESS, COMMAND), id, ctx (conexión, por ejemplo conn12), msg y attr.

¿Dónde queda?Cómo se controla
ArchivosystemLog.destination: file y systemLog.path en el archivo de configuración
Salida estándarSi no se configura systemLog (frecuente en contenedores)
Memoria del servidorSiempre: los 1024 eventos más recientes, consultables con getLog
function bitacora() {
return db.adminCommand({ getLog: "global" }).log.map(l => JSON.parse(l))
}
bitacora().filter(e => e.c === "ACCESS").slice(-5)

Fuente: MongoDB Manual, “Log Messages”, “getLog” y “Self-Managed Configuration File Options”

bitacora()
.filter(e => e.c === "ACCESS" && e.msg === "Failed to authenticate")
.map(e => ({ t: e.t, id: e.id, ctx: e.ctx, user: e.attr.user, client: e.attr.client, error: e.attr.error, result: e.attr.result }))
{
t: { '$date': '2026-10-25T22:52:58.122+00:00' },
id: 5286307, ctx: 'conn41', user: 'app_ventas', client: '127.0.0.1:37276',
error: 'AuthenticationFailed: SCRAM authentication failed, storedKey mismatch',
result: 18
},
{
..., user: 'usuario_inexistente',
error: 'UserNotFound: Could not find user "usuario_inexistente" for db "comercial"',
result: 11
}

Un acceso exitoso aparece como "Successfully authenticated" con result: 0 y el nombre de la aplicación en attr.doc.application.name.

Ejemplo guiado: correlacionar por conexión

Section titled “Ejemplo guiado: correlacionar por conexión”
{ ctx: 'conn31', msg: 'Profiler settings changed',
attr: { from: { level: 0, ... }, to: { level: 1, ..., filter: { ... } }, db: 'comercial' } }

El evento no dice quién hizo el cambio. Sí dice qué conexión:

bitacora().filter(e => e.ctx === "conn31" && e.msg === "Successfully authenticated")
.map(e => ({ t: e.t, ctx: e.ctx, user: e.attr.user, db: e.attr.db }))
[ { t: { '$date': '2026-10-25T22:52:56.959+00:00' }, ctx: 'conn31', user: 'admin', db: 'admin' } ]

La correlación solo es válida dentro del mismo periodo de ejecución: los números de conexión y la memoria de getLog se reinician con el servidor.

Cambios de privilegios: el límite más importante

Section titled “Cambios de privilegios: el límite más importante”
db.grantRolesToUser("app_ventas", [{ role: "dbAdmin", db: "comercial" }])

En la verificación del laboratorio, el cambio no apareció en system.profile ni en la bitácora de mongod.

Control compensatorio: instantáneas periódicas de privilegios.

db.getSiblingDB("auditoria").instantaneas_privilegios.insertOne({
momento: new Date(),
tomada_por: db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUsers,
usuarios: db.getUsers().users.map(u => ({ user: u.user, db: u.db, roles: u.roles }))
})
Pregunta¿Responde?
¿Cambiaron los privilegios de app_ventas?Sí
¿Qué rol se agregó?Sí: dbAdmin en comercial
¿Quién lo agregó?No
¿Cuándo exactamente?No, solo entre dos instantáneas

Un control compensatorio reduce un riesgo cuando el control preferido no existe, pero no lo sustituye y debe documentarse como tal.

EventoPostgreSQLMongoDB CommunityEnterprise / Atlas
Acceso exitoso y fallidoSíSí (bitácora)Documentado
Cambio de privilegiosSíNo (solo instantáneas)Documentado
Modificación de datosSíSí (perfilador)Documentado
Valor anteriorSí (disparador)NoNo lo describe el manual de MongoDB
Lectura de datos sensiblesCon pgaudit o allNivel 2, con su costoDocumentado
Cambio de configuraciónSíSí (ctx)Documentado

“Documentado” significa descrito en el manual oficial de MongoDB, no practicado en la materia

La edición, el perfilador, la bitácora de mongod, la correlación por conexión y las instantáneas de privilegios se practican en el Laboratorio 2 — Trazabilidad en MongoDB Community.

Continúa con 5. Conservación e interpretación de bitácoras.