15
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JPEGファイルのEXIFヘッダは危険(かもしれない)

Last updated at Posted at 2013-08-30

 これ以上ない小ネタですw

 JPEGファイルのEXIFヘッダは、位置情報を埋め込んだりできて便利(?)ですが、先日このEXIFヘッダにPHPコードを仕込むマルウェアが発見されており、画像を自由に登録できるサービスを提供している場合は注意が必要です。
(実際には、この方法でサイトの閲覧者に任意のコードを実行するにはEXIFヘッダに埋め込まれたコードを取得&実行するスクリプトを事前にサーバに配置しておかなければならないため、危険度はそれほど高くはないと思われます)

 ただ、ジオタグ(位置情報)など、個人情報が含まれる画像が意図せずアップロードされることは可能性としてあると思いますので、サービス側としては不要なEXIFタグは削除して登録したいものです。

こういう場合、対応手段としては

  • コマンドで一括して削除する
  • PHP Exif LibraryなどEXIFヘッダを読み書きできるライブラリを使い削除する

といった方法が考えられますが、一般的にはどうするものなのでしょうか?

 とりあえず、ライブラリは使いたくない、コマンドも嫌だという人のために、GDを利用したEXIFヘッダ削除関数を作成してみました。

jpeg_clean.php

<?php
function jpeg_clean( $src_file, $dest_file, $quality = 90 )
{
	$info = getimagesize($src_file);

	if ($info === false) {
		throw new Exception('画像ファイルではありません。');
	}

	if ( !is_numeric($quality) || $quality < 0 || $quality > 100 ){
		throw new Exception('品質は0~100の間で指定してください。');
	}

	// 幅と高さ、画像の種類を取得
	list( $width, $height, $type ) = $info;

	// JPEG画像か確認する
	if ( $type != IMAGETYPE_JPEG ){
		throw new Exception('JPEG画像ではありません。');
	}

	// 画像リソースを生成
	$img = imagecreatefromjpeg($src_file);
	if (!$img) {
		throw new Exception('画像リソースの生成に失敗しました。');
	}

	// 画像ファイルを作成
	$copy_img = imagecreatetruecolor($width, $height);
	imagecopyresampled($copy_img, $img, 0, 0, 0, 0, $width, $height, $width, $height);
	if (!imagejpeg($copy_img, $dest_file, $quality)) {
		throw new Exception('JPEG画像の変換に失敗しました。');
	}
}
function get_exif($file)
{
	return print_r(exif_read_data($file),true);
}

$src_file  = isset($argv[1]) ? $argv[1] : NULL;
$dest_file = isset($argv[2]) ? $argv[2] : NULL;
$quality   = isset($argv[3]) ? $argv[3] : NULL;

try{
	if ( !$src_file || !$dest_file ){
		throw new Exception( "入力および出力画像ファイルを指定してください" );
	}
	if ( !$quality ){
		$quality = 90;
	}
	echo "EXIF情報(変換前):" . get_exif($src_file) . PHP_EOL;
	jpeg_clean( $src_file, $dest_file, $quality );
	echo "変換成功:$dest_file" . PHP_EOL;
	echo "EXIF情報(変換後):" . get_exif($dest_file) . PHP_EOL;
}
catch( Exception $e ){
	echo $e->getMessage() . PHP_EOL;
	echo "usage:" . PHP_EOL;
	echo "jpeg_clean in-file out-file [quality]" . PHP_EOL;
}

関数だけ使いたい場合は、jpeg_cleanだけコピーしてください。コマンドラインから使う場合は以下のようにもできます。

コマンドラインから使う

php jpeg_clean.php in.jpg out.jpg 85

関数、コマンドラインともに第1引数に元画像のパス、第2引数に出力先のパス、第3引数に画像品質(100が最高ですがファイルサイズが大きくなる)を指定してください。

削除の仕組みですが、ただ単にimagecreatefromjpegで空のイメージを作って、ビットマップデータをコピーするだけです。imagejpegでファイルとしてセーブする際、少しだけヘッダを出力しますがほぼ空の状態です。
(僕の環境ではCOMMENTセクションに「CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90」などと記録されていました)

100にしても品質を保持することはできない(どころか大抵ファイルサイズが増えてしまう)のですが、qualityに85~90を指定しておけばかなりいい感じになると思います(適当)。
EXIFヘッダにでかいデータが入っていた場合は、ファイルサイズの削減にもなり一石二鳥(?)。

ちなみに、画像処理に関しては下記の記事を参考にさせていただきました。感謝。

アップロードされた画像ファイルをリサイズして保存するサンプル:
http://qiita.com/mpyw/items/73ee77a9535cc65eff1e

15
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?