mirror of
https://github.com/bitwarden/server
synced 2025-12-30 07:03:42 +00:00
[Innovation Sprint] Phishing Detection (#5516)
* Initial stubbing out of the phishing service * Add the phishing domain controller * Add changes for the phishing domain get * Add distributed cache to the phishing domain Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * Rename the variable name Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * Removed IPhishingDomainService * Feature/phishing detection cronjob (#5512) * Added caching to EF implementation. Added error handling and logging * Refactored update method to use sqlbulkcopy instead of performing a round trip for each new insert * Initial implementation for quartz job to get list of phishing domains * Updated phishing domain settings to be its own interface * Add phishing domain detection with checksum-based updates * Updated auth for phishing domain endpoints to either require api, or licensing claims to support both web and browser clients, and selfhost api clients * [Innovation Sprint] Updated Phishing domains to rely on blob storage (#5517) * Updated phishing detection data layer to rely on azure blob storage instead of sql server * dotnet format * Took rider refactors * Ensuring phishing.testcategory.com exists to test against * Added redis to dev's docker-compose * Removed redis from cloud profile * Remove the Authorize attribute * error whitespace fix whitespace formatting * error WHITESPACE: Fix whitespace formatting * Wrapped phishing detection feature behind feature flag (#5532) * Increased timeout for fetching source list a bunch * Removed PhishingDomains policy --------- Signed-off-by: Cy Okeke <cokeke@bitwarden.com> Co-authored-by: Cy Okeke <cokeke@bitwarden.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.PhishingDomainFeatures;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Repositories.Implementations;
|
||||
|
||||
public class AzurePhishingDomainRepository : IPhishingDomainRepository
|
||||
{
|
||||
private readonly AzurePhishingDomainStorageService _storageService;
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly ILogger<AzurePhishingDomainRepository> _logger;
|
||||
private const string _domainsCacheKey = "PhishingDomains_v1";
|
||||
private const string _checksumCacheKey = "PhishingDomains_Checksum_v1";
|
||||
private static readonly DistributedCacheEntryOptions _cacheOptions = new()
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24),
|
||||
SlidingExpiration = TimeSpan.FromHours(1)
|
||||
};
|
||||
|
||||
public AzurePhishingDomainRepository(
|
||||
AzurePhishingDomainStorageService storageService,
|
||||
IDistributedCache cache,
|
||||
ILogger<AzurePhishingDomainRepository> logger)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_cache = cache;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ICollection<string>> GetActivePhishingDomainsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var cachedDomains = await _cache.GetStringAsync(_domainsCacheKey);
|
||||
if (!string.IsNullOrEmpty(cachedDomains))
|
||||
{
|
||||
_logger.LogDebug("Retrieved phishing domains from cache");
|
||||
return JsonSerializer.Deserialize<ICollection<string>>(cachedDomains) ?? [];
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to retrieve phishing domains from cache");
|
||||
}
|
||||
|
||||
var domains = await _storageService.GetDomainsAsync();
|
||||
|
||||
try
|
||||
{
|
||||
await _cache.SetStringAsync(
|
||||
_domainsCacheKey,
|
||||
JsonSerializer.Serialize(domains),
|
||||
_cacheOptions);
|
||||
_logger.LogDebug("Stored {Count} phishing domains in cache", domains.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to store phishing domains in cache");
|
||||
}
|
||||
|
||||
return domains;
|
||||
}
|
||||
|
||||
public async Task<string> GetCurrentChecksumAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var cachedChecksum = await _cache.GetStringAsync(_checksumCacheKey);
|
||||
if (!string.IsNullOrEmpty(cachedChecksum))
|
||||
{
|
||||
_logger.LogDebug("Retrieved phishing domain checksum from cache");
|
||||
return cachedChecksum;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to retrieve phishing domain checksum from cache");
|
||||
}
|
||||
|
||||
var checksum = await _storageService.GetChecksumAsync();
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(checksum))
|
||||
{
|
||||
await _cache.SetStringAsync(
|
||||
_checksumCacheKey,
|
||||
checksum,
|
||||
_cacheOptions);
|
||||
_logger.LogDebug("Stored phishing domain checksum in cache");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to store phishing domain checksum in cache");
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
public async Task UpdatePhishingDomainsAsync(IEnumerable<string> domains, string checksum)
|
||||
{
|
||||
var domainsList = domains.ToList();
|
||||
await _storageService.UpdateDomainsAsync(domainsList, checksum);
|
||||
|
||||
try
|
||||
{
|
||||
await _cache.SetStringAsync(
|
||||
_domainsCacheKey,
|
||||
JsonSerializer.Serialize(domainsList),
|
||||
_cacheOptions);
|
||||
|
||||
await _cache.SetStringAsync(
|
||||
_checksumCacheKey,
|
||||
checksum,
|
||||
_cacheOptions);
|
||||
|
||||
_logger.LogDebug("Updated phishing domains cache after update operation");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to update phishing domains in cache");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user