The UK's Legal Aid Agency discovered attackers had exfiltrated around 250,000 records dating back to 1978. This breach included nearly five decades of personal data that should never have been accessible. Detected on April 23, 2025, the breach involved information from both the Legal Aid Agency and HM Prison and Probation Service, with most stolen records covering offenders supervised between 2016 and 2017.
Here's the uncomfortable truth: your organization probably has the same problem. Historical data accumulates because nobody owns the deletion process. This guide shows you how to identify, classify, and systematically purge records that have outlived their retention period before they become breach payload.
What You Need Before Starting
Access and Authority:
- Write permissions on databases and archival storage
- Authority to execute deletion operations (get this in writing from legal and compliance)
- Service account credentials for automated retention jobs
Documentation:
- Current data inventory showing all systems storing personal data
- Retention Rule definitions for each data category (draft them with legal if needed)
- Records Disposition Schedule mapping retention periods to regulatory requirements (GDPR Article 5(1)(e), UK General Data Protection Regulation, sector-specific rules)
Technical Prerequisites:
- Database query access to identify record ages
- Backup and recovery procedures (test these first)
- Logging infrastructure to create an audit trail of all deletion operations
Team Coordination:
- Legal sign-off on retention periods and deletion authority
- IT operations support for database access and backup verification
- Business unit confirmation that records flagged for deletion aren't under Preservation Order
Step-by-Step Implementation
Phase 1: Discovery and Classification
Map Your Data Landscape:
Run age-distribution queries across every system storing personal data. You're looking for records older than your defined retention periods.
SELECT
YEAR(created_date) as record_year,
COUNT(*) as record_count,
data_category
FROM legal_aid_applications
GROUP BY YEAR(created_date), data_category
ORDER BY record_year;
If you find records from the 1970s, 1980s, or 1990s, ask why they're still accessible. The Legal Aid Agency discovered records from 1978 in systems that attackers could reach. Don't let that be your breach disclosure.
Build a Deletion Candidate List:
Create a staging table of records that exceed retention periods:
CREATE TABLE deletion_candidates AS
SELECT
record_id,
created_date,
data_category,
retention_period_days,
DATEDIFF(CURRENT_DATE, created_date) as age_days
FROM all_personal_data_records
WHERE DATEDIFF(CURRENT_DATE, created_date) > retention_period_days
AND legal_hold_flag = FALSE;
The Preservation Order check is critical. Never purge records subject to litigation, investigation, or regulatory inquiry.
Classify by Sensitivity:
Sort deletion candidates by data sensitivity. Records containing National Insurance numbers, financial details, or criminal justice information require Secure Deletion methods, not just logical deletion.
Phase 2: Validation and Business Review
Cross-Reference with Active Processes:
Before you delete anything, confirm the records aren't needed for ongoing cases or appeals. The Legal Aid Agency breach included records from the Very High Cost Cases scheme dating from 1981 to 2004. If those cases were closed and retention periods expired, they should have been purged years ago.
Run this check:
SELECT dc.record_id, am.case_status, am.last_activity_date
FROM deletion_candidates dc
LEFT JOIN active_matters am ON dc.record_id = am.record_id
WHERE am.case_status IN ('Open', 'Under Appeal', 'Pending Review');
Remove any matches from your deletion candidate list.
Sample and Verify:
Pull a random sample of 100 records from your deletion candidate list. Manually verify they meet deletion criteria. If more than 5% don't qualify, your query logic needs refinement.
Phase 3: Secure Deletion Execution
Choose Your Deletion Method:
For structured database records containing sensitive personal data, use multi-pass Data Sanitization:
UPDATE legal_aid_records
SET
name = 'REDACTED',
address = 'REDACTED',
national_insurance_number = NULL,
financial_details = NULL,
updated_date = CURRENT_TIMESTAMP,
updated_by = 'RETENTION_PURGE_JOB'
WHERE record_id IN (SELECT record_id FROM deletion_candidates);
DELETE FROM legal_aid_records
WHERE record_id IN (SELECT record_id FROM deletion_candidates);
For unstructured data, use Cryptographic Erasure if the data was encrypted at rest. Destroy the encryption keys rather than attempting to locate and delete every file copy.
Execute in Batches:
Don't delete 250,000 records in one transaction. Batch deletions in groups of 1,000-5,000 to avoid locking tables and allow rollback if something goes wrong:
DECLARE @BatchSize INT = 5000;
WHILE EXISTS (SELECT 1 FROM deletion_candidates)
BEGIN
DELETE TOP (@BatchSize) FROM legal_aid_records
WHERE record_id IN (SELECT record_id FROM deletion_candidates);
DELETE TOP (@BatchSize) FROM deletion_candidates;
WAITFOR DELAY '00:00:10';
END
Log Everything:
Write every deletion operation to an immutable audit log:
INSERT INTO data_purge_audit_log
(record_id, deletion_date, deleted_by, retention_basis, approver)
SELECT
record_id,
CURRENT_TIMESTAMP,
'AUTOMATED_RETENTION_JOB',
'Retention period expired per Records Disposition Schedule',
'[email protected]'
FROM deletion_candidates;
This log is your evidence of Defensible Disposal if a Supervisory Authority asks why you deleted data.
Validation: How to Verify It Works
Confirm Deletion:
Re-run your age-distribution query. Records older than retention periods should be gone:
SELECT COUNT(*)
FROM legal_aid_applications
WHERE DATEDIFF(CURRENT_DATE, created_date) > retention_period_days
AND legal_hold_flag = FALSE;
Check Backups:
Your backup Retention Rule must align with data retention rules. If you purged records from production but they're still in 7-year backups, you haven't reduced breach exposure.
Set Time-to-Live (TTL) values on backups:
aws s3api put-bucket-lifecycle-configuration \
--bucket legal-aid-backups \
--lifecycle-configuration '{
"Rules": [{
"Id": "ExpireOldBackups",
"Status": "Enabled",
"Expiration": {"Days": 2555}
}]
}'
Audit Trail Verification:
Confirm your purge audit log captured every deletion:
SELECT COUNT(DISTINCT record_id) FROM data_purge_audit_log
WHERE deletion_date >= '2025-01-01';
This count should match the number of records you deleted.
Maintenance: Ongoing Tasks
Automate Retention Enforcement:
Schedule a monthly job that identifies and purges expired records:
0 2 1-7 * 0 /usr/local/bin/run_retention_purge.sh
Your purge script should email a summary report to legal and compliance teams showing what was deleted and why.
Quarterly Retention Review:
Every 90 days, review your Records Disposition Schedule with legal counsel. Regulatory requirements change. GDPR Article 5(1)(e) requires you to keep data "no longer than necessary". If you can justify a shorter retention period, implement it.
Annual Breach Surface Assessment:
Once per year, run the age-distribution query and ask: "If attackers exfiltrated our oldest 10% of records, what would they get?" If the answer includes data from before 2010, you've got historical accumulation drift.
The Legal Aid Agency learned this lesson after attackers stole records spanning 1978 to 2017. Your job is to prevent that headline by treating retention periods as mandatory deletion deadlines, not aspirational guidelines.





