The WebGL API provides a low-level, shader based, 3D graphics API based on OpenGL ES 2.0 that renders directly into an HTML5 Canvas element. As an API for the World Wide Web, WebGL necessarily conforms to the security principles of the web platform, and was designed with security in mind from day one. This white paper provides an overview of some of the security aspects of the WebGL specification and its implementations.
In the OpenGL ES 2.0 specification, behavior is left undefined in various circumstances, generally in order to achieve the highest performance for native applications on the system. On the web platform, undefined behavior makes it impossible to write strong conformance tests, and may introduce security vulnerabilities. For these reasons, all undefined behaviors in OpenGL ES that can affect security have been defined in the WebGL specification.
As one concrete example, consider the readPixels API. In OpenGL ES, if the rectangular region for a particular ReadPixels call extends outside the context's frame buffer, the values for the pixels outside the frame buffer are undefined (see the OpenGL ES 2.0 specification, version 2.0.25, section 4.4.5, p. 105). This behavior is clearly not acceptable for a web API, since it is conceivable that video memory belonging to another application might be returned for these pixels. The WebGL API defines that pixels outside the frame buffer will contain the value (0, 0, 0, 0), and the conformance suite verifies this behavior. WebGL implementations enforce this behavior while attempting to minimize its performance impact; for example, if a given readPixels call is completely within the bounds of the frame buffer, no extra work is necessary.
Out of Range Memory Accesses
Historically, in order to achieve the highest performance for trusted applications on the system, 3D graphics APIs such as OpenGL, OpenGL ES or Direct3D did not perform range checking for most operations. As one example, all of these APIs provide a facility to draw indexed geometry; the application can supply both a set of vertices and a separate set of indices to describe a 3D model. Until fairly recently, none of these APIs verified that each index referred to a valid vertex. In an API like WebGL where untrusted code makes 3D graphics calls, it is essential to ensure that all of the indices are valid, to avoid potential out of range memory accesses. For this reason, the WebGL specification tightly defines the behavior in this particular area. Out of range memory writes are an even more serious potential problem; buffer overruns are a common source of security vulnerabilities on CPUs.
The WebGL specification defines security measures for every possible kind of out-of-range indexing operation in the OpenGL ES API, and the conformance suite guarantees secure operation. The security measures preventing out-of-range memory accesses may, in some cases, reduce overall performance. However, relatively new APIs like Direct3D 10, and OpenGL extensions such as GL_ARB_robustness, provide stronger guarantees about out of range memory accesses which implementations can take advantage of to avoid any performance reduction. The WebGL working group will encourage future GPUs to be designed to minimize any performance impact.
Access to Uninitialized Memory
Again to achieve the highest performance, 3D graphics APIs do not typically clear the contents of newly allocated GPU resources such as textures and vertex buffers. A typical game, upon allocating such a resource, will immediately fill it with data, so zeroing the newly allocated region would impact performance. In the WebGL API, allowing an application to observe potentially uninitialized GPU memory carries high risk; for example, it might be possible to see stale contents of other windows on the system. For this reason, all GPU resources allocated by a WebGL application are initially cleared to zero. While this mandatory clearing may impact performance, WebGL implementations may try to optimize real world scenarios, for example if the entire contents of a texture are specified during its allocation.
Shader Validation and Transformation
WebGL adopts the OpenGL ES Shading Language (ESSL) to describe its vertex and fragment shaders, and reserves some additional identifiers for use by implementations. Restrictions above and beyond those in core ESSL are imposed on WebGL shaders, such as limitations on the structure of loops and indexing expressions in fragment shaders. These restrictions are to achieve maximum portability of WebGL content, not for security reasons. However, a WebGL implementation typically always validates that incoming shaders conform to the ESSL standard before passing the shader to the underlying 3D graphics API, even if running on top of OpenGL ES, so that uniform behavior is achieved on all platforms.
Some transformations to incoming shaders are performed; for example, WebGL supports variable names of a certain length, but there is no guarantee that the shader compiler in the underlying 3D graphics API will do so. Therefore, many WebGL implementations will rename variables in shaders so that they do not exceed a certain length. A transformation such as this is done for security reasons, to avoid buffer overruns in the system's shader compiler.
Denial of Service
If a particular draw call takes a long time to execute, because it contains very many triangles, because the associated shaders are computationally expensive, or for any other reason, the user's system may become unresponsive. This is a longstanding problem in the 3D graphics domain, and is one which has received renewed attention since WebGL has been released, because WebGL allows unknown and untrusted code to access the graphics processor.
Solutions already exist to this problem on some operating systems. For example, Microsoft Windows Vista and later support a new driver model which will reset the graphics processor if it spends too long on any particular operation. The WebGL implementation can detect that the graphics card was reset, warn the user that WebGL content might have caused it, and prompt the user if they want to continue running the content.
The Khronos group has introduced the GL_ARB_robustness OpenGL extension as a platform-independent way to prevent denial of service attacks. A WebGL implementation can use this extension to receive notifications that the graphics card was reset and respond in the manner described above. As of this writing, some GPU vendors support GL_ARB_robustness, while others are in the process of supporting it, and browser vendors are incorporating it into their WebGL implementations. In order to support robust and widespread deployment of WebGL, it is anticipated that browsers may soon require the presence of the GL_ARB_robustness extension in order to enable WebGL content. Follow-on extensions to GL_ARB_robustness are under development which will provide stronger guarantees about the potential side effects one application may have on another when the GPU is reset.
It is important to note that draw calls can take a long time even without loops in the shaders. Removing loops from the shading language is therefore not a solution to denial of service attacks.
Starting with version 1.0.1, the WebGL specification enforces these restrictions by specifying that cross-origin images and video may only be used as WebGL textures if they have been validated by Cross-Origin Resource Sharing (CORS). By this mechanism, a server can indicate that it is safe to allow scripts to read the pixels of an image, for example because the image does not contain confidential data. Images and video that are not validated by CORS cannot be used as WebGL textures, at all.
A bit of history
The first version of the WebGL specification, 1.0, imposed similar restrictions on the use of cross-origin images and videos, as the 2D Canvas Context does. In other words, it was allowed to use any cross-origin image and video, but doing so tainted the canvas so it was not possible to read its pixels anymore. During the development of the specification, it was noted that because arbitrary shader code can be uploaded to WebGL, it might be possible to cause the shader to run much longer if a given pixel's value was more or less than a certain brightness level, and thereby infer the contents of the image. A proof-of-concept attack implementing this idea was released in May 2011, demonstrating the feasibility of such a timing attack.
In response, Khronos and the WebGL working group have worked with the HTML5 working group to add support for CORS to image and video elements, and updated the WebGL specification to make CORS approval a strict requirement for the usage of cross-origin images and video, as explained above. This change is part of the WebGL 1.0.1 specification, resolving this issue. The Khronos group and WebGL working group are looking forward to working with media hosting services to ensure widespread support for CORS so that advanced WebGL applications can use appropriate cross-origin resources.
Blacklisting graphics drivers
In order to provide the best user experience, browsers may selectively enable or disable support for WebGL, or certain sub-features, in certain situations. This special treatment is typically used to work around stability or conformance problems. WebGL implementations use blacklists and whitelists to encode the rules determining whether WebGL is allowed to run. WebGL-enabled browsers are typically able to update their blacklists quickly without requiring a software update, providing an opportunity for GPU and system vendors to deploy updated drivers without compromising the security of user systems.
WebGL brings the power of GPU accelerated 3D graphics to the web platform with the utmost consideration for security concerns.
There are no GLSL ES commands or drawing commands in WebGL that can be used to access memory outside the bounds defined by the specification. The GPU's registers or other internal state cannot be accessed. To do so would require providing invalid input to the OpenGL driver, which is disallowed by the specification. For example, the size of a vertex buffer is defined at its creation, and data can not be set outside this valid range. vertexAttribPointer settings cannot be made in a way that would give access outside the valid range of the created and bound buffers. Out-of-range indices cannot be used for drawing. Uncleared renderbuffers cannot be accessed. Uncleared vertex buffers cannot be accessed. Uncleared textures cannot be accessed. Even an uncleared back buffer cannot be accessed.
There are no arbitrary data writing commands in GLSL. The WebGL specification requires all array accesses to be safe. A shader cannot address outside a texture; there is no such concept in OpenGL. A shader cannot draw with unbound or uninitialized textures.
The only remaining issue is denial of service as described above. This is not just an issue for WebGL but for any technology allowing web applications to issue drawing commands. It will be solved by the graphics driver preempting or killing offending contexts with functionality such as that described in the robustness extensions.
The Khronos group and WebGL working group are looking forward to widespread deployment of WebGL in a robust and secure manner.