hello-world
webエンジニアのメモ。とりあえずやってみる。

[rails]carrierwaveでファイル内容をもとにcontent-type判定を行う

公開日時

ruby2.1.4, rails4.1.7で確認。

carrierwaveでファイルアップロード機能を作成した場合、extension_white_listメソッドを使えば拡張子によるcontent-type判定ができます。

しかし、extension_white_listだけだと拡張子を偽装したファイルはアップロード可能になってしまいます。

そのため、厳密にチェックしたい場合はファイルの内容をもとにcontent-typeを判定する必要があります。

今回、「 CarrierWaveでファイルの内容をもとにcontent-typeの判定を行う」を参考にさせていただきました。

1. carrierwave-magic gemをインストール

carrierwave-magic gemを入れることでファイル内容をもとにcontent-type判定が可能になります。

# Gemfile

gem 'carrierwave-magic'

なお、carrierwave-magicを使用する場合は以下のライブラリをインストールしておく必要があります。

sudo yum install file file-devel

上記ライブラリをインストールしていない場合は、以下のエラーが発生します。

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

/home/vagrant/.rbenv/versions/2.1.4/bin/ruby extconf.rb
checking for magic_open() in -lmagic... no
*** ERROR: missing required library to compile this module
*** extconf.rb failed ***

2. carrierwave設定

今回は app/uploaders/sample_uploader.rb をアップローダーとして説明します。

class SampleUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick
  include CarrierWave::Magic                # これを追加
  process :set_magic_content_type => [true] # これを追加

  process :fix_exif_rotation

  # for mobile upload
  def fix_exif_rotation
    manipulate! do |img|
      img.auto_orient!
      img = yield(img) if block_given?
      img
    end
  end
end

set_magic_content_typeでcontent-typeのチェックを行っています。

fix_exif_rotationは今回のサンプルとはあまり関係ないですが、iOS端末での写真向きの自動最適化を解除するようにしています。

iPhoneで撮影した写真が横向きになってしまう場合のcarrierwaveでの対処法」に詳しい解説がありました。

iOS端末から写真アップロードを行う場合は設定しておきましょう。

3. modelにvalidation追加

content-type判定用のカスタムバリデータを作ります。

lib/autoload/sample_image_content_validator.rb を追加します。

# lib/autoload/sample_image_content_validator.rb

class SampleImageContentValidator < ActiveModel::EachValidator
  def initialize(options)
    options.reverse_merge!(:message => :invalid)
    super(options)
  end

  def validate_each(record, attribute, value)
    return if value.nil?
    white_list_content_types = ["image/jpg", "image/jpeg", "image/png", "image/x-citrix-png", "image/x-citrix-jpeg", "image/x-png", "image/pjpeg"]
    unless white_list_content_types.include?(value.file.content_type)
      record.errors.add(attribute, options[:message])
    end
  end
end

今回は、jpg, jpeg, pngを許可する設定になっています。

x-citrix-png, x-citrix-jpeg, x-png, pjpegはIEの場合にjpeg、pngのMIME typeが異なるので、その対応策です。

なお、lib/autoload以下が自動読み込みになっていない場合は、application.rbで設定をしておいてください。

#config/application.rb

config.autoload_paths += %W(#{config.root}/lib/autoload)

最後にモデル側でvalidation設定をします。

# app/models/hoge.rb

class Hoge < ActiveRecord::Base
  mount_uploader :sample_image, SampleUploader

  validates :sample_image,
    presence: true,
    sample_image_content: true # content-typeチェックを有効に

これでファイル内容をもとにcontent-type判定ができるようになりました。

pdf等、許可していない拡張子をpngに変更してアップロードを行うとバリデーションエラーになります。

参考


Related #carrierwave

[rails]carrierwaveで保存した画像のurlを取得する

carrierwave 0.10.0 で確認 imageカラムにcarrierwaveで保存した画像を保存している場合、デフォルトのままだとだとurlメソッドを実行してもpublic以下のパスのみで http://~ が設定されていません。

[rails]base64エンコードされた画像をcarrierwaveに保存する

Ruby 2.1.4, Rails 4.1.7 で確認 前回はcarrierwaveを使ってPOSTされた画像ファイルの保存を行うAPIを作ってみました。

[Rails]carrierwaveのcallbackをskip

Railsで画像アップロード機能を作る際に欠かせないgemと言えば carrierwave ですが、save時にcarrierwaveのcallbackをskipしたい場合の対応方法をメモしておきます。

[rails]carrierwaveを使って画像を保存するAPIサンプル

Ruby 2.1.4, Rails 4.1.7 で確認 画像アップロード機能を作成する際に carrierwaveをよく使うのですが毎回調べているので基本的な設定部分のメモを残しておきます。