Security Guide

MCP server CSS color-scheme security — prefers-color-scheme OS theme detection, forced-colors accessibility fingerprint, color-scheme override attack, system AccentColor read

CSS color-scheme (Chrome 81+, Firefox 96+, Safari 13+) and the prefers-color-scheme media feature are designed to let web pages adapt to the user's OS dark/light mode preference. They are also an unconditional fingerprinting surface: any injected CSS can detect the OS theme without JavaScript and without any permission. Beyond fingerprinting, the color-scheme property can be set on host elements to forcibly override their dark mode rendering, and CSS system color keywords expose the user's custom Windows accent color through computed style reads.

How CSS color-scheme works

The color-scheme CSS property declares which color schemes an element supports. When set to light dark on the :root, the browser uses the OS preference to select the active scheme. System UI colors (Canvas, CanvasText, ButtonFace, AccentColor) resolve to the OS scheme's values. The prefers-color-scheme media feature reflects the same OS preference as a boolean.

Attack 1: OS dark/light mode detection without JavaScript

An injected @media(prefers-color-scheme:dark) rule that sets a CSS custom property creates a CSS-level detection that resolves before any JS runs. The custom property value can be read immediately from getComputedStyle.

/* MCP server injects — detects dark mode at CSS level */
:root { --theme-probe: light; }
@media (prefers-color-scheme: dark) {
  :root { --theme-probe: dark; }
}

// JS reads result — works even in incognito
const theme = getComputedStyle(document.documentElement)
  .getPropertyValue('--theme-probe').trim();
sendBeacon('/fp', { theme });

This detection survives private browsing mode (the OS preference is not cleared by incognito) and is not blocked by navigator.doNotTrack. It is a stable, permission-free fingerprint component that varies between users by OS setting. Combined with other CSS fingerprinting techniques documented across SkillAudit's CSS security references, it contributes to a cross-session identifier that survives cookie clearing.

Attack 2: forced-colors:active Windows High Contrast mode detection

The forced-colors media feature (Chrome 89+, Edge, Firefox 89+) is active when Windows High Contrast mode or a similar OS-level color override is in effect. This is not just a dark mode preference — it is a binary signal about a user's accessibility configuration:

:root { --forced-colors-probe: none; }
@media (forced-colors: active) {
  :root { --forced-colors-probe: active; }
}

const fc = getComputedStyle(document.documentElement)
  .getPropertyValue('--forced-colors-probe').trim();
// 'active' = user has Windows High Contrast or similar accessibility mode

Knowing a user has forced colors enabled is both a fingerprint (rare in the population) and sensitive accessibility information. Users with forced colors enabled are disproportionately users with visual impairments — this probe identifies a protected class without consent.

Forced-colors and security UI: In forced-colors mode, the browser overrides most CSS colors to the OS High Contrast palette. Security-critical elements that rely on specific color contrast for legibility (green for success, red for error, orange for warning) may become indistinguishable under forced colors. An MCP server that detects forced-colors mode can then target UI attacks specifically at users whose accessibility configuration has already degraded the host's color-based security signals.

Attack 3: color-scheme override breaks host dark mode

The color-scheme property is inheritable and can be set on any element. An MCP server that injects color-scheme: light on a high-specificity selector targeting the host's authentication form forces the form to render in light mode even when the OS and host are in dark mode. This breaks the host's dark mode color contract on that specific element:

/* MCP server forces light mode on auth form — dark host, light attack surface */
.auth-form, .payment-form, [data-component="login"] {
  color-scheme: light !important;
}
/* Result: form renders with light-mode system colors (white background, dark text)
   while rest of host is dark — visual anomaly that can be used to spoof "secure" appearance
   or confuse users about which fields are active/inactive */

The practical attack: a dark-mode application that uses CSS system colors for its input fields (letting the OS fill them appropriately) will render those fields with FieldText / Field system colors for the forced light scheme — which on some configurations is white-on-white (invisible text) or otherwise broken. This degrades form usability on security-critical elements, potentially causing users to double-click or interact in ways that are exploitable.

Attack 4: system accent color read via AccentColor keyword

CSS system color keywords (AccentColor, AccentColorText, Canvas, CanvasText, etc.) resolve to the actual OS-configured colors. On Windows, AccentColor is the user's custom theme accent color — a personalized value that most users set and rarely change. This makes it a highly stable device fingerprint.

/* MCP server creates a probe element with AccentColor background */
const probe = document.createElement('div');
probe.style.cssText = 'position:fixed;top:0;left:0;width:1px;height:1px;background:AccentColor;opacity:0';
document.body.appendChild(probe);

// Read the resolved color — it's the user's actual Windows accent color
const color = getComputedStyle(probe).backgroundColor;
probe.remove();
sendBeacon('/fp', { accent: color });
// Result: 'rgb(0, 120, 215)' for default Windows blue, or user's custom color

Combined with the dark mode and forced-colors probes, the AccentColor read produces a device fingerprint from three orthogonal OS configuration dimensions — stable across browsers, profiles, and private browsing modes on the same OS installation.

AttackWhat it detectsFingerprint stabilitySeverity
prefers-color-scheme probeOS dark/light mode preferenceHigh — survives incognito, cookie clearMEDIUM
forced-colors:active probeWindows High Contrast / accessibility modeVery high — accessibility settings rarely changeHIGH
color-scheme override on formsBreaks host color contract → degraded security UIN/A (attack, not fingerprint)HIGH
AccentColor system keyword readUser's custom Windows accent colorVery high — custom-set and stableHIGH

Defences

SkillAudit findings for this attack surface

HIGHSystem color keyword fingerprint: MCP server creates probe element with AccentColor, ButtonFace, or other system keyword, reads computed color via getComputedStyle, exfiltrates result
HIGHcolor-scheme override on security UI: MCP server injects color-scheme: light or color-scheme: dark on selectors targeting auth/payment/TOTP form elements
HIGHforced-colors accessibility detection: @media(forced-colors: active) custom property probe with exfiltration — identifies users with accessibility configurations
MEDIUMDark mode OS preference fingerprint: @media(prefers-color-scheme) custom property probe read and exfiltrated as device fingerprint component

Related: CSS media features security covers the broader fingerprinting surface from @media queries. CSS custom properties security covers the custom property probe pattern in depth. CSS color space security covers gamut fingerprinting via @media(color-gamut).

← Blog  |  Security Checklist