CarrierWave với S3 trong Rails: cấu hình, security và lifecycle

CarrierWave tách model khỏi storage backend, nhưng abstraction không xóa trách nhiệm cấu hình S3.

1
2
3
4
5
6
7
8
9
10
# config/initializers/carrierwave.rb
CarrierWave.configure do |config|
config.fog_provider = "fog/aws"
config.fog_credentials = {
provider: "AWS",
region: ENV.fetch("AWS_REGION")
}
config.fog_directory = ENV.fetch("S3_BUCKET")
config.fog_public = false
end

Trên AWS, SDK/Fog nên nhận credential từ IAM role. Local có thể dùng profile hoặc environment; không commit access key.

Uploader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DocumentUploader < CarrierWave::Uploader::Base
storage :fog

def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end

def extension_allowlist
%w[pdf png jpg jpeg]
end

def size_range
1.byte..10.megabytes
end
end

Allowlist extension/MIME chỉ là lớp đầu; metadata client có thể giả. File không tin cậy cần scan và không nên public trước khi processing hoàn tất.

URL private

Với fog_public = false, tạo URL có thời hạn thay vì mở bucket:

1
record.file.url(expires: 10.minutes.from_now.to_i)

API cụ thể phụ thuộc adapter/version; test integration thay vì đoán option từ blog cũ.

Processing

Resize/convert ảnh tốn CPU và memory. Đưa version processing nặng sang background job, giữ original private, và invalidate/version CDN khi object thay đổi.

Khi nào dùng Active Storage?

Rails hiện đại có Active Storage, direct upload, variants và service adapter tích hợp framework. CarrierWave vẫn hợp lý cho project đang dùng ổn định hoặc cần uploader DSL cụ thể. Migration chỉ vì “core Rails nghe sang hơn” cũng có thể tạo incident rất core.

Checklist

  • IAM least privilege, bucket private.
  • Credential ngoài source.
  • Randomized key và filename sanitization.
  • Size/type validation + malware scanning khi cần.
  • Background processing.
  • Lifecycle rule cho temporary/orphan objects.
  • CDN và cache strategy rõ ràng.

References