Contents
Content-Typeは基本的に拡張子で決まる
Apacheの場合、HTTPヘッダのContent-Typeはファイルの拡張子から決めます。
例えば以下のようにJavaScriptを読み込んだ場合のContent-Typeを見てみます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>example.com</title>
<script src="./jquery.js"></script>
</head>
<body>
<p>test</p>
</body>
</html>
Content-Typeは下図のようにapplication/javascriptとなります。
このjsファイルの拡張子をjpgにしてimgタグで読み込んでみます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>example.com</title>
</head>
<body>
<p>test</p>
<img src="jquery.jpg">
</body>
</html>
その結果、Content-Typeはimage/jpegになっています。
ApacheにおけるContent-Typeの設定
Apacheの場合、デフォルトだと以下のようにhttpd.confで指定されたmime.typesファイルに拡張子ごとのMIMEが定義されています。
<IfModule mime_module>
#
# TypesConfig points to the file containing the list of mappings from
# filename extension to MIME-type.
#
TypesConfig conf/mime.types
MIME type (lowercased) Extensions
image/jpeg jpeg jpg jpe
application/javascript js
application/json json
Apacheは上記のリストに従って、拡張子ごとにContent-Typeを設定しています。
Content-Typeの設定を拡張子任せにしない
以下のようなJSONを返すphpプログラムの場合、先述したようにContent-Typeは拡張子で判断されるため、そのままだと「Content-Type: text/html」でレスポンスされます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>example.com</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/json.php');
xhr.send();
</script>
</body>
</html>
<?php
echo json_encode(['status' => 'OK']);
PHPのドキュメントにも記載されていますが、このような場合にはヘッダを明示的に設定する必要があります。
<?php
header('Content-Type: application/json');
echo json_encode(['status' => 'OK']);
なお、Laravelのようなフレームワークだと、JSONでレスポンスを返すメソッドを使えば自動でContent-Typeを変更してくれたりします。
jsonメソッドは自動的にContent-Typeヘッダをapplication/jsonにセットし、同時に指定された配列をjson_encode PHP関数によりJSONへ変換します。
Laravel 8.x HTTPレスポンス | readouble.com
不適切なContent-Typeによるセキュリティ上の問題
例えば画像ファイルのみをアップロード可能なフォームに、拡張子をjpgにしたスクリプトファイルをアップロードした際、Content-Typeがtext/htmlと判断されてしまうと、スクリプトが実行されてしまう可能性があります。
この場合、通常であればWebサーバからContent-Typeがimage/jpegとしてレスポンスされるためスクリプトの実行には至らないはずですが、後述するIEの仕様のように、Content-Typeとファイルの内容が異なることによって生じる問題もあるため、必ず適切なContent-Typeをレスポンスるする必要があります。
X-Content-Type-Options: nosniff
Content-Typeを適切に設定することは大事ですが、古いIEにはContent-Typeを無視してファイルの中身からMIMEを判断してオープンするという、今でも話題に挙がる問題があります。
例えば、以下のような「テキストファイル」をIEで開いたとします。
> HTTP/1.1 200 OKContent-Type: text/plain> <br/>
> これはテキストファイルです。
> <script>alert(“xss”);</script>
[無視できない]IEのContent-Type無視 | atmarkit.itmedia.co.jp
この場合、IEは“text/plain”と指定されたContent-Typeヘッダに従わず、コンテンツ内に含まれる内容から「HTML」であると判断して、そこに含まれるスクリプトを実行してしまいます。
このような挙動を避けるため、以下のHTTPヘッダを問答無用で付けるという対策がよく行われています。
X-Content-Type-Options: nosniff
現在では、X-Content-Type-Options: nosniff はIE以外のブラウザにも実装されており、セキュリティ強化に広く寄与するので、すべてのHTTPレスポンスに対して指定することを強く推奨します。
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版