2026-05-28·5 min read·sota.io Team

Secure by Default: CRA Art.12(2) Hardening Guide for SaaS Developers

Post #2 in the sota.io EU ENISA Secure by Design Series

Secure by Default CRA Art.12(2) Hardening Guide for SaaS Developers

The EU Cyber Resilience Act's Article 12(2) states that products with digital elements must be "placed on the market with a secure default configuration". That sentence carries €15 million in potential liability for SaaS companies that ship with insecure defaults.

In Post #1 of this series, we covered the five pillars of ENISA's Secure by Design framework. This post goes one layer deeper: what does "secure by default" mean in practice, which default configurations expose you to CRA non-compliance, and how do you run a systematic hardening audit before the September 2026 deadline?

The answer is more granular than most compliance checklists suggest. "Secure defaults" under the CRA is not a single configuration — it is a principle applied across authentication, network exposure, data handling, logging, and third-party integrations. ENISA has published specific guidance on each domain. This post maps that guidance to the implementation decisions your engineering team makes today.


What CRA Art.12(2) Actually Requires

Article 12(2) of the CRA reads:

"Products with digital elements shall be placed on the market with a secure default configuration, including the possibility to reset the product to its original state."

This is paired with Article 12(3), which specifies that products shall:

Reading these together, the CRA's "secure by default" requirement has four operational dimensions:

1. Hardened initial state — the product as shipped (or as provisioned in SaaS terms) must not require the user to take security actions before it is safe. Security configuration should be opt-out, not opt-in.

2. No security-downgrade without explicit consent — convenience features that weaken security (e.g., disabling MFA, enabling HTTP, relaxing password policies) must require deliberate action, not be enabled by default.

3. Reset capability — users must be able to return the product to its secure initial state. This matters for SaaS in the context of tenant isolation: a security-misconfigured tenant should be resettable without data spillage.

4. Proportionality to use case — defaults must be appropriate for the intended use and foreseeably intended misuse. A product marketed to enterprises must apply enterprise-grade defaults even if its consumer edition ships with weaker settings.

ENISA's interpretation in its SbD guidelines (ENISA-GOOD-PRACTICES-SBDI-v1.0, updated Q1 2026) adds one critical gloss: the burden of security must not be placed on the user. If making the product secure requires the user to know what a cipher suite is, the default is not secure.


The Five Secure-by-Default Domains

Domain 1: Authentication Defaults

Authentication is where most SaaS products fail the secure-by-default test. Common insecure default patterns:

Insecure DefaultCRA ImpactSecure Alternative
Password-only login enabled for admin accountsArt.12(3)(a) — unauthorized accessMFA required for all admin roles from first login
Default passwords (admin/admin, changeme)Art.12(2) — insecure initial stateForce credential rotation on first access
Session tokens with no expiryArt.12(3)(a) — persistent access vector24h max session, 1h idle timeout
Password reset via email with no expiryArt.12(3)(a) — account takeover15-minute reset token expiry
API keys without scope restrictionArt.12(3)(b) — excess privilegeKeys scoped to minimum required actions
OAuth redirect URIs: wildcard *Art.12(3)(a) — redirect attacksExplicit allowlist, no wildcards
"Remember this device" enabled by defaultArt.12(2) — security bypassOpt-in, device trust with audit trail

ENISA Recommendation — Authentication Defaults:

CRA Compliance Check — Authentication:

□ No default or static credentials exist in shipped configuration
□ MFA is available and enabled-by-default for admin roles
□ Session tokens expire automatically
□ Password reset tokens have a defined and short TTL
□ API keys require explicit scope selection at creation
□ All admin actions are logged with user identity and timestamp

Domain 2: Network Exposure Defaults

The principle of minimized attack surface (Art.12(3)(c)) directly governs what services a product exposes by default. Every unnecessarily exposed port, protocol, or service is a CRA violation surface.

Insecure network defaults that remain common in SaaS products:

Unrestricted CORS: Many SaaS APIs ship with Access-Control-Allow-Origin: * to simplify developer integration. Under the CRA, this is a default that enables cross-site attacks without user consent. Secure default: allowlist or specific origin enforcement.

Unencrypted internal traffic: Microservices communicating over HTTP internally is common in Kubernetes deployments. CRA Art.12(3)(b) requires confidentiality of transmitted data. ENISA SbD guidance extends this to internal traffic: mTLS or service mesh encryption should be the default.

Debug and management endpoints publicly exposed: /metrics, /health/verbose, /admin, /debug endpoints often expose internal topology, credentials, and performance data. CRA compliance requires these to be either disabled or access-controlled by default.

Wildcard DNS records: *.yoursaas.io DNS pointing to shared infrastructure allows tenant-enumeration and subdomain takeover if any subdomain lapses. Secure default: explicit DNS records per tenant, no wildcards.

Open egress: Default allow-all outbound traffic enables exfiltration and C2 communication. CRA's requirement to "limit the impact of an incident" implies that egress should be restricted to known destinations by default.

ENISA Network Hardening Defaults:

Inbound:
- TLS 1.2 minimum, TLS 1.3 preferred — enforced at load balancer
- HTTP → HTTPS redirect with HSTS preloading (max-age=31536000)
- Cipher suite allowlist: AES-256-GCM, ChaCha20-Poly1305 only
- No SSLv3, TLS 1.0, TLS 1.1
- Public ports: 443 only (plus 22 for management, access-controlled)

Internal:
- mTLS between service mesh components
- Network policies: default-deny with explicit allow rules
- No inter-namespace traffic without explicit policy

Egress:
- Allowlist: known third-party APIs, update endpoints
- Block: Tor exit nodes, known C2 IPs (threat intel feed)
- Rate limit: prevent large-volume data transfers

The EU-Sovereign Infrastructure Wrinkle:

Many SaaS products use US-based CDNs (Cloudflare, Fastly, AWS CloudFront) as their default network edge. ENISA's SbD guidance does not explicitly ban this — but it does require that TLS termination occurs before data leaves your control boundary. If your CDN terminates TLS and has access to plaintext request bodies containing PII, you have a CLOUD Act exposure that ENISA's threat model explicitly covers.

Secure-by-default for EU-exposed data means considering where TLS terminates relative to jurisdictional control. EU-sovereign edge options (Cloudflare EU Data Localization Suite with binding commitments, or EU-native CDN providers like Bunny.net or Gcore) allow you to enforce the CRA's data confidentiality requirements without jurisdictional ambiguity.


Domain 3: Data Handling Defaults

CRA Art.12(3)(b) requires products to "protect the confidentiality, integrity, and availability of data." ENISA translates this into four concrete default requirements for data handling.

Encryption at rest: Data stored in databases, object storage, and backup systems must be encrypted by default. AES-256 is the ENISA minimum. Encryption must use keys the product operator controls — not keys managed solely by the cloud provider. If your default RDS instance uses AWS-managed KMS keys with no customer-managed key option, that default does not meet the spirit of CRA data sovereignty requirements.

Encryption in transit: Already covered in network defaults, but applies specifically to database connections, message queue connections, and backup transfers. Default: require TLS for all external and internal database connections. No sslmode=disable in production configurations.

Data minimization defaults: CRA Art.12(3)(d) requires that products limit data to what is necessary. In practice this means: default logging configurations should not capture request bodies, user-entered data, or session tokens. Many frameworks log full HTTP requests by default — that configuration is not CRA-compliant if it captures personal data unnecessarily.

Retention defaults: Data collected by default must have a defined retention period that is appropriate to the purpose. Audit logs: 90 days minimum (ENISA), 12 months recommended for NIS2-covered entities. Application logs: 30-60 days typical. Personal data: minimum necessary for the service, with automated deletion enforced by default.

Data Classification at Collection:

ENISA SbD guidance introduces the concept of "secure defaults for data collection" — the product should not collect categories of sensitive data without explicit user configuration:

Data CategoryDefault ActionUser Override
IP addresses in logsAnonymize last octetAllow full logging for abuse-prevention
User-agent stringsTruncate to OS/browserFull UA for analytics
Request bodiesDo not logAllow for debugging with explicit consent
User PIIPseudonymize in logsNever allow plaintext PII in logs
Payment dataNever log, everNot overridable
Health/biometric dataSeparate encrypted storeRequires explicit controller consent

Domain 4: Logging and Audit Defaults

A product that does not log security-relevant events by default cannot satisfy the CRA's incident response requirements. Article 13(8) requires manufacturers to "address and remediate vulnerabilities without delay" — which requires knowing when something went wrong. That requires logging.

ENISA's SbD guidance specifies a minimum set of security events that must be logged by default:

Authentication events:

Authorization events:

Configuration changes:

Infrastructure events:

What must NOT be in logs by default:

Log Integrity Requirement (ENISA SbD):

Logs that can be modified by a compromised service are insufficient. ENISA SbD guidance recommends that security logs be written to append-only storage (WORM — Write Once Read Many) with cryptographic chaining (each log entry hashes the previous), and that they be replicated to a separate security domain not accessible to the application service role.

For SaaS on EU-sovereign infrastructure, this typically means writing security logs to a separate logging service (Grafana Loki with immutable storage, OpenSearch with index lifecycle policies, or a dedicated SIEM) with strict network isolation from the application tier.


Domain 5: Third-Party Integration Defaults

SaaS products routinely integrate with dozens of third-party services: payment processors, analytics platforms, error tracking, customer data platforms, communication providers. Each integration is a potential attack surface expansion. CRA Art.12(3)(a) requires that "the attack surface including external interfaces" be minimized.

Insecure third-party integration defaults:

Over-privileged OAuth scopes: When adding a Google Workspace or Microsoft 365 integration, many products request the maximum scope set to avoid future re-authorization. The secure default is minimum required scope at integration time, with scope expansion requiring re-authorization.

Third-party scripts in browser context: Marketing tags, analytics scripts, and chat widgets loaded from third-party CDNs run in the same browser context as your application. Without Content Security Policy (CSP) enforcing strict source allowlists, a compromised third-party script can exfiltrate session tokens. Secure default: strict CSP with nonce-based or hash-based script allowlisting.

Webhook reception without signature verification: Receiving webhooks from payment processors or CI/CD systems without verifying the HMAC signature is a default security gap. ENISA SbD: all webhook endpoints must verify origin signatures by default.

Third-party analytics with user PII: Sending user email addresses, names, or internal IDs to analytics platforms by default creates uncontrolled data flows. Secure default: analytics events contain pseudonymized user IDs only, no PII, unless explicitly enabled.

US-based error tracking: Sending stack traces, error context, and request data to Sentry (US), Datadog (US), or similar services by default creates CLOUD Act exposure for any error context that includes EU personal data. EU-sovereign alternatives: GlitchTip (self-hosted), Sentry Self-Hosted on EU infrastructure, Highlight.io with EU region.


The 48-Hour Configuration Audit

If you want a practical starting point for CRA Art.12(2) compliance, this 48-hour audit identifies the highest-impact insecure defaults in your current SaaS product.

Day 1: Discovery (4 hours)

Step 1 — Authentication inventory (1 hour)

# Enumerate all authentication methods enabled in production
# Document for each: is it default-on or requires configuration?
# Flag: anything that allows access without MFA for privileged accounts
# Flag: any API key or service account with no expiry
# Flag: any OAuth application with wildcard redirect URIs

Step 2 — Network exposure scan (1 hour)

# From outside your VPC:
nmap -sV --open <your-production-ip-range>
# Expected result: 443 only (plus jump host if applicable)
# Flag: anything else open

# Test HTTPS configuration:
curl -I https://yoursaas.io
# Expect: Strict-Transport-Security header present
# Expect: X-Content-Type-Options: nosniff
# Expect: X-Frame-Options: DENY or SAMEORIGIN
# Flag: missing security headers

# Test CORS:
curl -H "Origin: https://evil.example.com" -I https://api.yoursaas.io/api/v1/health
# Flag: Access-Control-Allow-Origin: * in response

Step 3 — Default credentials scan (30 min)

# Search codebase and deployment configs for default credentials:
grep -r "password.*=.*admin\|password.*=.*changeme\|password.*=.*default\|secret.*=.*secret" \
  --include="*.env*" --include="*.yaml" --include="*.yml" --include="*.json" \
  --include="*.conf" --include="*.config" . 2>/dev/null

# Check Kubernetes secrets for plaintext defaults:
kubectl get secrets -A -o json | python3 -c "
import json,sys,base64
d=json.load(sys.stdin)
for item in d['items']:
    for k,v in item.get('data',{}).items():
        decoded=base64.b64decode(v).decode()
        if any(w in decoded.lower() for w in ['password','secret','key','token']):
            print(item['metadata']['namespace'], item['metadata']['name'], k, decoded[:20])
"

Step 4 — Logging coverage check (1.5 hours)

# Trigger a test authentication failure and verify it appears in logs:
curl -X POST https://api.yoursaas.io/auth/login \
  -d '{"email":"test@example.com","password":"wrongpassword"}'

# Check your logging system for the event within 30 seconds
# Flag: event not present, or present without IP/timestamp/user

# Trigger a test access to a restricted endpoint and verify log entry:
curl -H "Authorization: Bearer invalid_token" \
  https://api.yoursaas.io/admin/users

# Verify: unauthorized access attempt appears in security log
# Flag: not logged, or logged without user identity context

Day 2: Remediation Prioritization (4 hours)

Priority 1 — Immediate remediation (do today):

Priority 2 — Sprint remediation (next 2 weeks):

Priority 3 — Roadmap items (before September 2026):


Secure Default Configuration File Template

For SaaS products using environment-variable-based configuration, this template shows a secure baseline. Deploy this as your config.defaults.yml or equivalent — users override individual settings, but start from a hardened baseline.

# CRA Art.12(2) Secure Default Configuration
# Last updated: 2026-05-28 | ENISA SbD v1.0 alignment

authentication:
  mfa_required: true                    # Art.12(3)(a)
  mfa_required_for_roles: [admin, owner, billing]
  password_min_length: 12               # ENISA SbD §4.1
  password_breach_check: true           # HaveIBeenPwned API
  session_timeout_minutes: 480          # 8 hours for admin sessions
  session_idle_timeout_minutes: 60      # 1 hour idle
  api_key_expiry_days: 90               # Force rotation
  api_key_scope_required: true          # No omnipotent keys by default
  remember_device: false                # Opt-in
  legacy_auth_protocols: disabled       # No Basic Auth, OAuth 1.0

network:
  force_https: true                     # Art.12(3)(b)
  hsts_max_age: 31536000                # 1 year
  hsts_include_subdomains: true
  hsts_preload: true
  tls_minimum_version: "1.2"
  tls_preferred_version: "1.3"
  cors_allowed_origins: []              # Must be explicitly configured
  cors_allow_credentials: false
  csp_enabled: true
  csp_mode: "strict-nonce"              # Nonce-based, no unsafe-inline

data:
  encryption_at_rest: true              # AES-256-GCM
  encryption_key_rotation_days: 90
  encryption_in_transit: true           # TLS for all DB connections
  log_request_bodies: false             # Art.12(3)(b) — data minimization
  log_pii: false
  log_tokens: false
  analytics_user_id_pseudonymized: true
  data_retention_audit_logs_days: 90
  data_retention_app_logs_days: 30
  automated_deletion_enabled: true

logging:
  security_events_enabled: true         # Art.13(8) — incident response
  log_auth_success: true
  log_auth_failure: true
  log_mfa_bypass: true
  log_permission_changes: true
  log_config_changes: true
  log_cert_expiry_warnings: true
  log_integrity_mode: "append-only"     # ENISA SbD §5.3
  log_destination_isolated: true

third_party:
  analytics_pii: false                  # No PII in analytics by default
  webhook_signature_verify: true        # HMAC verification required
  csp_third_party_scripts: false        # Explicit allowlist required
  oauth_scope_minimum: true             # Request minimum scope

debug:
  debug_endpoints_enabled: false        # Art.12(3)(c)
  verbose_errors_enabled: false         # No stack traces in responses
  metrics_public: false                 # Auth required for /metrics

Configuration Drift: The Ongoing Compliance Problem

Shipping a secure default configuration is a one-time action. Maintaining it over time is an ongoing engineering discipline. ENISA SbD guidance specifically calls out "configuration drift" — the gradual accumulation of security-weakening deviations from the secure baseline — as a primary failure mode.

Configuration drift happens through:

CRA Art.12 implicitly requires ongoing configuration verification, not just a one-time audit. ENISA recommends:

Automated configuration scanning: Tools like kube-bench (Kubernetes CIS benchmarks), checkov (IaC security scanning), and ScoutSuite (cloud configuration auditing) should run on every deployment. Pass/fail thresholds enforced in CI/CD pipeline.

Configuration-as-code with drift detection: All security-relevant configuration stored in version-controlled Infrastructure-as-Code (Terraform, Pulumi, CDK). Drift detection compares live state to declared state on a schedule (daily minimum).

Immutable infrastructure: Container images rebuilt from scratch on each deployment prevent configuration accumulation. Servers that are upgraded in-place are not compliant with ENISA SbD drift prevention guidance.

Tenant configuration auditing: For multi-tenant SaaS, each tenant's security configuration should be auditable against your secure baseline. Tenants that have explicitly weakened defaults (disabled MFA, extended sessions) should be flagged in your compliance dashboard.


What "Reset to Original State" Means for SaaS

CRA Art.12(2) includes an often-overlooked requirement: products must offer "the possibility to reset the product to its original state." For a physical IoT device, this means a factory reset button. For SaaS, it's more nuanced.

ENISA's interpretation for software products (ENISA-CRA-FAQ-2026, Q.47):

"For software-as-a-service products, the manufacturer should provide a mechanism by which the operator can restore the product's security configuration to its default secure state. This does not require deletion of user data — it requires restoration of security parameters to their shipped defaults."

In practice, this means your product should support a security configuration reset that:

Implement this as a documented administrative function, not a user-facing feature. Most enterprise customers will never use it — but your ability to demonstrate it exists will matter during CRA conformity assessment.


The September 2026 Deadline: What You Need Ready

CRA enters full application on September 12, 2026 — approximately 15 weeks from the date of this post. Products placed on the EU market after that date must comply with all CRA requirements, including Art.12(2) secure defaults.

For SaaS teams targeting CRA compliance, the minimum viable secure-by-default implementation before September 2026:

Must have (Art.12(2) direct requirements):

Should have (ENISA SbD alignment, expected in conformity assessment):

EU-sovereign hosting alignment (not strictly required but reduces conformity assessment friction):


What's Next in This Series

This post covered the Secure by Default dimension of ENISA's framework. Post #3 will address Secure Software Development Lifecycle (SDLC) requirements under CRA Art.13 — the process requirements for how you build, test, and update software, not just what you ship.

Post #4 will cover vulnerability disclosure and incident response defaults (CRA Art.14) — the notification timelines, ENISA's coordinated vulnerability disclosure (CVD) framework, and how to implement a compliant responsible disclosure program.

Post #5 will be the series finale: a complete CRA-ENISA Compliance Scorecard you can use to assess your current SaaS product across all five ENISA Secure by Design dimensions.

If you're deploying on EU-sovereign infrastructure — or evaluating whether to migrate before September 2026 — the sota.io EU PaaS guide covers hosting options that reduce your CRA conformity assessment surface.


Summary: CRA Art.12(2) Secure Default Obligations

DimensionCRA RequirementENISA MinimumCompliance Status Test
AuthenticationArt.12(3)(a)MFA by default for adminMFA enrollment rate admin accounts
CredentialsArt.12(2)No static/default credentialsgrep scan — zero findings
Network exposureArt.12(3)(c)TLS enforced, CORS restrictednmap scan — 443 only
Data handlingArt.12(3)(b)Encryption at rest and transitNo plaintext storage confirmed
LoggingArt.13(8)Security events capturedAuth failure appears in log within 60s
Third-partyArt.12(3)(c)Minimum scope, CSP enforcedCSP header present and strict
Configuration resetArt.12(2)Reset mechanism documentedAdmin can restore secure baseline
Drift preventionArt.12(2) continuousAutomated scanning in CI/CDCI pipeline fails on config regression

EU-Native Hosting

Ready to move to EU-sovereign infrastructure?

sota.io is a German-hosted PaaS — no CLOUD Act exposure, no US jurisdiction, full GDPR compliance by design. Deploy your first app in minutes.