😽

X-Content-Type-Options: nosniff が効く条件と簡単な確認

2023/12/18に公開

軽いメモ程度です

X-Content-Type-Options: nosniff とは

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Content-Type-Options

MDNによる X-Content-Type-Options: nosniff の説明は日本語ページでは以下のようになっています。

X-Content-Type-Options は HTTP のレスポンスヘッダーで、 Content-Type ヘッダーで示された MIME タイプを変更せずに従うべきであることを示すために、サーバーによって使用されるマーカーです。

それは、スクリプト等を読み込むHTML側のレスポンスヘッダなのか、スクリプト自身のレスポンスヘッダなのか、検索して出てきた情報だけではわかりにくいなと思ってまとめておきます。

結論

結論としては、スクリプト・スタイル等の本体側にこのヘッダがついている場合にこのヘッダは働きます。

また、追加でわかったこととしては、そもそも <script src="foo.js" /> のように読み込んでおきながら、Content-Type が image/jpeg のようなものだったら弾くようで、少なくとも私が確認できたのは text/html のときにブロックするか否かを返すもので、いわば、 X-Content-Type-Options: nosniff をつけることで、 Content-Type もちゃんと設定してますよ〜と意思表示する、みたいなものと捉えられそうです。

image/jpeg を nosniff なしでブロックするのは特に Living Standard には記述がないような気がするんですが、私が見つけれていないだけかもしれないので、Chromeの独自仕様なのかどうかはわかりません。

Firefoxも同様の挙動でしたが、そのエラー文の [Learn More] を押すと上記のページが出てきており、 top-level なドキュメントに対しては自動でMIME sniffingをしないようにする、と書かれています。

規格

一応、規格上は https://fetch.spec.whatwg.org/#x-content-type-options-header に記述されております。

今読み返すとわかるような気もするのですが、以下で一応実験しています。

実験

以下のTypeScriptファイルを用意して、

import express from 'express';
const app = express();

app.get('/', function (req, res) {
  // こちらにつけてもつけなくても、スクリプト等の読み込みブロックには関係ない
  // res.header('X-Content-Type-Options', 'nosniff');
  res.send(`<!DOCTYPE html>
  <html>
  <body>
    hello!
    <div id="main"></div>
    <script src="/foo.js"></script>
  </body>
  </html>
  `);
});

app.get('/foo.js', function (req, res) {
  // ある場合は、(Content-Typeがなければデフォルトで text/html 扱い、以下のように明示しても同じ)になるが、JSのMIMEではないので弾かれる
  res.header('Content-Type', 'text/html');
  // これがあるかないかで結果が変わる
  res.header('X-Content-Type-Options', 'nosniff');

  // 逆にこれだけを入れても勝手にブロックされる?独自仕様?
  // res.header('Content-Type', 'image/jpeg');

  res.send(`
  console.log('foo');
  `);
});

const port = 3333;
console.log(`Server listening on http://localhost:${port}`);
app.listen(port);

a.ts で保存してるなら、以下のような感じでやれば起動できまして、

npm init -y
bun i express@4.18.2
# bun i -D @types/express @types/node
bun run ./a.ts

あとはブラウザで https://localhost:3333 を開いて、スクリプトがnosniffが要因でブロックされていることが確認できます。

expressはこういうとき便利だなって思います。

その他の場合

Content-Type: image/jpeg + nosniffはなし

Content-Type: image/jpeg + nosniffはなし (Firefox)

バージョン情報

  • Chrome: Version 120.0.6099.109 (Official Build) (arm64)
  • Firefox: 120.0.1 (20231129155202)
GitHubで編集を提案
OPTIMINDテックブログ

Discussion