これ以上ない小ネタですw
JPEGファイルのEXIFヘッダは、位置情報を埋め込んだりできて便利(?)ですが、先日このEXIFヘッダにPHPコードを仕込むマルウェアが発見されており、画像を自由に登録できるサービスを提供している場合は注意が必要です。
(実際には、この方法でサイトの閲覧者に任意のコードを実行するにはEXIFヘッダに埋め込まれたコードを取得&実行するスクリプトを事前にサーバに配置しておかなければならないため、危険度はそれほど高くはないと思われます)
ただ、ジオタグ(位置情報)など、個人情報が含まれる画像が意図せずアップロードされることは可能性としてあると思いますので、サービス側としては不要なEXIFタグは削除して登録したいものです。
こういう場合、対応手段としては
- コマンドで一括して削除する
- PHP Exif LibraryなどEXIFヘッダを読み書きできるライブラリを使い削除する
といった方法が考えられますが、一般的にはどうするものなのでしょうか?
とりあえず、ライブラリは使いたくない、コマンドも嫌だという人のために、GDを利用したEXIFヘッダ削除関数を作成してみました。
<?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