Introduction
A stored cross site scripting vulnerability in the Optimole WordPress plugin allows unauthenticated attackers to inject persistent JavaScript payloads into any page that renders optimized images, affecting over 200,000 active WordPress installations. The flaw is particularly notable because the plugin's own HMAC based endpoint protection is undermined by leaking the required tokens directly in the frontend HTML, turning what appears to be a secured API into an open door.
Optimole is a popular WordPress plugin that provides real time image optimization, automatic WebP and AVIF conversion, CDN delivery, and lazy loading. With over 200,000 active installations and nearly 8 million total downloads, it occupies a significant footprint in the WordPress ecosystem. Any security flaw in a plugin this widely deployed immediately becomes a high value target for automated exploitation campaigns.
Technical Information
The Unauthenticated Endpoint
The vulnerability lives in the /wp-json/optimole/v1/optimizations REST endpoint, registered in rest.php. The endpoint's permission callback is set to __return_true, which means WordPress does not enforce any authentication on incoming requests. The developers instead implemented their own authorization scheme using an HMAC signature and a timestamp. The critical design flaw is that both the HMAC signature and the timestamp are embedded directly in the frontend HTML of every page that uses Optimole. Any visitor can view the page source, extract these values, and use them to construct fully valid API requests.
Sanitization Gap in sanitize_text_field()
The endpoint accepts a user supplied s parameter that functions as a srcset descriptor. On line 1008 of rest.php, the plugin sanitizes this input using WordPress's sanitize_text_field() function. This function strips HTML tags and removes extra whitespace, but it does not escape double quote characters. This is the precise gap that enables the attack: a payload containing double quotes passes through the sanitization step completely intact.
Storage and Retrieval: The Stored XSS Chain
After sanitization, the plugin stores the descriptor value via WordPress transients, which are backed by the wp_options database table. The payload now persists in the database.
When any user visits a page where Optimole renders images, tag_replacer.php (around line 526) retrieves the stored descriptor and injects it directly into the srcset attribute of an <img> tag. No output escaping is applied at this stage. Because double quotes were never neutralized, the attacker's payload breaks out of the srcset attribute context and can introduce arbitrary HTML attributes, including JavaScript event handlers like onload or onerror.
Step by Step Attack Flow
- Token Extraction: The attacker visits a target WordPress site running a vulnerable version of Optimole and extracts the HMAC signature and timestamp from the page's HTML source.
- Payload Delivery: Using the extracted tokens, the attacker sends a crafted POST request to
/wp-json/optimole/v1/optimizationswith a malicioussparameter. The value contains double quotes designed to break out of thesrcsetattribute, followed by a JavaScript event handler. - Sanitization Bypass: The endpoint validates the HMAC (which succeeds because the attacker used real tokens), then passes the descriptor through
sanitize_text_field(). HTML tags are stripped, but the double quotes remain. - Database Persistence: The poisoned descriptor is stored as a WordPress transient in the
wp_optionstable. - Payload Execution: When any user, including site administrators, loads a page where Optimole renders images,
tag_replacer.phpretrieves the stored descriptor and injects it verbatim into thesrcsetattribute. The double quotes break the attribute boundary, and the injected JavaScript executes in the victim's browser session.
This chain is entirely unauthenticated and requires no user interaction on the injection side. The payload persists across page loads and affects every visitor to the compromised page.
Affected Systems and Versions
All versions of the Optimole plugin (also known as "Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization") up to and including version 4.2.2 are affected by CVE-2026-5217.
The vulnerability is fixed in version 4.2.3. However, version 4.2.3 itself introduced a separate Reflected XSS vulnerability (CVE-2026-5226), so the recommended minimum safe version is 4.2.4.
Any WordPress site with the optimole-wp plugin installed at version 4.2.2 or earlier is vulnerable, regardless of WordPress core version or hosting configuration.
Vendor Security History
The vendor, Codeinwp, responded quickly to the disclosure of CVE-2026-5217. Version 4.2.3 was released on April 1, 2026, with commit messages explicitly referencing "fix: prevent cross site scripting" and "Enhance security." Two days later, on April 3, 2026, version 4.2.4 was released to address CVE-2026-5226, a Reflected XSS vulnerability in the page profiler URL that was present in the 4.2.3 release.
The rapid succession of two XSS vulnerabilities in adjacent releases underscores the difficulty of comprehensively securing REST API endpoints in WordPress plugins, even for an actively maintained project with a large user base.
References
- NVD Entry for CVE-2026-5217
- Wordfence Threat Intel: Optimole <= 4.2.2 Unauthenticated Stored XSS
- Wordfence Threat Intel: Optimole <= 4.2.2 Stored XSS via Srcset Descriptor
- Wordfence Threat Intel: Optimole <= 4.2.3 Reflected XSS (CVE-2026-5226)
- Wordfence: Optimole Plugin Vulnerability History
- rest.php (tag 4.2.1, line 159) in WordPress Plugin Repository
- rest.php (tag 4.2.1, line 1008) in WordPress Plugin Repository
- tag_replacer.php (tag 4.2.1, line 526) in WordPress Plugin Repository
- rest.php (trunk, line 159) in WordPress Plugin Repository
- rest.php (trunk, line 1008) in WordPress Plugin Repository
- tag_replacer.php (trunk, line 526) in WordPress Plugin Repository
- Optimole Plugin on WordPress.org
- GitHub Release v4.2.3
- GitHub Diff v4.2.2 to v4.2.3
- Optimole Changelog



