NIS2 Art.21(2)(g): Basic Cyber Hygiene and Security Training — SaaS Developer Guide (2026)
Most data breaches exploit predictable weaknesses: reused passwords, unpatched software, employees clicking phishing links, developers committing secrets. These are not zero-day vulnerabilities — they are hygiene failures. NIS2 Directive Art.21(2)(g) addresses them directly, requiring essential and important entities to maintain basic cyber hygiene practices and regular security training across the organisation.
Art.21(2)(g) is the cultural and operational foundation of NIS2 compliance. The other nine measures — incident handling, business continuity, supply chain security, cryptography — are only effective when the people and systems running them understand basic security principles. Without (g), the technical controls in (b) through (j) operate on a compromised human layer.
NCA auditors in June 2026 will evaluate whether organisations have moved beyond ad-hoc awareness initiatives to structured, measurable, role-specific training programmes with documented coverage and test results.
This guide builds the Art.21(2)(g) framework for SaaS development teams: hygiene baselines, training programme design, phishing simulation cadence, developer-specific secure coding training, and audit-grade evidence collection.
1. Art.21(2)(g) in the Full NIS2 Context
NIS2 Art.21(2) mandates ten cybersecurity risk-management measures. Art.21(2)(g) is the seventh — and the human-layer foundation that all technical controls depend on.
The Ten Mandatory Measures
| Subparagraph | Requirement | Primary Owner |
|---|---|---|
| Art.21(2)(a) | Risk analysis and information system security policies (see risk analysis guide) | CISO / Management |
| Art.21(2)(b) | Incident handling (see incident handling guide) | SOC / DevSecOps |
| Art.21(2)(c) | Business continuity, backup management, disaster recovery (see BCM guide) | Ops / SRE |
| Art.21(2)(d) | Supply chain security (see supply chain guide) | Procurement / DevSecOps |
| Art.21(2)(e) | Security in acquisition, development and maintenance (see SDL guide) | Engineering |
| Art.21(2)(f) | Policies to assess effectiveness of cybersecurity measures (see effectiveness guide) | Audit / GRC |
| Art.21(2)(g) | Basic cyber hygiene and training | HR / Security Awareness |
| Art.21(2)(h) | Cryptography and encryption policies (see cryptography guide) | Architecture |
| Art.21(2)(i) | HR security, access control and asset management (see IAM guide) | IT / HR / Engineering |
| Art.21(2)(j) | Multi-factor authentication and continuous authentication (see MFA guide) | IT / IAM / Engineering |
Art.21(2)(g) is the only measure explicitly focused on people — the behaviour, knowledge, and habits of every person who interacts with the organisation's systems.
The Exact Regulatory Text
Art.21(2)(g) requires:
"basic cyber hygiene practices and cybersecurity training"
ENISA's technical guidelines and the NIS2 Implementation Guidance (2023) expand this into four operational requirements:
- Defined hygiene baseline — documented minimum security practices for all staff and systems
- Role-specific training — content differentiated by role (general staff, developers, privileged users, management)
- Measurable coverage — tracking of who has completed training and when
- Recurrent cadence — not a one-off event but a sustained programme with annual minimum and triggered refreshes
"We did a training session last year" fails audit. Auditors ask for the training curriculum, the completion records, the phishing simulation results, and the remediation actions taken for non-completers.
2. The Cyber Hygiene Baseline
Three frameworks define the international consensus on what "basic cyber hygiene" means. Each approaches the problem from a different angle; together they form a complete baseline.
NCSC 10 Steps to Cyber Security
The UK National Cyber Security Centre's 10 Steps framework is the clearest mapping of hygiene practices to organisational control areas:
| Step | Control Area | NIS2 Mapping |
|---|---|---|
| 1. Risk Management | Governance | Art.21(2)(a) risk analysis |
| 2. Engagement and Training | Culture | Art.21(2)(g) |
| 3. Asset Management | Inventory | Art.21(2)(i) |
| 4. Architecture and Configuration | Hardening | Art.21(2)(g) baseline |
| 5. Vulnerability Management | Patching | Art.21(2)(g) hygiene |
| 6. Identity and Access Management | IAM | Art.21(2)(i)(j) |
| 7. Data Security | Encryption | Art.21(2)(h) |
| 8. Logging and Monitoring | Detection | Art.21(2)(b)(f) |
| 9. Incident Management | Response | Art.21(2)(b) |
| 10. Supply Chain Security | Third-party | Art.21(2)(d) |
For Art.21(2)(g), Steps 2, 4, and 5 are directly applicable. Steps 3, 6, and 7 are covered by other Art.21(2) measures.
Step 2 — Engagement and Training requires organisations to:
- Establish a security awareness programme
- Deliver role-specific content
- Measure awareness levels (phishing simulations, knowledge tests)
- Create a reporting culture where staff feel safe escalating security concerns
Step 4 — Architecture and Configuration defines the technical hygiene baseline:
- Standard secure configurations for all device types
- Removal of unnecessary software and features
- Automated patch deployment within defined SLAs
- Baseline security settings enforced via policy (MDM, GPO, or configuration management)
Step 5 — Vulnerability Management sets the operational cadence:
- Regular vulnerability scanning (minimum monthly for external attack surface)
- Patch SLAs: Critical ≤48h, High ≤7 days, Medium ≤30 days, Low ≤90 days
- Documented exception process for systems that cannot be patched on schedule
- Vulnerability tracking in a register with owner accountability
CIS Controls IG1 — Basic Cyber Hygiene for Every Organisation
CIS Controls v8 Implementation Group 1 (IG1) defines the minimum security posture for organisations with limited security resources. IG1 comprises 56 safeguards across 18 controls. The core hygiene safeguards relevant to Art.21(2)(g):
Inventory and Control (CIS 1, 2)
- CIS 1.1: Establish and maintain a detailed enterprise asset inventory
- CIS 2.1: Establish and maintain a software inventory
- CIS 2.2: Ensure authorised software is currently supported (no EOL software)
Data Protection (CIS 3)
- CIS 3.3: Configure data access control lists
- CIS 3.4: Enforce data retention policies
Secure Configuration (CIS 4)
- CIS 4.1: Establish and maintain a secure configuration process
- CIS 4.2: Establish and maintain a secure configuration process for network infrastructure
- CIS 4.7: Manage default accounts on enterprise assets and software (change or disable defaults)
Account Management (CIS 5)
- CIS 5.1: Establish and maintain an inventory of accounts
- CIS 5.2: Use unique passwords — never share credentials
- CIS 5.3: Disable dormant accounts after 45 days of inactivity
- CIS 5.4: Restrict administrator privileges to dedicated admin accounts
- CIS 5.6: Centrally manage accounts (IdP/SSO)
Patch Management (CIS 7)
- CIS 7.1: Establish and maintain a vulnerability management process
- CIS 7.2: Establish and maintain a remediation process
- CIS 7.3: Perform automated operating system patch management
- CIS 7.4: Perform automated application patch management
Email and Web Browser Protections (CIS 9)
- CIS 9.1: Ensure use of only fully supported browsers and email clients
- CIS 9.2: Use DNS filtering services
- CIS 9.3: Maintain and enforce network-based URL filters
- CIS 9.4: Restrict unnecessary or unauthorised browser and email client extensions
Malware Defences (CIS 10)
- CIS 10.1: Deploy and maintain anti-malware software on all endpoints
- CIS 10.2: Configure automatic updates for anti-malware signature files
- CIS 10.7: Use behaviour-based anti-malware
Security Awareness and Skills Training (CIS 14)
- CIS 14.1: Establish and maintain a security awareness programme
- CIS 14.2: Train workforce members to recognise social engineering attacks
- CIS 14.3: Train workforce members on authentication best practices
- CIS 14.5: Train workforce on causes of unintentional data exposure
- CIS 14.6: Train workforce members on how to identify and report threats
- CIS 14.7: Train workforce on how to identify and report when data is mishandled
IG1 is the minimum — organisations handling sensitive data or operating critical services should progress to IG2.
BSI IT-Grundschutz Basis-Absicherung
The German Federal Office for Information Security (BSI) IT-Grundschutz standard provides the European regulatory baseline. The Basis-Absicherung (basic safeguarding) level is directly aligned with NIS2 Art.21(2)(g) requirements for organisations beginning their security programme:
ORP.3 — Security Awareness and Training (directly Art.21(2)(g)):
- ORP.3.A1: Sensitisation of the management level for information security
- ORP.3.A3: Training and education on information security
- ORP.3.A4: Employee training in safety procedures
- ORP.3.A9: Role-specific security training (developers, admins, managers)
- ORP.3.A10: Regular update of security awareness
ORP.2 — Personnel (hygiene baseline for HR/onboarding):
- ORP.2.A1: Regulated handover and return of resources when personnel change
- ORP.2.A2: Returning assets on resignation
- ORP.2.A4: Regulation of tasks and responsibilities
SYS.2.1 — General Desktop Systems (device hygiene):
- SYS.2.1.A1: Secure configuration of IT systems
- SYS.2.1.A2: Secure logging in IT systems
- SYS.2.1.A4: Regular data backup
- SYS.2.1.A6: Principle of least privilege for user accounts
- SYS.2.1.A14: Updates and patches for firmware and software
BSI IT-Grundschutz aligns directly with NIS2 because BSI played a central role in shaping the German NIS2 transposition (NISG 2.0). German NCA auditors will use IT-Grundschutz as their reference framework.
3. Security Awareness Training Programme Design
A compliant Art.21(2)(g) training programme has four design dimensions: audience segmentation, content curriculum, delivery cadence, and measurement methodology.
Audience Segmentation
One training module cannot serve all roles. NCA auditors expect evidence that training is differentiated by the security risks associated with each role:
| Audience | Risk Profile | Training Focus |
|---|---|---|
| All staff | Phishing, credential theft, data handling | Social engineering, password hygiene, clean desk, incident reporting |
| Developers | Code-level vulnerabilities, secrets, supply chain | OWASP Top 10, secure coding, secrets management, dependency risk |
| Privileged users (admins, ops) | Lateral movement, privilege escalation, insider threat | Privileged access hygiene, MFA for admin, change management |
| Management | Business email compromise, strategic risk | Whaling/CEO fraud, vendor fraud, risk governance responsibilities |
| New joiners | All baseline risks, company-specific procedures | Comprehensive onboarding module covering all categories |
Annual Training Curriculum
The minimum compliant training programme for a SaaS organisation:
Module 1 — Security Fundamentals (All Staff)
- Duration: 45–60 minutes
- Frequency: Annual recertification + onboarding
- Content: Password hygiene, phishing recognition, data classification, clean desk/clear screen, incident reporting procedure, acceptable use policy
- Assessment: 10-question knowledge test, minimum 80% pass mark
Module 2 — Social Engineering and Phishing (All Staff)
- Duration: 30 minutes
- Frequency: Annual + triggered by failed simulation
- Content: Email phishing anatomy, spear phishing indicators, vishing/smishing, pretexting, business email compromise, reporting procedures
- Assessment: Post-training phishing simulation within 30 days
Module 3 — Secure Development Practices (Developers)
- Duration: 90–120 minutes
- Frequency: Annual + triggered by new OWASP findings in codebase
- Content: OWASP Top 10 current edition, CWE/SANS Top 25, secrets management, dependency security, code review security focus areas, secure CI/CD pipeline configuration
- Assessment: Code-level exercise (identify vulnerabilities in sample code snippets)
Module 4 — Privileged Access and Administrative Security (Admins, Ops)
- Duration: 60 minutes
- Frequency: Annual
- Content: PAM principles, just-in-time access, session recording, change management, insider threat indicators, emergency access procedures
- Assessment: Scenario-based exercise
Module 5 — Management and Executive Security (Management)
- Duration: 45 minutes
- Frequency: Annual
- Content: BEC anatomy, CEO/CFO fraud patterns, vendor payment fraud, strategic risk ownership, security governance responsibilities
- Assessment: Practical: identify 3 suspicious elements in sample email chain
Triggered Training Events
Beyond the annual cadence, the following events must trigger immediate training interventions:
| Trigger | Response | Timeline |
|---|---|---|
| Failed phishing simulation | Targeted remedial module + re-simulation | Within 14 days |
| Security incident involving human error | Root-cause-specific refresher for affected team | Within 7 days of incident closure |
| New OWASP Top 10 edition published | Developer training update | Within 60 days of publication |
| Significant change in threat landscape | Awareness bulletin + optional briefing | Within 5 business days of identification |
| New joiner | Complete onboarding programme | Week 1 |
| Role change to privileged access | Module 4 completion | Before access is granted |
4. Phishing Simulation Programme
Phishing simulations are the most effective way to measure the real-world effectiveness of security awareness training. Art.21(2)(g) compliance requires not just running simulations but demonstrating improvement over time.
Simulation Cadence
| Programme Maturity | Frequency | Complexity |
|---|---|---|
| Year 1 (establish baseline) | Quarterly | Generic credential harvest, simple lure |
| Year 2 (measure improvement) | Monthly | Spear phishing, pretexting, phone follow-up |
| Year 3+ (maintain and target) | Monthly baseline + targeted for high-risk roles | BEC simulation, supply chain spoofing, multi-step attacks |
Simulation Metrics for Audit Evidence
Track and document for NCA audit:
| Metric | Definition | Target |
|---|---|---|
| Click rate | % of recipients who clicked the phishing link | <5% mature programme |
| Credential submission rate | % who submitted credentials after clicking | <2% mature programme |
| Report rate | % who reported the simulation via security channel | >30% target |
| Time to report | Median time from delivery to first report | <15 minutes |
| Repeat offender rate | % who fail 3+ consecutive simulations | <3% |
Simulation Design Principles
- No punitive consequences — simulations measure programme effectiveness, not individual performance. Public shaming increases psychological harm and reduces reporting rates.
- Immediate teachable moment — display educational content immediately after a click: explain the indicators the employee missed.
- Realistic but not harmful — avoid simulations that impersonate genuine emergency services, health alerts, or family emergencies. Use realistic business scenarios (invoice approval, password expiry, IT helpdesk).
- Rotate lure types — credential harvest, malware download prompt, URL redirect, QR code phishing (quishing), and voice/SMS follow-up (vishing/smishing).
- Benchmark against industry — Proofpoint's State of the Phish and SANS Security Awareness Reports provide industry click-rate benchmarks.
Documentation for Art.21(2)(g) Audit
Maintain the following as audit evidence:
phishing_programme_record:
period: "2025-Q4"
simulations_run: 4
total_recipients: 87
average_click_rate: 6.2%
average_report_rate: 24%
trend: "improving" # from 11.4% click rate Q1 2025
remedial_training_triggered: 12
remedial_training_completed: 12
methodology: "GoPhish / Knowbe4 / Cofense"
approved_by: "CISO"
next_review: "2026-Q1"
5. Developer Security Training: OWASP Focus
Developers are the highest-risk role for Art.21(2)(g) purposes because they create the attack surface the other controls must defend. Developer-specific training requires depth that generic awareness programmes cannot provide.
OWASP Top 10 (2021 Edition) — Developer Training Curriculum
Each OWASP category maps to a concrete training module:
A01: Broken Access Control (moved to #1 in 2021)
- Training content: IDOR patterns, path traversal, privilege escalation vectors, horizontal/vertical access control failures
- Lab exercise: Identify and fix a multi-tenant data isolation failure in sample code
- Mitigation: Enforce server-side access control, deny by default, log access failures
A02: Cryptographic Failures
- Training content: Sensitive data exposure patterns, weak algorithm identification (MD5/SHA1/DES), key management anti-patterns, cleartext transmission detection
- Lab exercise: Audit a sample application for cleartext credential storage
- Mitigation: Use TLS everywhere, AES-256 for data at rest, bcrypt/Argon2 for passwords, never roll custom crypto
A03: Injection (SQL, LDAP, OS, NoSQL)
- Training content: SQL injection anatomy, NoSQL injection patterns, LDAP injection, OS command injection, template injection
- Lab exercise: Fix 5 SQL injection vulnerabilities in sample ORM code
- Mitigation: Parameterised queries, input validation, least-privilege DB accounts, WAF as defence-in-depth
A04: Insecure Design
- Training content: Threat modelling basics (STRIDE), security design patterns, trust boundary identification, abuse case development
- Lab exercise: Threat model a simple API endpoint
- Mitigation: Secure design principles in architecture review, not bolted on post-development
A05: Security Misconfiguration
- Training content: Default credentials, excessive permissions, unnecessary features, verbose error messages, missing security headers
- Lab exercise: Audit a sample Nginx + Node.js configuration for misconfigurations
- Mitigation: Hardening checklists, IaC security scanning, automated configuration drift detection
A06: Vulnerable and Outdated Components
- Training content: SCA tools (Dependabot, Snyk, OWASP Dependency-Check), CVE severity scoring, transitive dependency risk
- Lab exercise: Generate SBOM for sample project, identify critical CVEs
- Mitigation: Automated dependency scanning in CI, lockfiles, SBOM generation, vendor patch monitoring
A07: Identification and Authentication Failures
- Training content: Credential stuffing, password spraying, session fixation, weak JWT implementation patterns, insecure "remember me"
- Lab exercise: Identify authentication weaknesses in sample login flow
- Mitigation: MFA enforcement, secure session management, rate limiting, breached password detection (HIBP API)
A08: Software and Data Integrity Failures
- Training content: Supply chain attack vectors, unsigned update mechanisms, insecure deserialization, CI/CD pipeline injection
- Lab exercise: Identify supply chain risk in sample GitHub Actions workflow
- Mitigation: Signed commits, SLSA framework, reproducible builds, integrity verification
A09: Security Logging and Monitoring Failures
- Training content: What to log (authentication events, access failures, privilege changes), structured logging, SIEM integration, alert fatigue
- Lab exercise: Define structured log schema for a sample authentication service
- Mitigation: Centralised logging, alerting on suspicious patterns, log retention policy
A10: Server-Side Request Forgery (SSRF)
- Training content: SSRF anatomy, cloud metadata service exploitation (AWS/GCP/Azure IMDS), DNS rebinding
- Lab exercise: Identify SSRF vulnerability in webhook handler sample code
- Mitigation: Allowlist of permitted URLs, disable redirects in HTTP clients, cloud metadata service restrictions
SANS/CWE Top 25 Supplement
Beyond OWASP, the SANS/CWE Top 25 Most Dangerous Software Weaknesses provides additional developer training focus areas:
| CWE | Weakness | Developer Training Priority |
|---|---|---|
| CWE-787 | Out-of-bounds Write | High (C/C++ codebases) |
| CWE-79 | Cross-site Scripting (XSS) | High (all web applications) |
| CWE-89 | SQL Injection | High (all database access) |
| CWE-416 | Use After Free | High (memory-managed languages) |
| CWE-78 | OS Command Injection | Critical (any shell execution) |
| CWE-20 | Improper Input Validation | High (all API endpoints) |
| CWE-125 | Out-of-bounds Read | Medium |
| CWE-22 | Path Traversal | High (file access operations) |
| CWE-352 | CSRF | High (state-changing web operations) |
| CWE-434 | Unrestricted File Upload | High (file handling features) |
6. Password Policy Framework (NIST SP 800-63B Aligned)
Password policy is explicitly named in Art.21(2)(g) requirements. Modern password policy guidance (NIST SP 800-63B, 2024 revision) has shifted significantly from earlier complexity requirements.
What NIST SP 800-63B Requires
Do:
- Minimum length 15 characters for memorised secrets (passwords)
- Check passwords against known breached password lists (HIBP, custom dictionaries)
- Allow all printable ASCII characters and Unicode
- Offer password managers (no restrictions on paste)
- Rate-limit authentication attempts (lockout after 5–10 failures with increasing delays)
- Implement MFA for all accounts (see Art.21(2)(j) MFA guide)
- Hash passwords with bcrypt (cost ≥12), scrypt, or Argon2id
Don't:
- Don't require regular password rotation without evidence of compromise (the 90-day rotation rule is obsolete)
- Don't require complexity rules (uppercase + special character + number combinations) — they produce predictable patterns (Password1!)
- Don't display password strength meters based on complexity rules alone
- Don't impose maximum length restrictions below 64 characters
- Don't use security questions as a recovery mechanism
Password Policy Template
# Password Policy — [Organisation Name]
Version: 1.0 | Review: Annual | Owner: Security Team
## Scope
All user accounts accessing [Organisation] systems, services, and applications.
## Requirements
### Length and Complexity
- Minimum: 15 characters
- Maximum: 128 characters (longer is permitted)
- Complexity: No mandatory character class rules
- Passphrase encouraged: four random words ≥ 20 characters
### Forbidden Passwords
Passwords are checked against:
- HIBP Have I Been Pwned database (k-anonymity API)
- Organisation-specific dictionary (company name, product names, sequential years)
- Common keyboard patterns (qwerty, 123456789)
### Rotation Policy
- No mandatory periodic rotation
- Immediate rotation required: suspected compromise, shared credential discovered, colleague departure
- Privileged accounts: rotation on every use (PAM-managed where possible)
### Account Lockout
- Trigger: 5 consecutive failures
- Lockout: 15-minute progressive delay (not hard lockout to avoid DoS)
- Admin override: Security team can unlock with identity verification
- Monitoring: Failed login patterns alerted to SIEM
### Multi-Factor Authentication
- Required for: all accounts (see MFA Policy)
- Permitted methods: TOTP, hardware key (FIDO2), passkey
- Prohibited: SMS OTP for privileged accounts
### Password Storage
- Algorithm: Argon2id (memory=19456, iterations=2, parallelism=1) or bcrypt (cost=12)
- Salt: Per-user, cryptographically random, minimum 128 bits
- Never: MD5, SHA-1, SHA-256 unsalted, plain text
## Password Manager
Organisation-provided password manager: [Bitwarden/1Password/etc.]
All staff required to use for work credentials. Personal use permitted.
7. Python NIS2HygieneAssessor
The following tool evaluates an organisation's Art.21(2)(g) compliance posture across five dimensions:
from dataclasses import dataclass, field
from typing import Literal
from datetime import date, timedelta
import json
ComplianceStatus = Literal["COMPLIANT", "PARTIAL", "NON_COMPLIANT", "NOT_ASSESSED"]
@dataclass
class HygieneBaseline:
"""CIS IG1 / NCSC 10 Steps technical hygiene baseline."""
asset_inventory_maintained: bool = False
patch_sla_defined: bool = False
patch_sla_met_percentage: float = 0.0 # % of critical CVEs patched within SLA
secure_config_baseline_documented: bool = False
eol_software_present: bool = True
anti_malware_deployed: bool = False
email_filtering_enabled: bool = False
dns_filtering_enabled: bool = False
mfa_enforced_all_staff: bool = False
def score(self) -> float:
checks = [
self.asset_inventory_maintained,
self.patch_sla_defined,
self.patch_sla_met_percentage >= 90,
self.secure_config_baseline_documented,
not self.eol_software_present,
self.anti_malware_deployed,
self.email_filtering_enabled,
self.dns_filtering_enabled,
self.mfa_enforced_all_staff,
]
return sum(checks) / len(checks)
@dataclass
class TrainingProgramme:
"""Security awareness and training programme status."""
all_staff_training_curriculum_exists: bool = False
developer_training_curriculum_exists: bool = False
privileged_user_training_exists: bool = False
management_training_exists: bool = False
last_training_completion_date: date | None = None
completion_rate_percentage: float = 0.0 # % of staff completed in last 12 months
training_pass_mark_enforced: bool = False
new_joiner_training_exists: bool = False
def score(self) -> float:
days_since_training = (
(date.today() - self.last_training_completion_date).days
if self.last_training_completion_date else 999
)
checks = [
self.all_staff_training_curriculum_exists,
self.developer_training_curriculum_exists,
self.privileged_user_training_exists,
self.management_training_exists,
days_since_training <= 365,
self.completion_rate_percentage >= 90,
self.training_pass_mark_enforced,
self.new_joiner_training_exists,
]
return sum(checks) / len(checks)
@dataclass
class PhishingProgramme:
"""Phishing simulation programme status."""
programme_active: bool = False
simulations_per_year: int = 0
last_simulation_date: date | None = None
last_click_rate_percentage: float = 100.0
click_rate_trend: Literal["improving", "stable", "worsening", "not_measured"] = "not_measured"
report_rate_percentage: float = 0.0
remedial_training_for_failures: bool = False
def score(self) -> float:
days_since_sim = (
(date.today() - self.last_simulation_date).days
if self.last_simulation_date else 999
)
checks = [
self.programme_active,
self.simulations_per_year >= 4,
days_since_sim <= 90,
self.last_click_rate_percentage <= 10,
self.click_rate_trend in ("improving", "stable"),
self.report_rate_percentage >= 20,
self.remedial_training_for_failures,
]
return sum(checks) / len(checks)
@dataclass
class PasswordPolicy:
"""Password policy compliance (NIST SP 800-63B aligned)."""
minimum_length: int = 8
breached_password_check_enabled: bool = False
mandatory_rotation_disabled: bool = False
complexity_rules_disabled: bool = False
password_manager_provided: bool = False
account_lockout_configured: bool = False
argon2_or_bcrypt_hashing: bool = False
def score(self) -> float:
checks = [
self.minimum_length >= 15,
self.breached_password_check_enabled,
self.mandatory_rotation_disabled, # True = compliant (rotation not mandatory)
self.complexity_rules_disabled, # True = compliant (no complexity theatre)
self.password_manager_provided,
self.account_lockout_configured,
self.argon2_or_bcrypt_hashing,
]
return sum(checks) / len(checks)
@dataclass
class NIS2HygieneAssessor:
hygiene: HygieneBaseline = field(default_factory=HygieneBaseline)
training: TrainingProgramme = field(default_factory=TrainingProgramme)
phishing: PhishingProgramme = field(default_factory=PhishingProgramme)
password: PasswordPolicy = field(default_factory=PasswordPolicy)
WEIGHTS = {
"hygiene": 0.35,
"training": 0.30,
"phishing": 0.20,
"password": 0.15,
}
def overall_score(self) -> float:
return (
self.hygiene.score() * self.WEIGHTS["hygiene"]
+ self.training.score() * self.WEIGHTS["training"]
+ self.phishing.score() * self.WEIGHTS["phishing"]
+ self.password.score() * self.WEIGHTS["password"]
)
def compliance_status(self) -> ComplianceStatus:
score = self.overall_score()
if score >= 0.85:
return "COMPLIANT"
elif score >= 0.60:
return "PARTIAL"
else:
return "NON_COMPLIANT"
def gaps(self) -> list[dict]:
findings = []
if not self.hygiene.asset_inventory_maintained:
findings.append({"severity": "HIGH", "area": "Hygiene", "gap": "No asset inventory maintained (CIS 1.1)", "remediation": "Implement automated asset discovery and maintain CMDB"})
if self.hygiene.eol_software_present:
findings.append({"severity": "CRITICAL", "area": "Hygiene", "gap": "EOL software present in environment", "remediation": "Identify all EOL components, plan upgrade or compensating controls"})
if self.hygiene.patch_sla_met_percentage < 90:
findings.append({"severity": "HIGH", "area": "Hygiene", "gap": f"Patch SLA compliance {self.hygiene.patch_sla_met_percentage:.0f}% (target ≥90%)", "remediation": "Review patch management process, identify blockers"})
if not self.hygiene.mfa_enforced_all_staff:
findings.append({"severity": "CRITICAL", "area": "Hygiene", "gap": "MFA not enforced for all staff", "remediation": "Enforce MFA via IdP policy — no exceptions for standard accounts"})
if self.training.completion_rate_percentage < 90:
findings.append({"severity": "HIGH", "area": "Training", "gap": f"Training completion {self.training.completion_rate_percentage:.0f}% (target ≥90%)", "remediation": "Escalate non-completers to line managers, consider mandatory completion policy"})
if not self.training.developer_training_curriculum_exists:
findings.append({"severity": "HIGH", "area": "Training", "gap": "No developer-specific security training curriculum", "remediation": "Develop OWASP Top 10 training module for engineering teams"})
if not self.phishing.programme_active:
findings.append({"severity": "HIGH", "area": "Phishing", "gap": "No phishing simulation programme active", "remediation": "Implement quarterly phishing simulations (GoPhish, KnowBe4, or Cofense)"})
if self.phishing.last_click_rate_percentage > 10:
findings.append({"severity": "MEDIUM", "area": "Phishing", "gap": f"Click rate {self.phishing.last_click_rate_percentage:.1f}% exceeds 10% target", "remediation": "Increase simulation frequency, targeted training for repeat clickers"})
if self.password.minimum_length < 15:
findings.append({"severity": "MEDIUM", "area": "Password", "gap": f"Minimum password length {self.password.minimum_length} chars (NIST SP 800-63B: ≥15)", "remediation": "Update password policy to require minimum 15 characters"})
if not self.password.breached_password_check_enabled:
findings.append({"severity": "MEDIUM", "area": "Password", "gap": "No breached password check on account creation/change", "remediation": "Integrate HIBP k-anonymity API into authentication flow"})
if not self.password.argon2_or_bcrypt_hashing:
findings.append({"severity": "CRITICAL", "area": "Password", "gap": "Passwords not hashed with Argon2id or bcrypt", "remediation": "Migrate to Argon2id (memory=19456, iterations=2) or bcrypt (cost≥12)"})
return sorted(findings, key=lambda f: {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}[f["severity"]])
def nca_audit_report(self) -> str:
status = self.compliance_status()
score = self.overall_score()
gaps = self.gaps()
critical_gaps = [g for g in gaps if g["severity"] == "CRITICAL"]
report = f"""
=== NIS2 Art.21(2)(g) Compliance Assessment ===
Date: {date.today()}
Overall Score: {score:.0%} → {status}
Dimension Scores:
Hygiene Baseline (35%): {self.hygiene.score():.0%}
Training Programme (30%): {self.training.score():.0%}
Phishing Programme (20%): {self.phishing.score():.0%}
Password Policy (15%): {self.password.score():.0%}
Critical Gaps ({len(critical_gaps)} items):
"""
for gap in critical_gaps:
report += f" ❌ [{gap['area']}] {gap['gap']}\n"
report += f" → {gap['remediation']}\n"
if not critical_gaps:
report += " ✅ No critical gaps identified\n"
report += f"""
All Gaps ({len(gaps)} items): see full gap list in JSON export
NCA Audit Readiness: {"READY" if status == "COMPLIANT" and not critical_gaps else "ACTION REQUIRED"}
"""
return report
# Example assessment — typical SaaS organisation starting NIS2 compliance
if __name__ == "__main__":
assessor = NIS2HygieneAssessor(
hygiene=HygieneBaseline(
asset_inventory_maintained=True,
patch_sla_defined=True,
patch_sla_met_percentage=78.0,
secure_config_baseline_documented=False,
eol_software_present=False,
anti_malware_deployed=True,
email_filtering_enabled=True,
dns_filtering_enabled=False,
mfa_enforced_all_staff=True,
),
training=TrainingProgramme(
all_staff_training_curriculum_exists=True,
developer_training_curriculum_exists=True,
privileged_user_training_exists=False,
management_training_exists=False,
last_training_completion_date=date(2025, 11, 15),
completion_rate_percentage=84.0,
training_pass_mark_enforced=True,
new_joiner_training_exists=True,
),
phishing=PhishingProgramme(
programme_active=True,
simulations_per_year=4,
last_simulation_date=date(2026, 3, 1),
last_click_rate_percentage=7.2,
click_rate_trend="improving",
report_rate_percentage=22.0,
remedial_training_for_failures=True,
),
password=PasswordPolicy(
minimum_length=12,
breached_password_check_enabled=False,
mandatory_rotation_disabled=True,
complexity_rules_disabled=True,
password_manager_provided=True,
account_lockout_configured=True,
argon2_or_bcrypt_hashing=True,
),
)
print(assessor.nca_audit_report())
print(json.dumps(assessor.gaps(), indent=2))
8. 25-Item Art.21(2)(g) Compliance Checklist
Hygiene Baseline (8 items)
- G-01 Asset inventory maintained and updated (CIS 1.1) — CMDB or equivalent
- G-02 Patch SLA defined and documented (Critical ≤48h, High ≤7d, Medium ≤30d)
- G-03 Patch SLA compliance ≥90% for Critical and High CVEs — evidence in vulnerability tracker
- G-04 Secure configuration baseline documented for all device/server types
- G-05 No EOL software present, or compensating controls documented with remediation plan
- G-06 Anti-malware deployed and auto-updating on all endpoints
- G-07 Email and DNS filtering enabled (anti-phishing, anti-spam, malicious domain blocking)
- G-08 MFA enforced for all staff accounts — no exemptions for standard accounts
Training Programme (8 items)
- T-01 Written security awareness training curriculum exists and is version-controlled
- T-02 Training is role-differentiated: all staff / developers / privileged users / management
- T-03 New joiner training completed before system access granted
- T-04 Annual training completion rate ≥90% — documented in LMS or equivalent
- T-05 Knowledge assessment with defined pass mark (≥80%) for all modules
- T-06 Developer security training covers current OWASP Top 10 edition
- T-07 Training records retained for minimum 3 years (NCA audit evidence)
- T-08 Triggered training process defined for incidents caused by human error
Phishing Simulation (4 items)
- P-01 Phishing simulation programme active — minimum quarterly simulations
- P-02 Simulation metrics tracked: click rate, report rate, credential submission rate
- P-03 Click rate trend improving year-over-year — documented in programme report
- P-04 Remedial training triggered automatically for simulation failures within 14 days
Password Policy (5 items)
- W-01 Password policy documented and published — aligned with NIST SP 800-63B
- W-02 Minimum length ≥15 characters enforced at technical level
- W-03 Breached password check enabled (HIBP API or equivalent) on account creation and change
- W-04 Password hashing uses Argon2id or bcrypt — no MD5, SHA-1, or unsalted SHA-256
- W-05 Organisation-provided password manager deployed and staff trained
9. Building Art.21(2)(g) in 12 Weeks
| Week | Milestone | Owner |
|---|---|---|
| 1–2 | Hygiene baseline audit: asset inventory, patch SLA compliance, EOL software scan | IT / Ops |
| 3–4 | Training curriculum design: all-staff and developer modules | HR / Security Team |
| 5–6 | LMS setup and first training cohort (all staff) | HR |
| 7 | First phishing simulation — establish baseline click rate | Security Team |
| 8 | Developer training cohort — OWASP Top 10 module | Engineering Lead |
| 9 | Password policy update — migrate to NIST SP 800-63B alignment | IT / Engineering |
| 10 | Privileged user and management training cohorts | HR / CISO |
| 11 | Second phishing simulation — measure improvement vs Week 7 baseline | Security Team |
| 12 | Evidence compilation for NCA audit: completion records, simulation reports, policy documents | CISO / GRC |
10. Common Art.21(2)(g) Audit Failures
NCA auditors consistently find these gaps when assessing Art.21(2)(g) compliance:
1. Training exists but coverage is incomplete "We did training" is not the same as "90% of staff completed training." Auditors request completion records by department, role, and date. Organisations without an LMS often cannot demonstrate coverage.
2. Developer training is generic awareness content Developers are treated as "just another staff group" in many awareness programmes. Art.21(2)(g) requires training that addresses role-specific risks. An OWASP Top 10 module is not optional for engineering teams.
3. Phishing simulations lack trend data Running a single simulation in the week before the audit demonstrates awareness of the requirement, not a functioning programme. Auditors look for multi-year data showing measurement and improvement.
4. Password policy contradicts NIST guidance Organisations that mandate 90-day rotation and complexity rules are implementing 2010-era guidance that NIST explicitly discourages. Auditors will not reject this, but they will note the gap between policy and current best practice.
5. Training records are not retained Staff who completed training two years ago may have left; their records may have been deleted. NCA expects a 3-year minimum retention period for training evidence as part of the Art.21(2)(a) documentation requirements.
Completing the NIS2 Art.21(2) Series
With Art.21(2)(g), all ten mandatory NIS2 cybersecurity risk-management measures are now covered:
| Subparagraph | Requirement | Guide |
|---|---|---|
| Art.21(2)(a) | Risk analysis and information security policies | Risk Analysis Guide |
| Art.21(2)(b) | Incident handling | Incident Handling Guide |
| Art.21(2)(c) | Business continuity, backup, disaster recovery | BCM Guide |
| Art.21(2)(d) | Supply chain security | Supply Chain Guide |
| Art.21(2)(e) | Secure development lifecycle | SDL Guide |
| Art.21(2)(f) | Effectiveness assessment | Effectiveness Guide |
| Art.21(2)(g) | Basic cyber hygiene and training | This guide |
| Art.21(2)(h) | Cryptography and encryption | Cryptography Guide |
| Art.21(2)(i) | HR security, access control, asset management | IAM Guide |
| Art.21(2)(j) | Multi-factor authentication | MFA Guide |
NIS2 Art.21(2) compliance requires all ten measures. This series provides the implementation guidance for each. Start with the Risk Analysis guide as the foundation — it creates the risk register that all other measures must address.
For SaaS organisations seeking a European hosting platform that reduces their CLOUD Act exposure under Art.21(2)(a) risk analysis requirements, sota.io provides EU-sovereign infrastructure with no US jurisdiction.