Security Guide
MCP server Vibration API security — navigator.vibrate() social engineering, battery drain, and physical covert channel
The Vibration API lets JavaScript produce physical haptic output on the device's vibration motor via navigator.vibrate() — no permission prompt required, no Permissions-Policy directive to block it. In an MCP server context tool output can mimic urgent OS notification vibration patterns to manipulate the user into checking their device, induce continuous vibration to drain battery, or encode binary data in precisely timed vibration pulses that a nearby device with an accelerometer can read back — forming a physical-layer covert channel that bypasses all network security controls.
What the Vibration API provides
The Vibration API is defined by the W3C Vibration API specification. It exposes a single method on the Navigator interface:
// Three forms of navigator.vibrate() // 1. Single pulse: vibrate for N milliseconds navigator.vibrate(200); // 2. Pattern: alternating [vibrate, pause, vibrate, pause, ...] navigator.vibrate([200, 100, 200, 100, 600]); // SOS-style pattern // 3. Cancel ongoing vibration navigator.vibrate(0);
The API requires no permission. It functions in any document with user-activation or — in most browsers — without it when invoked from a script context. It is available on Android (Chrome, Firefox), some desktop Linux distributions with haptic hardware, and in WebViews on Android.
Social engineering via haptic urgency patterns
Mobile operating systems have established vibration patterns that users associate with specific events. An attacker with knowledge of these patterns can mimic them in MCP tool output to manipulate user attention:
| Pattern | Mimicked event | Social engineering goal |
|---|---|---|
[100, 50, 100] |
New message / notification (Android default) | User picks up phone, unlocks device, becomes distracted from MCP session |
[500, 200, 500, 200, 500] |
Urgent alert or alarm pattern | User feels physical urgency; more likely to approve a pending permission dialog or click a confirmation |
Array(120).fill(50).flatMap(v => [v, 30]) |
Continuous rumble | Device feels faulty; user may dismiss MCP application or reboot, aborting a security review or audit in progress |
[50, 50, 50, 50, 50] |
Incoming call (pulse pattern) | User looks at device expecting a call; disrupts attention during sensitive tool output review |
No permission prompt, no visual indicator. Unlike Camera or Microphone access — which show a hardware indicator light or browser chrome badge — vibration produces no visible UI feedback. There is no way for a user to know that a web page or MCP tool response triggered the vibration without inspecting JavaScript execution. The user's natural assumption is that the vibration came from their OS or another app.
Physical covert channel via vibration timing
A more sophisticated attack uses the Vibration API to create a physical-layer covert channel. The premise: a device vibrating in precise patterns creates physical displacement in its mounting surface (table, desk, pocket). A nearby device with an accelerometer or microphone can detect the displacement pattern and decode it:
// Vibration-based covert binary encoding
// Bit 1 = vibrate 100ms; Bit 0 = vibrate 50ms; separator = 200ms pause between bits
function encodeVibratePayload(text) {
const bytes = new TextEncoder().encode(text);
const pattern = [];
for (const byte of bytes) {
for (let bit = 7; bit >= 0; bit--) {
const isOne = (byte >> bit) & 1;
pattern.push(isOne ? 100 : 50); // vibrate duration encodes bit value
pattern.push(200); // pause between bits
}
pattern.push(500); // byte separator
}
return pattern;
}
// Transmit string data physically — readable by accelerometer on same desk
navigator.vibrate(encodeVibratePayload('apikey=sk-...'));
Practical transmission rate is low (a few bytes per second), but for short payloads like API keys, session tokens, or single passwords, the channel is viable. The receiving device requires only an accelerometer and JavaScript — the same Generic Sensor API surface covered in the Generic Sensor API security guide.
The Vibration API is not available in browsers on desktop platforms. Chrome and Firefox on macOS and Windows return false from navigator.vibrate() even when called. The attack surface is primarily Android WebView-based MCP clients and Android Chrome. iOS does not expose the Vibration API to web content at all (WebKit intentionally omitted it).
Battery drain attack
The vibration motor is one of the highest power consumers in a mobile device, comparable to the display at full brightness. Continuous vibration from a long-running MCP tool response can noticeably drain battery:
// Continuous vibration via repeating pattern call
// navigator.vibrate() called every 5 seconds with a 4900ms vibration
function startDrain() {
navigator.vibrate(4900);
// Reschedule before current vibration ends
setTimeout(startDrain, 4950);
}
startDrain();
At typical motor power consumption of 40–60 mW on mid-range Android devices, continuous vibration can discharge a 4000 mAh battery by 2–4% per hour. While not a rapid DoS, it contributes to battery exhaustion on a device left running an MCP session overnight and may serve as a distraction or interference mechanism rather than a primary attack.
No Permissions-Policy directive — what defenses exist
Unlike the Generic Sensor API, the Vibration API has no corresponding Permissions-Policy directive. Browser vendors have discussed restricting it to user-activation contexts, but the current W3C specification does not require activation and browsers ship varied behavior. Available defenses:
| Defense | Blocks | Cost |
|---|---|---|
Cross-origin iframe sandbox without allow-vibration |
Vibration API completely blocked for sandboxed cross-origin iframes | Requires cross-origin tool output rendering architecture |
CSP script-src restricting inline scripts |
Prevents inline navigator.vibrate() calls in tool output HTML |
Requires strict CSP; does not block scripts loaded from allowed origins |
MCP client side: strip or sanitize navigator.vibrate calls from tool output before rendering |
Prevents vibration from rendered tool output HTML | Requires output sanitization in MCP client renderer |
Findings SkillAudit reports
navigator.vibrate() calls with encoded data patterns — physical covert channel or deliberate social engineering payload identified
Related guides: Generic Sensor API security, DeviceMotionEvent attacks, AmbientLightSensor covert channel.
Get a graded audit. Paste your MCP server's GitHub URL at skillaudit.dev for a graded security report covering the Vibration API, Generic Sensor API, and the full browser permission surface — in 60 seconds.