Most file integrity monitoring deployments fail in one of two ways: they watch too little to catch a web shell or a tampered binary, or they watch so much that the alerts are ignored. File integrity monitoring (FIM) records a trusted baseline of files, directories and registry keys (hashes, permissions, owners and other attributes), then reports additions, modifications and deletions against that baseline. PCI DSS requires it for critical files, and it is one of the few controls that catches an attacker who uses legitimate credentials but changes the system. This guide covers what to monitor on Linux, Windows, web servers and containers, scheduled versus real-time and "who-data" modes, how to tie alerts to change management so the noise falls away, and how to measure it. It is platform neutral, for security engineers and SOC teams; the Linux File Integrity Monitoring Guide goes further into Linux-only tooling.
Key takeaways
- Scope beats coverage. A short, well-chosen list of binaries, configuration, web roots, scheduled task and service definitions, and authentication files produces alerts people act on; monitoring whole disks does not.
- An alert without "who" is half an alert. Use the operating system's audit facility (Linux audit or eBPF, Windows object access auditing) so each change carries the user and process, not just the new hash.
- Change management is the noise filter. Changes made by the approved deployment pipeline or patch tool, inside a change window, should be auto-closed; anything else is an incident until explained.
- Protect the baseline itself. A FIM database stored on the host it protects, writable by root, can be rewritten by an attacker; keep a copy off host and sign or hash it.
- PCI DSS sets the floor, not the ceiling. Requirement 11.5.2 asks for a change-detection mechanism that compares critical files at least weekly; real-time monitoring of web roots and authentication files is what catches skimmers and web shells in time.
What FIM detects and what it does not
FIM answers one question: did this file or key change from its trusted state, and if so, who changed it? It is detective, not preventive, and it complements controls that stop the change in the first place, such as application allowlisting and read-only file systems.
| Attacker behaviour | ATT&CK v20 | What FIM sees |
|---|---|---|
| Web shell dropped into a web root | T1505.003 Web Shell | New file in a monitored directory, written by the web server process |
| System binary replaced or patched | T1554 Compromise Host Software Binary | Hash change on a binary outside a patch window |
| Malicious library or search-order hijack | T1574 Hijack Execution Flow | New library in an application or system path, or a changed loader configuration |
| Renamed or relocated tools posing as system files | T1036 Masquerading | New executable in a system directory |
| Tampering with stored data or configuration | T1565.001 Stored Data Manipulation | Change to monitored configuration or data files |
| Payment page script altered to skim card data | T1565.001 Stored Data Manipulation | Change to served JavaScript or HTML in the web root or content delivery store |
What FIM does not do: it does not see changes in memory, it cannot judge a change made through an approved but compromised update channel (the baseline will simply move), and it does not see files on hosts or paths outside its scope. NIST's National Cybersecurity Center of Excellence lists file system monitoring among the auditing and reporting capabilities in its data integrity work (SP 1800-11), alongside user activity and database monitoring; FIM is one part of that picture, not the whole.
What to monitor
Start from this list and add per application. Everything here changes rarely and legitimately only through deployment or patching.
| Area | Linux examples | Windows examples | Mode |
|---|---|---|---|
| System binaries and libraries | /bin, /sbin, /usr/bin, /usr/sbin, /lib, /usr/lib | C:\Windows\System32, C:\Windows\SysWOW64 | Scheduled, hash |
| Authentication and privilege configuration | /etc/passwd, /etc/shadow, /etc/sudoers, /etc/sudoers.d, /etc/pam.d, /etc/ssh/sshd_config, authorized_keys files | Local security policy exports, LSA registry keys | Real-time with who-data |
| Persistence locations | /etc/cron*, /var/spool/cron, systemd unit directories, /etc/ld.so.preload, shell profile files | Run and RunOnce keys, services keys, scheduled task folder, startup folders | Real-time with who-data |
| Web roots and served scripts | /var/www, application template and static directories | IIS site directories (inetpub) | Real-time |
| Application binaries and configuration | /opt/app, application config files | C:\Program Files\App, application config files | Scheduled, hash |
| Security tooling and audit logs | Audit rules, agent configuration, closed log files | Agent configuration, exported logs | Real-time |
| Boot chain | /boot, /boot/efi | Boot configuration data | Scheduled |
Exclude: log files that grow constantly (monitor them as "growing" files or not at all), caches, temporary directories, package manager databases that change on every update, and user profile data. AIDE, for example, ships a "growing file" attribute group for logs so that size growth alone is not an alert.
Do not copy sensitive file content into alerts. Some tools can report the exact content change in a text file. Wazuh's documentation warns that its report_changes option copies every monitored file to a private location, and offers a nodiff exception so that content of files such as credential stores is never sent in a finding.
Monitoring modes and attributes
Scheduled scans
The tool walks the monitored paths on a schedule and compares against the stored baseline. Wazuh's FIM module, for instance, scans every 12 hours (43200 seconds) by default, adjustable with the frequency option or scan_time and scan_day settings; AIDE runs when invoked, usually from cron or a systemd timer. Scheduled mode is cheap and catches everything in scope, but a change made and reverted between scans is missed, and detection waits for the next run.
Real-time monitoring
The tool subscribes to the kernel's file change notifications (inotify on Linux, change journal or directory notifications on Windows) and reports changes as they happen. Wazuh notes that its real-time detection pauses during scheduled scans and that a path must exist before the agent starts. Real-time costs more resources, so reserve it for persistence, authentication and web root paths.
Who-data
Who-data mode adds the user and process that made the change by drawing on the audit subsystem. On Linux that is the audit daemon or, in newer agents, eBPF; Wazuh's documentation shows a finding that records the audit user (the person who ran sudo), the effective user, the process (/usr/bin/nano) and the parent process (/usr/bin/sudo). On Windows the equivalent is object access auditing: enable the File System audit subcategory (auditpol /set /subcategory:"File System" /success:enable) and add a SACL entry to each monitored folder. Event 4663 then records the account, logon ID, object name, process name and the access used.
Attributes to record
Hash alone misses permission and ownership changes. AIDE's default "R" group combines permissions, file type, inode, link count, user, group, size, modification and change times and a SHA3-256 hash, plus ACLs, SELinux labels, extended attributes, file system attributes and capabilities where compiled in. Record at least: content hash (SHA-256 or stronger), size, owner, group, permissions or ACL, and on Linux file capabilities and SELinux context, because a setuid bit or capability added to an existing binary is a classic privilege escalation that leaves the hash unchanged.
Windows event sources for file and registry change
| Source | Event ID | What it records | Notes |
|---|---|---|---|
| Security log (Audit File System, Audit Registry) | 4663 | An access right was actually used on a file or registry object: account, process, object name, accesses such as WriteData, AppendData, DELETE, WRITE_DAC, WRITE_OWNER | Only generated where the object's SACL audits that right |
| Security log | 4656 | A handle to an object was requested | Correlate with 4663 by handle ID |
| Sysmon | 2 | A process changed a file creation time | Timestomping indicator |
| Sysmon | 11 | FileCreate: a file was created or overwritten | Good for web roots and startup folders |
| Sysmon | 12, 13, 14 | Registry key and value create or delete, value set, rename | Persistence keys |
| Sysmon | 23, 26 | File deleted (23 also archives the deleted file) | Archiving needs disk space controls |
| Sysmon | 27, 29 | Executable file creation blocked or detected | New PE files in unexpected paths |
Microsoft's monitoring guidance for 4663 suggests alerting on the object name for critical files, filtering on the accesses where only some actions matter, and flagging process names outside expected folders such as System32 or Program Files.
Containers, cloud and web content
- Immutable workloads: for containers, the strongest integrity control is a read-only root file system plus image signing and admission policy; FIM inside a container then only needs to watch the few writable paths. Treat any change to the image file system at run time as suspicious.
- Golden images: on virtual machines built from images, generate the baseline from the image build pipeline rather than from a running host, so the baseline reflects the reviewed state.
- Payment and login pages: card skimming changes scripts served to browsers, which may live in a content delivery network, tag manager or third-party host, outside any server FIM scope. PCI DSS v4.0.1 adds a separate requirement (11.6.1) for a change and tamper detection mechanism on payment pages as received by the browser; server-side FIM on the web root is necessary but not sufficient. See the PCI DSS for E-commerce Payment Page Scripts Guide.
- Configuration stores: cloud resources are better covered by configuration change logs and policy as code than by file monitoring.
Rolling out FIM without drowning in alerts
- Define scope per server role (web, database, domain controller, jump host) from the table above, and record it in the configuration baseline.
- Baseline from a known good state: build the database right after a clean build or from the image pipeline, store a copy off host (for example, AIDE's database_out written to read-only media or a central store), and record its hash.
- Run in report-only mode for two to four weeks on a pilot ring of each role. Log every change, then classify the sources: patching, deployment, configuration management agents, backup tools and administrators.
- Tune, do not mute: exclude genuinely volatile paths, mark logs as growing, and add rules that tag changes made by the deployment service account or package manager during an approved window.
- Integrate with change management: correlate each finding with a change ticket or pipeline run; auto-close matches, raise the rest. NIST SP 800-53 SI-7(7) calls this integration of detection and response.
- Go live by role, highest value first: domain controllers, jump hosts, payment and web servers, then the wider estate.
- Re-baseline deliberately: update the baseline only after approved changes, through the pipeline, never by running an "accept all" on the host. Roll back a bad tuning change by restoring the previous rule set from version control.
Choosing an approach. Small organisations can meet most needs with an endpoint detection agent's file events plus an open source checker such as AIDE on a nightly timer for Linux servers. Larger estates usually need a central FIM platform or SIEM-integrated agent with who-data, role-based policies and change ticket correlation. Prefer the tool your SOC already triages from: a separate console that nobody watches is the most common way FIM fails.
Where identity fits
The best FIM noise filter is identity: approved changes come from a small set of non-human identities, such as the deployment pipeline's service account, the patch management agent and the configuration management tool. Record those identities, alert when a change comes from any other account, and alert loudly when one of those service accounts changes files outside its normal paths or hours, because a stolen deployment credential lets an attacker make "approved-looking" changes. Watch the files that hold credentials too: SSH authorized_keys, sudoers and service account key files are FIM targets. See the Service Account Security Guide.
Detection and measurement
- Forward every finding to the SIEM with host, path, old and new hash, user, process and change ticket match.
- High-priority rules: new file in a web root written by the web server process; any change to sudoers, authorized_keys, PAM or sshd configuration; new executable in a system directory; change to the FIM or audit configuration itself; FIM agent stopped.
- Health checks: alert when an agent has not reported a scan within twice its schedule, and when the baseline hash changes outside a pipeline run.
Metrics that show it is working:
- Percentage of in-scope servers with FIM active and reporting (by role).
- Percentage of findings automatically matched to an approved change (rising over time).
- Unmatched findings per 100 hosts per week (falling, but never zero).
- Median time from an unmatched change to analyst triage.
- Number of baseline updates made outside the pipeline (target zero).
- Days since the last test change was injected and detected end to end.
Framework mapping
| Framework | Reference | How FIM applies |
|---|---|---|
| PCI DSS v4.0.1 | 11.5.2 | Change-detection mechanism alerting on unauthorised modification of critical files, with critical file comparisons at least weekly |
| PCI DSS v4.0.1 | 10.3.4, 11.6.1 | Integrity monitoring of audit logs; change and tamper detection on payment pages |
| NIST SP 800-53 Rev. 5 | SI-7, SI-7(1), SI-7(7) | Software, firmware and information integrity; integrity checks; integration of detection and response |
| NIST SP 800-53 Rev. 5 | CM-3, CM-6, AU-9 | Change control as the source of truth; configuration baselines; protecting audit information |
| NIST CSF 2.0 | PR.DS-01, DE.CM-09 | Integrity of data at rest; monitoring of software, runtime environments and data |
| ISO/IEC 27001:2022 Annex A | A.8.9 Configuration management, A.8.16 Monitoring activities | Baselines and monitoring of change |
Common failure patterns
- Monitoring everything: the whole of C:\ or / is in scope, thousands of daily changes are reported, and the console is muted within a month.
- Baselining a compromised state: the baseline is built on a host that is already compromised, or moves automatically with every update. Supply chain attacks show the limit: the XZ Utils backdoor sat in release tarballs that differed from the reviewed git source, so a baseline taken after installing the package would simply have recorded the malicious library as trusted. Baselines must come from a reviewed source, and FIM needs package and build provenance checks alongside it.
- No who-data: a change to sudoers is reported with only a new hash, and the analyst cannot tell an administrator from an intruder without a separate investigation.
- Auto-accepting changes on the host: a scheduled job re-initialises the database every night, so any attacker change becomes the new baseline the next day.
- Database on the protected host only: an attacker with root edits the FIM database or configuration and the tool reports nothing.
- Weekly scans on web roots: meeting the PCI DSS weekly minimum on a payment server leaves a web shell or skimmer running for days.
Practitioner checklist
- Define FIM scope per server role, covering binaries, authentication files, persistence locations, web roots and security tooling.
- Build baselines from the image pipeline or a verified clean build and keep a hashed copy off host.
- Enable real-time and who-data monitoring for authentication, persistence and web root paths.
- On Windows, enable Audit File System and Audit Registry and add SACLs to monitored paths; deploy Sysmon with file and registry events where used.
- Record hash, owner, permissions, ACLs and capabilities, not hash alone.
- Exclude volatile paths and never send credential file content in findings.
- Correlate findings with change tickets and the deployment service accounts; auto-close only matches.
- Alert on FIM agent stoppage and on changes to FIM or audit configuration.
- Inject a test change every quarter and confirm it reaches the SOC.
- For payment pages, add browser-side script change detection as well as server FIM.
Standards and references
- NIST: SP 800-53 Rev. 5
- NIST NCCoE: Data Integrity: Recovering from Ransomware and Other Destructive Events (SP 1800-11)
- Microsoft Learn: 4663(S) An attempt was made to access an object
- Microsoft Learn: Sysmon
- AIDE project: Advanced Intrusion Detection Environment
- Wazuh documentation: File integrity monitoring
- MITRE ATT&CK: Server Software Component: Web Shell
Related NHI Mgmt Group resources: Linux File Integrity Monitoring Guide · PCI DSS Scoping and Network Segmentation Guide · Service Account Security Guide · DNS Logging and Detection Guide