WPHammer
Log in
  • Malicious pattern detection
  • Screenshots
  • Notifications
  • Updating the baseline
  • Data retention
  • Related
  • Visual Checks

    WPHammer's canary system monitors your WordPress sites for unauthorized changes by comparing the current state of each page against a stored baseline. It detects structural changes to the DOM, new third-party scripts, hidden links, and known malicious patterns — the kind of changes that indicate a site has been compromised.

    How canary monitoring works

    The canary system operates in two phases:

    1. Baseline capture — the SetCanaryBaselineAction fetches the site's HTML over HTTP (30-second timeout, 2 retries) and extracts a structural fingerprint. This becomes the reference point stored as a CanaryBaseline record with is_baseline set to true.

    2. Scheduled checks — the DispatchCanaryChecks command finds all sites with canary_enabled set to true that have an existing baseline. For each, it dispatches a RunCanaryCheckJob that fetches the current HTML, extracts a new fingerprint, and compares it against the baseline.

    DOM fingerprinting

    The CanaryFingerprinter extracts a structural fingerprint from raw HTML using DOM parsing. The fingerprint includes:

    • Navigation hash — an MD5 hash of the site's navigation structure
    • Footer hash — an MD5 hash of the footer content
    • Scripts hash — an MD5 hash of all script elements
    • Meta hash — an MD5 hash of meta tags
    • Iframe count — the number of iframes on the page
    • Hidden link count — links that are visually hidden (a pharma hack indicator)
    • HTML lang attribute — the page language (Japanese SEO hack changes this)
    • Script domains — external domains loading JavaScript
    • Script URLs — full URLs of all script sources
    • Meta tags — key meta tag values

    When a check runs, the CanaryFingerprinter.compare() method calculates a diff percentage and details by comparing each element of the baseline and snapshot fingerprints.

    What triggers an alert

    The RunCanaryCheckAction evaluates the comparison and assigns a CanaryStatus:

    • Clean — no differences detected
    • Changed — differences detected but below 15% and no critical findings
    • Alert — differences at or above 15%, or a critical finding detected
    • Error — the check could not complete (network failure, timeout)

    Critical findings

    Certain changes are always flagged as critical regardless of the diff percentage:

    • New iframes — iframes that were not present in the baseline
    • Hidden links — links using CSS or inline styles to hide from visitors (a signature of pharma hacks)
    • HTML lang changes — the page language switching unexpectedly (common in Japanese SEO spam attacks)
    • New script domains — external JavaScript loading from domains not seen in the baseline

    Malicious pattern detection

    Beyond structural comparison, the CanaryFingerprinter.detectMaliciousPatterns() method scans the raw HTML for known attack signatures:

    • Base64-encoded eval blocks — obfuscated PHP/JavaScript injection
    • Meta refresh redirects — redirects to external domains via meta tags
    • Crypto miner scripts — domains associated with browser-based cryptocurrency mining (coinhive.com, jsecoin.com, cryptoloot.pro, and others)
    • Suspicious hidden iframes — iframes designed to be invisible, excluding known legitimate embeds from Google Tag Manager, reCAPTCHA, Facebook, and DoubleClick

    Each detected pattern is classified by severity: critical, high, or medium.

    Screenshots

    When a canary check results in an Alert status, the TakeCanaryScreenshotAction captures a full-page screenshot of the site using the ScrapingBee API. This provides visual evidence of what changed.

    Screenshots are stored at storage/canary/{site_id}/{datetime}.png and linked to the CanaryBaseline record via screenshot_storage_path.

    Taking a screenshot requires a ScrapingBee API key configured in your team settings. Each screenshot costs 5 API credits (compared to 1 credit for an HTML fetch). The screenshot process includes JavaScript execution with a scenario that scrolls the full page and waits for lazy-loaded images to render.

    Notifications

    When a canary check returns an Alert status, WPHammer sends a CanaryAlertNotification to the team owner. The notification includes details about what changed and a link to review the findings.

    Updating the baseline

    Baselines should be updated after you make intentional changes to your site — such as updating a theme, adding a plugin, or restructuring navigation. When you set a new baseline via the SetCanaryBaselineJob, the previous baseline is deactivated (is_baseline set to false) and a new one is created from the current site state.

    The baseline job tracks activity so you can see who set it and when in the activity log.

    Data retention

    Non-baseline canary check records are pruned by the PruneCanaryChecks command. By default, checks older than 30 days are deleted along with their associated screenshots. The retention period is configurable via the --days flag. A --dry-run option is available to preview what would be deleted.

    Baseline records are never pruned — only the most recent baseline (with is_baseline set to true) is used for comparisons, but historical baselines are preserved.

    Related