更新フォームを送信するたびに、画像の URL が何度もエスケープされていると思います。これは、エスケープされていない URL をフォームで使用できるようにする必要があることを示しています。
ユーザーが の URL を送信した場合、フォームが更新されたときに'images/my image name.jpg'
表示されることはありません。表示用にデコードする必要があります。フォームに出力するときに'images/my%20image%20%name.jpg'
渡すだけです。URI.decode
(これは、HTML で安全でない文字列をフォームに含めることとは異なります。これは、文字通り、URI エンコーディングなしで元のとおりにユーザー入力を正確に表現<
するという問題にすぎません。たとえば、文字は<
ページ上のエンティティになります。ただし、%3C
エスケープ シーケンスではありません.Rails は自動的に HTML セーフを処理する必要があります.--絶対に確認して、URL 内のいくつかのタグを試してください.)
編集: XSS ガーデン パスに誘導したくないので、試してみました...
%input{ type: :text, value: URI.decode("http://%3Cscript%3Ealert(1);%3C/script%3E.jpg") }
大丈夫だよ。テキストボックスには として表示されますhttp://<script>alert(1);</script>.jpg
が、ソースでは予想どおり HTML セーフです。
あなたのコメントに基づいて、小さなアプリを最初からセットアップして、何が起こるかを確認することにしました。普通の Rails 4 アプリです。Carrierwave gem を導入し、デフォルトのアップローダを作成しました。(carrierwave_direct
別のステップで見ていきます)。Photo
次のようにモデルを作成します。
class Photo < ActiveRecord::Base
attr_accessible :image, :description, :approved
mount_uploader :image, PhotoUploader
end
クラスは次のPhotoUploader
ようになります (ジェネレーターからのすべてのデフォルト)。
class PhotoUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
フォームに近づく前にPhoto
、コンソールからインスタンスを作成して再読み込みし、属性を更新して、パスがどうなるか見てみましょう。picture of neil.jpg
アプリのルート ディレクトリに座っているというファイルがあります。
2.0.0-p195 :001 > p = Photo.new
=> #<Photo id: nil, image: nil, description: nil, approved: nil, created_at: nil, updated_at: nil>
2.0.0-p195 :002 > p.image.class
=> PhotoUploader
これまでのところすべて順調です。フォーム ハッシュを偽造し、アップローダーにデータへのファイル ハンドルを与えてから保存しましょう。
2.0.0-p195 :012 > p.image = { file_name: 'picture of neil.jpg', content_type: 'image/jpeg', size: File.size('picture of neil.jpg') }
=> {:file_name=>"picture of neil.jpg", :content_type=>"image/jpeg", :size=>53637}
2.0.0-p195 :013 > p.image = File.open 'picture of neil.jpg'
=> #<File:picture of neil.jpg>
2.0.0-p195 :014 > p.save!
(0.2ms) begin transaction
SQL (1.4ms) INSERT INTO "photos" ("created_at", "image", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 29 Oct 2013 18:03:49 UTC +00:00], ["image", "picture_of_neil.jpg"], ["updated_at", Tue, 29 Oct 2013 18:03:49 UTC +00:00]]
(1.6ms) commit transaction
=> true
を調べてみるとpublic/uploaders/photo/image/1
、そこには というファイルがありますpicture_of_neil.jpg
。Carrierwave は名前をサニタイズしましたが、気にしないでください。スペースを%20
文字などに変換していません。
ファイル名サニタイザーの正規表現を微調整して、スペースを受け入れ、もう一度試してみます。
2.0.0-p195 :015 > CarrierWave::SanitizedFile.sanitize_regexp = /[^a-zA-Z0-9_\.\-\+ ]/
=> /[^a-zA-Z0-9_\.\-\+ ]/
2.0.0-p195 :016 > p = Photo.new
=> #<Photo id: nil, image: nil, description: nil, approved: nil, created_at: nil, updated_at: nil>
2.0.0-p195 :017 > p.image = { file_name: 'picture of neil.jpg', content_type: 'image/jpeg', size: File.size('picture of neil.jpg') }
=> {:file_name=>"picture of neil.jpg", :content_type=>"image/jpeg", :size=>53637}
2.0.0-p195 :018 > p.image = File.open 'picture of neil.jpg'
=> #<File:picture of neil.jpg>
2.0.0-p195 :019 > p.save!
(0.2ms) begin transaction
SQL (0.6ms) INSERT INTO "photos" ("created_at", "image", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 29 Oct 2013 18:12:09 UTC +00:00], ["image", "picture of neil.jpg"], ["updated_at", Tue, 29 Oct 2013 18:12:09 UTC +00:00]]
(1.3ms) commit transaction
=> true
かっこいい、名前にスペースを含むファイルを保存しましたが、問題ありません。別の属性を微調整して再度保存すると、期待どおりに問題ありません。通話update_attributes
もOKです。
carrierwave_direct
他のことをする前に、fog を (優れたモック モードで)使用する:fog
ように切り替えて、今行ったことをすべてもう一度試します。新しい Carrierwave イニシャライザは次のようになります。
Fog.mock!
S3_CREDENTIALS = { provider: 'AWS',
region: 'eu-west-1',
aws_access_key_id: 'MOCKKEYMOCKKEY',
aws_secret_access_key: 'MOCKSECRETMOCKSECRET' }
S3_DIRECTORY = 'mock_bucket'
# If you're using Fog in mock mode, you have to create an in-memory directory.
Fog::Storage.new(S3_CREDENTIALS).directories.create(key: S3_DIRECTORY, public: false) if Fog.mocking?
CarrierWave::SanitizedFile.sanitize_regexp = /[^a-zA-Z0-9_\.\-\+ ]/
CarrierWave.configure do |config|
config.storage = :fog
config.fog_credentials = S3_CREDENTIALS
config.fog_directory = S3_DIRECTORY
end
Photo
次に、インスタンスを再度作成する手順を実行し、何が起こるかを確認します。コンソール出力は同一であるため、繰り返しません。p.image.url
これで、フォグが何を返すかを呼び出して確認できます。
2.0.0-p195 :005 > p.image.url
=> "https://s3.amazonaws.com/mock_bucket/uploads/photo/image/5/picture%20of%20neil.jpg"
Ok!これはまさに私たちが期待していることです。Photo インスタンスで保存されたパスには、これらの文字は含まれません。%20
スペースが含まれています。ただし、URL はエスケープされています。これはすべて良いです。説明を更新するとどうなりますか?
2.0.0-p195 :006 > p.update_attributes description: "A picture of me in a hat"
(0.3ms) begin transaction
SQL (0.5ms) UPDATE "photos" SET "description" = ?, "updated_at" = ? WHERE "photos"."id" = 5 [["description", "A picture of me in a hat"], ["updated_at", Tue, 29 Oct 2013 18:29:34 UTC +00:00]]
(2.1ms) commit transaction
=> true
2.0.0-p195 :007 > p.reload
Photo Load (0.4ms) SELECT "photos".* FROM "photos" WHERE "photos"."id" = ? LIMIT 1 [["id", 5]]
=> #<Photo id: 5, image: "picture of neil.jpg", description: "A picture of me in a hat", approved: nil, created_at: "2013-10-29 18:26:57", updated_at: "2013-10-29 18:29:34">
2.0.0-p195 :008 > p.image.url
=> "https://s3.amazonaws.com/mock_bucket/uploads/photo/image/5/picture%20of%20neil.jpg"
それでも大丈夫です。ここで、何らかの方法でファイル名が元のファイル名ではなく url に設定された場合、これがどのように繰り返しエスケープされ、再エスケープされるかがわかります。しかし、あなたが言うように、1 つのフィールドだけを更新しています。
それが何をするか見てみましょうcarrierwave_direct
。私PhotoUploader
のクラスは次のようになります。
class PhotoUploader < CarrierWave::Uploader::Base
include CarrierWaveDirect::Uploader
end
アップロード フォームを作成するための文書化された手順を確認しました。
ARGH、この時点で私は困惑しています。なぜなら、モック モードの Fog ではうまく動作しないようだからです。実際の s3 サーバーに誘導されるため、新しいモデルを更新するための往復は発生しません。ごめん。もう少し時間があれば、後でコンソールから同じことを試してみます。
OK、コンソール経由で動作しました。うまくいけば、何が起こっているのかを見るのに十分です。検証をミュートする必要がありましたが、以下を見てください。あなたが見ている動作を再現しています。
2.0.0-p195 :027 > class CarrierWaveDirect::Validations::ActiveModel::FilenameFormatValidator < ::ActiveModel::EachValidator
2.0.0-p195 :028?> def validate_each(record, attribute, value)
2.0.0-p195 :029?> end
2.0.0-p195 :030?> end
=> nil
2.0.0-p195 :031 > class CarrierWaveDirect::Validations::ActiveModel::RemoteNetUrlFormatValidator < ::ActiveModel::EachValidator
2.0.0-p195 :032?> def validate_each(record, attribute, value)
2.0.0-p195 :033?> end
2.0.0-p195 :034?> end
=> nil
2.0.0-p195 :035 > p = Photo.new
=> #<Photo id: nil, image: nil, description: nil, approved: nil, created_at: nil, updated_at: nil>
2.0.0-p195 :036 > p.image = { file_name: 'picture of neil.jpg', content_type: 'image/jpeg' }
=> {:file_name=>"picture of neil.jpg", :content_type=>"image/jpeg"}
2.0.0-p195 :037 > p.image = File.open 'picture of neil.jpg'
=> #<File:picture of neil.jpg>
2.0.0-p195 :038 > p.save!
(0.1ms) begin transaction
Photo Exists (0.2ms) SELECT 1 AS one FROM "photos" WHERE "photos"."image" = '1383074454-35950-4053/picture%2520of%2520neil.jpg' LIMIT 1
SQL (2.2ms) INSERT INTO "photos" ("created_at", "image", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 29 Oct 2013 19:20:56 UTC +00:00], ["image", "1383074454-35950-4053/picture%2520of%2520neil.jpg"], ["updated_at", Tue, 29 Oct 2013 19:20:56 UTC +00:00]]
(2.5ms) commit transaction
=> true
2.0.0-p195 :039 > p.image.url
=> "https://s3.amazonaws.com/mock_bucket/uploads/1383074454-35950-4053/picture%252520of%252520neil.jpg"
明らかに、この URL は間違っています。何らかの理由で、何度もエスケープされています。属性の更新時にこれが繰り返し発生するのを見ていないことに注意してください。私が追加したいのは、そのオブジェクトを作成するときcarrierwave_direct
に設定する必要があるとドキュメントに書かれていることです。image.key
2.0.0-p195 :048 > p.image.key = "1383074454-35950-4053/picture%20of%20neil.jpg"
=> "1383074454-35950-4053/picture%20of%20neil.jpg"
これは正常に機能し、URL は二重にエスケープされません。作成段階でこれを行っていますか、それとも標準的な搬送波が使用するマルチパート形式のアプローチを使用していますか?