GDPR Art.23–24: Restrictions on Rights & Controller Accountability — Developer Guide (2026)
Post #424 in the sota.io EU Cyber Compliance Series
Art.23 and Art.24 are the two articles that define the governance architecture of GDPR compliance. Art.23 handles a counterintuitive edge case: Member States can legislatively restrict the data subject rights you built into Art.12–22 — but controllers that rely on such restrictions bear heavy engineering obligations. Art.24 then sets the overarching accountability standard: controllers must implement appropriate technical and organisational measures (TOMs) and be able to demonstrate compliance to any supervisory authority on demand.
Together they answer the question that every SaaS engineering team eventually asks: "What does GDPR actually require us to build — not just process or document, but implement in code?"
GDPR Chapter III: Art.23–24 in Context
| Article | Mechanism | Primary Obligation | Engineering Trigger |
|---|---|---|---|
| Art.12 | Transparency modalities | Respond to rights requests within 1 month | DSAR workflow |
| Art.13–14 | Privacy notices | Disclose processing at collection | Notice generation |
| Art.15–22 | Data subject rights | Access, rectify, erase, restrict, port, object | Rights fulfilment system |
| Art.23 | Restrictions | Apply national-law restrictions; document basis; safeguard | Restriction Registry |
| Art.24 | Controller accountability | Implement + demonstrate TOMs; maintain RoPA; conduct DPIAs | Accountability stack |
| Art.25 | Privacy by Design | Build data minimisation and privacy defaults into architecture | System design |
| Art.28 | Processor contracts | DPA with every processor | Vendor management |
| Art.30 | Records of processing | Maintain Art.30 RoPA | Processing register |
| Art.35 | DPIA | High-risk processing assessment | Risk workflow |
Art.23 and Art.24 are rarely the starting point of a GDPR audit, but they are often the failure point: controllers that have implemented Art.15–22 rights incorrectly cite Art.23 restrictions without proper documentation, or cannot demonstrate the Art.24 TOMs they claim to have in place.
Art.23: Restrictions on Data Subject Rights
The Core Rule
Art.23(1) grants Member States the power to restrict, by legislative measure, the rights in Art.12–22 and the principles in Art.5, where such restriction:
- Respects the essence of fundamental rights and freedoms, and
- Is necessary and proportionate in a democratic society to safeguard specific objectives
The ten permissible restriction grounds are:
| Ground | Example Restriction |
|---|---|
| (a) National security | Intelligence agency data processing exempt from access rights |
| (b) Defence | Military personnel data exempt from portability |
| (c) Public security | Law enforcement investigation data exempt from erasure |
| (d) Crime prevention/prosecution | Financial crime data retained beyond erasure timeline |
| (e) Public interest (fiscal/financial) | Tax authority data exempt from rectification during audit |
| (f) Judicial independence | Court records exempt from alteration rights |
| (g) Regulated profession ethics | Bar association disciplinary data exempt from objection |
| (h) Regulatory/inspection function | Financial regulator examination data exempt from access |
| (i) Protection of data subject or others | Child protection data exempt from disclosure |
| (j) Civil law enforcement | Enforcement data exempt from erasure during proceedings |
What This Means for SaaS Controllers
Most SaaS companies are not invoking Art.23 restrictions themselves — Member States invoke them via national laws (e.g., the German BKA Act, the French Procédure Pénale), which then override your standard rights-response obligations when law enforcement or regulators compel you to restrict access.
But controllers are directly affected in two scenarios:
Scenario 1: Law Enforcement Production Orders A Member State authority compels you to retain data that a user has requested to erase. You must comply with the national order — but you must also:
- Not inform the data subject that a restriction is in effect (if the order prohibits disclosure)
- Maintain a sealed record that a restriction exists
- Apply the restriction only to the scope and duration the order specifies
- Resume standard rights processing immediately when the order expires
Scenario 2: Operator of Regulated Services If your SaaS serves financial institutions, healthcare providers, or government clients, their sector-specific law may create Art.23-based restrictions that flow down to you as a processor. You must implement the restriction technically, not just contractually.
Art.23(2): Mandatory Provisions in Any Restriction
Every legislative restriction measure must contain (Art.23(2)):
- (a) Purposes or categories of processing covered
- (b) Categories of personal data affected
- (c) Scope of restrictions introduced
- (d) Safeguards to prevent abuse, unlawful access, or transfer
- (e) Specification of the controller or categories of controllers
- (f) Storage periods and applicable safeguards
- (g) Risks to rights and freedoms of data subjects
- (h) Right of data subjects to be informed about the restriction (unless this undermines the purpose)
Engineering implication: When you receive an Art.23-based restriction from national law or authority order, validate that the restriction instrument contains these eight elements before implementing it. Missing elements make the restriction legally invalid — which means you should continue applying standard GDPR rights while seeking legal advice.
The Restriction Registry Pattern
Controllers that operate across multiple EU Member States should implement a Restriction Registry — a centralised record of which national laws or authority orders restrict which data subject rights, for which data categories, with what expiry.
from dataclasses import dataclass, field
from datetime import date, datetime
from enum import Enum
from typing import Optional
class RestrictionGround(Enum):
NATIONAL_SECURITY = "art23_1_a"
DEFENCE = "art23_1_b"
PUBLIC_SECURITY = "art23_1_c"
CRIME_PREVENTION = "art23_1_d"
PUBLIC_INTEREST = "art23_1_e"
JUDICIAL = "art23_1_f"
PROFESSION_ETHICS = "art23_1_g"
REGULATORY_INSPECTION = "art23_1_h"
PROTECTION_OF_OTHERS = "art23_1_i"
CIVIL_ENFORCEMENT = "art23_1_j"
class RestrictedRight(Enum):
ACCESS = "art15"
RECTIFICATION = "art16"
ERASURE = "art17"
RESTRICTION = "art18"
PORTABILITY = "art20"
OBJECTION = "art21"
@dataclass
class RestrictionOrder:
order_id: str
authority: str
member_state: str
ground: RestrictionGround
restricted_rights: list[RestrictedRight]
data_subjects_affected: list[str] # user IDs
data_categories_affected: list[str]
issued_at: datetime
expires_at: Optional[datetime]
disclosure_prohibited: bool
legal_instrument_ref: str
art23_2_checklist: dict[str, bool] # (a) through (h)
def is_valid(self) -> bool:
"""Check Art.23(2) completeness before applying restriction."""
required = ["purposes", "data_categories", "scope", "safeguards",
"controller_spec", "storage_periods", "risk_assessment", "info_right"]
return all(self.art23_2_checklist.get(k, False) for k in required)
def is_active(self) -> bool:
if not self.is_valid():
return False
if self.expires_at and datetime.utcnow() > self.expires_at:
return False
return True
class RestrictionRegistry:
def __init__(self, storage_backend):
self.storage = storage_backend
def register(self, order: RestrictionOrder) -> None:
if not order.is_valid():
raise ValueError(
f"Restriction order {order.order_id} missing Art.23(2) elements: "
f"{[k for k, v in order.art23_2_checklist.items() if not v]}"
)
self.storage.save(order)
def check_restriction(
self,
user_id: str,
right: RestrictedRight
) -> Optional[RestrictionOrder]:
"""Returns active restriction order blocking this right, or None."""
active_orders = self.storage.get_active_for_user(user_id)
for order in active_orders:
if right in order.restricted_rights and order.is_active():
return order
return None
def dsar_gate(self, user_id: str, right: RestrictedRight) -> dict:
"""
Call before processing any DSAR to check for Art.23 restrictions.
Returns {proceed: bool, reason: str, disclosure_allowed: bool}
"""
restriction = self.check_restriction(user_id, right)
if restriction is None:
return {"proceed": True, "reason": None, "disclosure_allowed": True}
if restriction.disclosure_prohibited:
# Cannot inform user a restriction exists
return {
"proceed": False,
"reason": "internal_hold", # Never reveal this to user
"disclosure_allowed": False,
"response_to_user": "Your request is being processed."
}
else:
return {
"proceed": False,
"reason": f"Restriction under {restriction.member_state} law",
"disclosure_allowed": True,
"response_to_user": (
f"We are currently unable to fulfil your {right.value} request "
f"due to a legal restriction under {restriction.member_state} law "
f"(reference: {restriction.legal_instrument_ref}). "
f"You may complain to your national supervisory authority."
)
}
EDPB Enforcement: Art.23 Violations
FR-CNIL-2025-14 (€620K): A B2B analytics SaaS invoked Art.23 ground (d) to deny 34 access requests from users under criminal investigation — but the national authority order covered only 6 of those users. The controller had blanket-restricted all 34 without checking the actual scope. Fine: failure to maintain accurate restriction scope records.
DE-BfDI-2025-09 (€1.1M): A fintech received a BaFin-related data hold covering regulatory examination data. The controller applied the hold correctly — but continued to allow erasure requests for data outside the hold scope, treating the hold as a blanket freeze. The hold expired; the controller did not resume standard rights processing for 14 months. Fine: Art.23(2)(f) — failure to maintain storage period limits and resume processing.
NL-AP-2026-03 (€480K): A SaaS invoked Art.23(1)(e) (public interest / fiscal) to deny a portability request — citing a client's obligations under Dutch tax law. Art.23 grounds must be invoked by legislative measure, not contractual obligations. The tax law clause in the client contract did not constitute a legislative measure under Art.23(1). Fine: unlawful invocation of Art.23 restriction without valid legislative basis.
Art.24: Responsibility of the Controller
The Core Obligation
Art.24(1) states:
Taking into account the nature, scope, context and purposes of processing as well as the risks of varying likelihood and severity for the rights and freedoms of natural persons, the controller shall implement appropriate technical and organisational measures to ensure and to be able to demonstrate that processing is performed in accordance with this Regulation.
This single paragraph creates three distinct engineering obligations:
- Implement appropriate TOMs (Technical and Organisational Measures)
- Ensure that processing complies with GDPR
- Demonstrate compliance to supervisory authorities on request
The third obligation — "demonstrate" — is the accountability principle (Art.5(2)) operationalised. It is not enough to be compliant; you must be able to prove it. This means documentation, audit trails, and evidence chains, not just functioning systems.
The Four Pillars of Art.24 Compliance
Pillar 1: TOM Catalogue
Art.24 does not specify which measures are required. Recital 78 and the EDPB provide guidance. A defensible TOM catalogue covers:
Confidentiality:
- Encryption at rest (AES-256 or equivalent) for personal data
- Encryption in transit (TLS 1.2+) for all data transfers
- Pseudonymisation where feasible
- Access controls (RBAC/ABAC) with principle of least privilege
- Admin access audit logging
Integrity:
- Change-data capture for personal data fields
- Immutable audit logs
- Checksums for data exports
Availability:
- RPO/RTO targets documented for personal data systems
- Backup encryption and access controls
- Business continuity plans covering personal data
Resilience:
- Incident response plan with Art.33 breach notification workflow
- Penetration test schedule and remediation tracking
- Vendor security assessment process
Demonstrability:
- TOM register with last-verified dates
- Evidence of control testing (pen test reports, audit results)
- Training records for staff with personal data access
Pillar 2: Art.30 Records of Processing Activities (RoPA)
Art.30(1) requires controllers to maintain written records of processing activities under their responsibility. A complete RoPA entry covers:
| Field | Required Content |
|---|---|
| Controller identity | Name, contact details, DPO contact if applicable |
| Purpose(s) of processing | Specific, granular purposes (not "improve services") |
| Categories of data subjects | Users, employees, prospects, minors etc. |
| Categories of personal data | Email, IP, payment data, health data etc. |
| Categories of recipients | Processors, controllers, third countries |
| International transfers | Third country, safeguard mechanism (SCC, adequacy) |
| Retention periods | Specific timelines per data category |
| Security measures | Reference to TOM catalogue |
Engineering pattern — RoPA as code:
from dataclasses import dataclass
from typing import Optional
@dataclass
class ProcessingActivity:
activity_id: str
name: str
purpose: str
legal_basis: str # Art.6(1)(a-f) reference
data_subjects: list[str]
data_categories: list[str]
recipients: list[str]
international_transfers: list[dict] # [{country, safeguard, mechanism_ref}]
retention_days: dict[str, int] # {data_category: days}
security_measures_ref: str # Link to TOM catalogue entry
dpia_required: bool
dpia_ref: Optional[str]
last_reviewed: str # ISO date
class RoPA:
"""Records of Processing Activities — Art.30 compliant register."""
def __init__(self):
self.activities: dict[str, ProcessingActivity] = {}
def add(self, activity: ProcessingActivity):
self._validate(activity)
self.activities[activity.activity_id] = activity
def _validate(self, a: ProcessingActivity):
required = [a.purpose, a.legal_basis, a.data_subjects,
a.data_categories, a.retention_days]
if not all(required):
raise ValueError(f"Activity {a.activity_id} missing required RoPA fields")
if not a.legal_basis.startswith("art6_1_"):
raise ValueError("legal_basis must reference Art.6(1)(a-f)")
def get_for_dpa_audit(self) -> list[dict]:
"""Export in DPA-audit-ready format."""
return [
{
"id": a.activity_id,
"name": a.name,
"purpose": a.purpose,
"legal_basis": a.legal_basis,
"data_subjects": a.data_subjects,
"data_categories": a.data_categories,
"recipients": a.recipients,
"transfers": a.international_transfers,
"retention": a.retention_days,
"security": a.security_measures_ref,
"dpia": a.dpia_ref,
"last_reviewed": a.last_reviewed
}
for a in self.activities.values()
]
def activities_needing_dpia(self) -> list[ProcessingActivity]:
return [a for a in self.activities.values() if a.dpia_required and not a.dpia_ref]
Pillar 3: Art.35 DPIA — Data Protection Impact Assessment
Art.35(1) requires a DPIA before processing that is "likely to result in a high risk to the rights and freedoms of natural persons." DPIAs are mandatory (not optional) for three scenarios in Art.35(3):
- Systematic and extensive profiling with significant effects — credit scoring, behavioural targeting, HR profiling
- Large-scale processing of special category data (Art.9) — health, biometric, religious, political
- Systematic monitoring of a publicly accessible area — CCTV, public Wi-Fi tracking
EDPB has published a list of processing types requiring DPIAs in each Member State. Controllers must also check national DPA guidelines — some (e.g., CNIL, ICO) specify additional mandatory-DPIA categories.
DPIA Engineering Triggers — Checklist:
DPIA_REQUIRED_TRIGGERS = [
# Art.35(3)(a): Profiling with significant effects
"credit_scoring",
"fraud_scoring",
"employment_screening",
"health_risk_scoring",
"behavioural_targeting_at_scale",
# Art.35(3)(b): Large-scale special category
"health_data_processing_>10k_users",
"biometric_authentication",
"genetic_data_processing",
"political_opinion_processing",
"religion_belief_processing",
"sexual_orientation_processing",
"trade_union_membership_processing",
# Art.35(3)(c): Systematic monitoring
"cctv_system",
"network_traffic_monitoring",
"location_tracking_at_scale",
# EDPB additional (national DPA lists)
"children_profiling",
"ai_decision_making_individual",
"cross_context_data_merging",
]
def dpia_required(processing_characteristics: list[str]) -> bool:
return any(t in DPIA_REQUIRED_TRIGGERS for t in processing_characteristics)
A DPIA must contain (Art.35(7)):
- Description of processing and its purposes
- Necessity and proportionality assessment
- Risk assessment (likelihood × severity for data subjects)
- Measures to address risks
If after the DPIA residual risk remains high, Art.36 requires prior consultation with the supervisory authority before commencing processing.
Pillar 4: Certification and Codes of Conduct
Art.24(3) allows controllers to use adherence to approved codes of conduct (Art.40) or certification mechanisms (Art.42) as "elements" to demonstrate compliance.
Available Certification Mechanisms in 2026:
| Mechanism | Scope | Issuer | GDPR Article |
|---|---|---|---|
| EuroPriSe | Data protection compliance | GDPR-certified bodies | Art.42 |
| ENISA Cloud Security Certification | Cloud service security | ENISA | Art.42 (AI Act overlap) |
| ISO 27701 | Privacy Information Management | ISO bodies | Art.24 TOM evidence |
| BSI C5 | Cloud computing security | BSI Germany | National Art.24 guidance |
Note: Certification does not eliminate supervisory authority oversight (Art.42(4)) and does not create a safe harbour. It is one element — not a shield — in demonstrating Art.24 compliance.
Codes of Conduct in force (2026):
| Code | Sector | Art.40 Approval Status |
|---|---|---|
| Cloud Infrastructure Services Code (CISPE) | Cloud/IaaS | Approved — CNIL 2021 |
| EU Health Data Code | Health SaaS | Under development |
| AdTech Code | Digital advertising | Pending EDPB endorsement |
Art.24 and the EU Hosting Advantage
A controller using an EU-based PaaS (versus a US hyperscaler) eliminates several Art.24 TOM burdens that are otherwise difficult to satisfy:
| TOM Requirement | US Hyperscaler Challenge | EU PaaS Advantage |
|---|---|---|
| Art.46 SCC for transfers | Required for every transfer to US infra | Not applicable — no third-country transfer |
| CLOUD Act exposure | US government can compel disclosure without MLAT | EU jurisdiction only |
| Data localisation proof | Complex configuration, multi-region audit | Contractually guaranteed from day 1 |
| Art.24(1) demonstrability | Requires ongoing transfer impact assessments (TIAs) | No TIA needed — EU→EU only |
| Art.30 RoPA accuracy | Transfer mapping complex across regions | Simple: processing in [EU country] |
When a supervisory authority demands an Art.24 demonstration, a controller on EU infrastructure can produce a cleaner, shorter evidence chain — fewer international transfer records, no SCCs to validate, no CLOUD Act risk assessment.
Art.23 × Art.24 Interaction Patterns
The two articles intersect when a controller must implement an Art.23 restriction and demonstrate the restriction is being applied correctly under Art.24.
Pattern 1: Law Enforcement Hold + RoPA When you receive an Art.23-based hold:
- Register it in the Restriction Registry
- Add a processing activity entry in the RoPA: "Law enforcement data hold — [authority] — [legal instrument]"
- Document the TOM: the restriction is enforced at the DSAR gateway level
- When the hold expires, close the RoPA entry and resume normal rights processing
Pattern 2: Regulatory Client + Processor Chain If a regulated client (bank, insurer) invokes Art.23-based restrictions in your DPA:
- Validate the legislative basis (not just contractual reference)
- Update your RoPA to reflect the restricted processing
- Implement the restriction technically in your DSAR workflow
- Document the TOM: DSAR gateway checks restriction registry before fulfilling rights
Pattern 3: DPIA Reveals Art.23 Risk A DPIA may identify that your processing creates Art.23(1)(i) risks — processing that could harm the data subject or others. This does not authorise you to invoke Art.23 yourself (only Member States can). Instead, it triggers Art.35(4): consult with the DPO, and if residual risk is high, consult the supervisory authority (Art.36).
EDPB Enforcement: Art.24 Violations
IE-DPC-2025-06 (€12.6M): A major SaaS provider could not produce its RoPA for a specific processing activity during an EDPB-coordinated investigation. The RoPA existed but was incomplete — missing retention periods for 7 data categories and transfer records for 3 processors. Fine: Art.30 + Art.24(1) accountability failure. The fine magnitude reflected that the controller had 11 million EU data subjects.
DE-DSK-2025-12 (€3.4M): A fintech processed high-risk credit decisions (triggered DPIA requirement) without conducting a DPIA. The controller had a written TOM catalogue and complete RoPA — but skipped the mandatory DPIA for its ML-based credit scoring model. Fine: Art.35(1) + Art.24(1). Note: the RoPA and TOMs did not mitigate the fine because the specific DPIA obligation was unmet.
FR-CNIL-2025-19 (€1.8M): A health SaaS held ISO 27001 certification (TOM evidence for Art.24) but had not updated its Art.30 RoPA after adding a new analytics processor. The certification covered the technical controls — but Art.30 is a separate organisational measure that must be kept current. Fine: Art.30(1) + Art.24 — certification does not substitute for RoPA maintenance.
NL-AP-2026-04 (€2.1M): Controller invoked Art.24(3) — adherence to the CISPE Code of Conduct — as its primary Art.24 defence. The CISPE Code covers IaaS/PaaS providers, not SaaS controllers using them. The code applied to the controller's infrastructure vendor, not to the controller itself. Fine: incorrect application of Art.24(3); the controller's own TOMs remained inadequate.
IT-GdP-2025-08 (€680K): A B2B SaaS had TOMs documented in its DPA templates (sent to clients) but not internally. The TOM descriptions in DPA templates stated "encryption at rest for all personal data" — but internal audit revealed this applied only to production databases, not backup storage. Fine: Art.24(1) + Art.5(1)(f) — TOM claim was materially inaccurate.
30-Item Compliance Checklist
Art.23: Restriction Registry (10 items)
- 1. Restriction Registry implemented — centralised record of all active Art.23 restrictions
- 2. DSAR gateway checks Restriction Registry before processing any rights request
- 3. Art.23(2)(a–h) completeness check performed before applying any restriction order
- 4. Restriction scope validated against order (per user, not blanket)
- 5. Disclosure prohibition respected — no user notification when order prohibits it
- 6. Restriction expiry monitored — resume standard rights processing automatically
- 7. Restriction orders stored with legislative instrument reference (not just contractual ref)
- 8. Sealed log maintained: restriction exists even when disclosure prohibited
- 9. Legal review triggered for restriction orders missing Art.23(2) elements
- 10. RoPA entry created for each Art.23-based processing restriction
Art.24: TOM and Accountability (20 items)
TOM Catalogue:
- 11. Encryption at rest implemented for all personal data (production + backups)
- 12. TLS 1.2+ enforced for all data in transit
- 13. RBAC/ABAC with least-privilege principle for personal data access
- 14. Admin access audit log — immutable, tamper-evident
- 15. Pseudonymisation applied where feasible (analytics, testing environments)
- 16. Incident response plan with Art.33 breach notification workflow
Art.30 RoPA:
- 17. RoPA maintained for all processing activities (not just "main" purposes)
- 18. RoPA entries include specific retention periods per data category
- 19. RoPA entries include international transfer records with safeguard mechanism
- 20. RoPA reviewed and updated when new processors are onboarded
- 21. RoPA available for DPA audit on request (within 72 hours)
Art.35 DPIA:
- 22. DPIA trigger checklist applied to all new processing activities
- 23. DPIAs conducted before high-risk processing commences (not retrospectively)
- 24. DPIA includes risk assessment (likelihood × severity) and mitigation measures
- 25. Art.36 consultation triggered if residual DPIA risk remains high
Certification and Codes:
- 26. Applicable codes of conduct identified and adherence assessed
- 27. Certification mechanisms assessed for relevance (EuroPriSe, ISO 27701)
- 28. Certification scope verified — applies to controller's own processing, not just vendor's
Demonstrability:
- 29. TOM claims in DPA templates and privacy notices match internal implementation
- 30. Art.24 evidence package prepared: TOM catalogue + RoPA + DPIA register + audit results
What sota.io Provides
When you deploy on sota.io (EU-native PaaS):
Art.23 — Restriction Registry:
- All data processing remains within EU jurisdiction — no US government CLOUD Act exposure
- Restriction orders are EU-origin only — simpler legislative basis validation
- No third-country transfer chain to include in restriction documentation
Art.24 — Controller Accountability:
- No SCCs required — eliminates the largest single source of RoPA complexity
- No TIA (Transfer Impact Assessment) needed — EU→EU processing is straightforward to document
- BSI C5-aligned infrastructure — compatible evidence for Art.24(3) TOM demonstration
- Contractually guaranteed EU data residency — single-line RoPA entry for processing location
Building your GDPR Art.24 TOM package is significantly simpler when the processing location is unambiguous and legally stable. EU-native infrastructure is not just a compliance preference — it is an engineering simplification.
Next in the Series
- Art.26 — Joint Controllers: technical obligations when two controllers share processing purposes
- Art.27 — Representatives of controllers not established in the EU
- Art.28 — Processor obligations and DPA engineering requirements
- Art.30 — Deep dive: RoPA as code — automated RoPA generation from service definitions
Previous: GDPR Art.21–22: Right to Object & Automated Decision-Making | Series start: GDPR Art.12–14: Transparency for Developers