Content-Type

Content-Type

October 1, 2020

Leaking the Content-Type of a request would provide attackers with a new way of distinguishing two requests from each other.

typeMustMatch #

typeMustMatch is a Boolean that reflects the typeMustMatch attribute of the object element. It ensures that a certain MIME type must be enforced when loading an object, by verifying if the Content-Type of the resource is the same as the one provided in the object. Unfortunately, this enforcement also allowed attackers to leak the Content-Type and Status Codes returned by a website 1.

Root Cause #

Considering the snippet below, not_loaded would be rendered if the returned Content-Type of https://target/api did not match the one in type, or if the server returned a status different than 200.

<object type="application/json"
        data="https://example.org"
        typemustmatch>
not_loaded </object>

Issues #

An attacker could leak the Content-Type and Status Codes of a website by detecting whether the object rendered, which happens when all conditions are met. The attacker could check the values of clientHeight and clientWidth which are likely to be different than 0 when the object renders (and returns status 200). Since typeMustMatch requires the server to return status 200 to load a resource, it would be possible to detect error pages, similar to Error Events XS-Leaks.

The example below shows how this behavior could be detected by embedding an object inside an iframe and checking the values of clientHeight and clientWidth when the iframe triggers the onload event.

// Set the destination URL
var url = 'https://example.org';
// The content type we want to check for
var mime = 'application/json';
var ifr = document.createElement('iframe');
// Load an object inside iframe since object does not trigger onload event
ifr.srcdoc = `
  <object id="obj" type="${mime}" data="${url}" typemustmatch>
    error
  </object>`;
document.body.appendChild(ifr);

// When the iframe loads, read the height of the object. If it is the height 
// of a single line of text, then the content type of the resource was not 
// `application/json`. If it is a different height, then it was `application/json`. 
ifr.onload = () => {
    console.log(ifr.contentWindow.obj.clientHeight)
};

Fix #

Firefox was the only browser that supported the typeMustMatch attribute 2, and since no other browsers offered support, it was removed in version 68 and from the HTML Living Standard.

References #


  1. Cross-Site Content and Status Types Leakage, link ↩︎

  2. Remove support for typemustmatch, link ↩︎