Cross-Origin Security Restrictions in WebGL

WebGL enforces strict security boundaries governed by the Same-Origin Policy (SOP) and Cross-Origin Resource Sharing (CORS) mechanisms to prevent malicious websites from extracting sensitive data across domains. Because WebGL allows direct GPU access and programmatic reading of pixel data, unauthorized cross-origin images, videos, and canvas elements are strictly barred from being uploaded as textures without explicit cryptographic and server authorization.

The Core Threat: Information Leakage

In a standard HTML context, browsers allow elements like <img> to render cross-origin resources without CORS. However, WebGL fundamentally changes the security equation. Fragment shaders can inspect, sample, and manipulate pixel values directly, and operations like gl.readPixels() allow raw graphical memory to be extracted back into JavaScript.

Without restrictions, an attacker could load a sensitive document—such as a user's webmail inbox, banking dashboard, or intranet portal—into a WebGL texture and use shaders to read the text and imagery byte-by-byte. Even without direct pixel-read APIs, subtle timing attacks on GPU execution times can infer the content of loaded images.

Canvas Tainting vs. WebGL Execution Blocks

The standard HTML5 2D Canvas allows untrusted cross-origin images to be drawn, but it applies a "tainted" flag that permanently disables methods like toDataURL(), toBlob(), or getImageData().

WebGL adopts a stricter posture:

  • WebGL completely refuses to load a cross-origin element into texture memory using texImage2D or texSubImage2D unless proper CORS validation has occurred.
  • If an application attempts to pass an untrusted cross-origin source into a WebGL context, the browser immediately throws a SECURITY_ERR DOM exception, halting the texture upload entirely.

CORS Requirements for Textures

To load an external visual resource—such as an image or video hosted on a Content Delivery Network (CDN)—into a WebGL texture, two criteria must be satisfied:

  1. Client-Side Declaration: The HTML element must explicitly request cross-origin privileges before the asset begins loading. This is achieved by setting the crossOrigin property:
    const image = new Image();
    image.crossOrigin = "anonymous";
    image.src = "https://cdn.example.com/texture.jpg";
  2. Server-Side Authorization: The remote server hosting the asset must respond to the HTTP request with the appropriate CORS header:
    Access-Control-Allow-Origin: *
    Or explicitly specify the requesting origin:
    Access-Control-Allow-Origin: https://yourdomain.com

If the server omits this header or explicitly rejects the origin, the image will trigger an onerror event, and passing it to gl.texImage2D() will fail.

Redirects and HTTP Caching Pitfalls

Browsers cache resources based on their request headers. If an image is first loaded by an ordinary <img> tag without the crossorigin attribute, the browser may cache the response without CORS headers. If WebGL later attempts to load the same asset with crossOrigin = "anonymous", the browser might serve the cached version lacking the Access-Control-Allow-Origin header, causing an unexpected security failure. Assets intended for WebGL textures must consistently use CORS headers across all requests or be served with appropriate Vary: Origin HTTP headers.