Compliance considerations, audit logging, and event tracking for Bitwarden Server
Bitwarden Server provides comprehensive audit logging and event tracking capabilities to support compliance requirements and security monitoring. This guide covers event logging, audit trails, and compliance considerations.
-- Recent user authentication eventsSELECT e.Date, e.Type, u.Email, e.IpAddress, e.DeviceTypeFROM [dbo].[Event] eJOIN [dbo].[User] u ON e.UserId = u.IdWHERE e.Type IN (1000, 1005) -- LoggedIn, FailedLogInAND e.Date >= DATEADD(day, -30, GETDATE())ORDER BY e.Date DESC;
Organization Changes:
-- Organization user management eventsSELECT e.Date, e.Type, u.Email AS Actor, ou.Email AS TargetUser, o.Name AS OrganizationFROM [dbo].[Event] eJOIN [dbo].[Organization] o ON e.OrganizationId = o.IdJOIN [dbo].[User] u ON e.ActingUserId = u.IdLEFT JOIN [dbo].[OrganizationUser] ou ON e.OrganizationUserId = ou.IdWHERE e.Type BETWEEN 4000 AND 4999AND e.Date >= DATEADD(day, -90, GETDATE())ORDER BY e.Date DESC;
Sensitive Data Access:
-- Track password copies and autofillSELECT e.Date, e.Type, u.Email, c.Name AS ItemName, e.IpAddressFROM [dbo].[Event] eJOIN [dbo].[User] u ON e.UserId = u.IdJOIN [dbo].[Cipher] c ON e.CipherId = c.IdWHERE e.Type IN (2007, 2009) -- CopiedPassword, AutofilledAND e.Date >= DATEADD(day, -7, GETDATE())ORDER BY e.Date DESC;
-- Export compliance audit logSELECT CONVERT(varchar, e.Date, 120) AS Timestamp, et.Name AS EventType, u.Email AS User, e.IpAddress, e.DeviceType, o.Name AS Organization, c.Name AS ItemFROM [dbo].[Event] eJOIN [dbo].[EventType] et ON e.Type = et.IdLEFT JOIN [dbo].[User] u ON e.UserId = u.IdLEFT JOIN [dbo].[Organization] o ON e.OrganizationId = o.IdLEFT JOIN [dbo].[Cipher] c ON e.CipherId = c.IdWHERE e.Date BETWEEN '2026-01-01' AND '2026-12-31'ORDER BY e.DateFOR CSV;
Export to JSON:
// API endpoint for event exportpublic async Task<IEnumerable<EventResponseModel>> GetEvents( DateTime start, DateTime end){ var events = await _eventRepository.GetManyByOrganizationAsync( organizationId, start, end); return events.Select(e => new EventResponseModel(e));}
-- Delete user data (GDPR Article 17)BEGIN TRANSACTION;-- Delete user ciphersDELETE FROM [dbo].[Cipher] WHERE UserId = @UserId;-- Delete user eventsDELETE FROM [dbo].[Event] WHERE UserId = @UserId;-- Delete user accountDELETE FROM [dbo].[User] WHERE Id = @UserId;COMMIT TRANSACTION;
-- Detect unusual access patterns (HIPAA 164.312(b))SELECT u.Email, COUNT(*) AS AccessCount, COUNT(DISTINCT e.CipherId) AS UniqueItemsAccessedFROM [dbo].[Event] eJOIN [dbo].[User] u ON e.UserId = u.IdWHERE e.Type IN (2006, 2007, 2009) -- Viewed, Copied, AutofilledAND e.Date >= DATEADD(hour, -1, GETDATE())GROUP BY u.EmailHAVING COUNT(*) > 100; -- Threshold for suspicious activity
Integrity Controls:
-- Verify data integrityDBCC CHECKDB (vault) WITH NO_INFOMSGS;-- Check for unauthorized modificationsSELECT c.Id, c.Name, c.RevisionDate, e.Date AS LastEventDateFROM [dbo].[Cipher] cLEFT JOIN [dbo].[Event] e ON c.Id = e.CipherId AND e.Type = 2001 -- Cipher_Updated AND e.Date = ( SELECT MAX(Date) FROM [dbo].[Event] WHERE CipherId = c.Id AND Type = 2001 )WHERE c.RevisionDate > ISNULL(e.Date, c.CreationDate);
-- Service availability trackingSELECT CAST(Date AS DATE) AS Day, COUNT(*) AS Requests, COUNT(CASE WHEN Type = 1000 THEN 1 END) AS SuccessfulLogins, COUNT(CASE WHEN Type = 1005 THEN 1 END) AS FailedLoginsFROM [dbo].[Event]WHERE Type IN (1000, 1005)AND Date >= DATEADD(day, -30, GETDATE())GROUP BY CAST(Date AS DATE)ORDER BY Day;
-- Configure retention period (90 days recommended minimum)DECLARE @RetentionDays INT = 90;DELETE FROM [dbo].[Event]WHERE Date < DATEADD(day, -@RetentionDays, GETDATE());
-- Archive old events to separate tableBEGIN TRANSACTION;INSERT INTO [dbo].[EventArchive]SELECT * FROM [dbo].[Event]WHERE Date < DATEADD(year, -1, GETDATE());DELETE FROM [dbo].[Event]WHERE Date < DATEADD(year, -1, GETDATE());COMMIT TRANSACTION;
CREATE PROCEDURE GenerateAccessReport @StartDate DATETIME, @EndDate DATETIMEASBEGIN SELECT u.Email, COUNT(DISTINCT CAST(e.Date AS DATE)) AS DaysActive, COUNT(*) AS TotalEvents, COUNT(CASE WHEN e.Type = 1000 THEN 1 END) AS Logins, MIN(e.Date) AS FirstActivity, MAX(e.Date) AS LastActivity FROM [dbo].[Event] e JOIN [dbo].[User] u ON e.UserId = u.Id WHERE e.Date BETWEEN @StartDate AND @EndDate GROUP BY u.Email ORDER BY TotalEvents DESC;END;
Security Incident Report:
CREATE PROCEDURE GenerateSecurityIncidentReport @StartDate DATETIME, @EndDate DATETIMEASBEGIN -- Failed login attempts SELECT 'Failed Logins' AS IncidentType, u.Email, COUNT(*) AS Occurrences, MAX(e.Date) AS LastOccurrence FROM [dbo].[Event] e LEFT JOIN [dbo].[User] u ON e.UserId = u.Id WHERE e.Type = 1005 AND e.Date BETWEEN @StartDate AND @EndDate GROUP BY u.Email HAVING COUNT(*) > 5 UNION ALL -- Bulk data exports SELECT 'Vault Export' AS IncidentType, u.Email, COUNT(*) AS Occurrences, MAX(e.Date) AS LastOccurrence FROM [dbo].[Event] e JOIN [dbo].[User] u ON e.UserId = u.Id WHERE e.Type IN (1006, 4007) AND e.Date BETWEEN @StartDate AND @EndDate GROUP BY u.Email;END;