Skip to content

Commit e64e5d3

Browse files
authored
Merge pull request rails#54040 from mrpasquini/md5_config
Add config option for MD5 class to support FIPS compliance
2 parents 0989745 + bbc0cf1 commit e64e5d3

20 files changed

+72
-44
lines changed

activestorage/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
* Add support for alternative MD5 implementation through `config.active_storage.checksum_implementation`.
2+
3+
Also automatically degrade to using the slower `Digest::MD5` implementation if `OpenSSL::Digest::MD5`
4+
is found to be disabled because of OpenSSL FIPS mode.
5+
6+
*Matt Pasquini*, *Jean Boussier*
7+
18
* A Blob will no longer autosave associated Attachment.
29

310
This fixes an issue where a record with an attachment would have

activestorage/app/models/active_storage/blob.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ def service
348348
def compute_checksum_in_chunks(io)
349349
raise ArgumentError, "io must be rewindable" unless io.respond_to?(:rewind)
350350

351-
OpenSSL::Digest::MD5.new.tap do |checksum|
351+
ActiveStorage.checksum_implementation.new.tap do |checksum|
352352
read_buffer = "".b
353353
while io.read(5.megabytes, read_buffer)
354354
checksum << read_buffer

activestorage/lib/active_storage.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,15 @@ module ActiveStorage
363363

364364
mattr_accessor :track_variants, default: false
365365

366+
singleton_class.attr_accessor :checksum_implementation
367+
@checksum_implementation = OpenSSL::Digest::MD5
368+
begin
369+
@checksum_implementation.hexdigest("test")
370+
rescue # OpenSSL may have MD5 disabled
371+
require "digest/md5"
372+
@checksum_implementation = Digest::MD5
373+
end
374+
366375
mattr_accessor :video_preview_arguments, default: "-y -vframes 1 -f image2"
367376

368377
module Transformers

activestorage/lib/active_storage/downloader.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def download(key, file)
3535
end
3636

3737
def verify_integrity_of(file, checksum:)
38-
unless OpenSSL::Digest::MD5.file(file).base64digest == checksum
38+
unless ActiveStorage.checksum_implementation.file(file).base64digest == checksum
3939
raise ActiveStorage::IntegrityError
4040
end
4141
end

activestorage/lib/active_storage/engine.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ class Engine < Rails::Engine # :nodoc:
119119
ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
120120
ActiveStorage.video_preview_arguments = app.config.active_storage.video_preview_arguments || "-y -vframes 1 -f image2"
121121
ActiveStorage.track_variants = app.config.active_storage.track_variants || false
122+
if app.config.active_storage.checksum_implementation
123+
ActiveStorage.checksum_implementation = app.config.active_storage.checksum_implementation
124+
end
122125
end
123126
end
124127

activestorage/lib/active_storage/service/disk_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def make_path_for(key)
161161
end
162162

163163
def ensure_integrity_of(key, checksum)
164-
unless OpenSSL::Digest::MD5.file(path_for(key)).base64digest == checksum
164+
unless ActiveStorage.checksum_implementation.file(path_for(key)).base64digest == checksum
165165
delete key
166166
raise ActiveStorage::IntegrityError
167167
end

activestorage/test/controllers/direct_uploads_controller_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ActiveStorage::S3DirectUploadsControllerTest < ActionDispatch::Integration
1515
end
1616

1717
test "creating new direct upload" do
18-
checksum = OpenSSL::Digest::MD5.base64digest("Hello")
18+
checksum = ActiveStorage.checksum_implementation.base64digest("Hello")
1919
metadata = {
2020
"foo" => "bar",
2121
"my_key_1" => "my_value_1",
@@ -61,7 +61,7 @@ class ActiveStorage::GCSDirectUploadsControllerTest < ActionDispatch::Integratio
6161
end
6262

6363
test "creating new direct upload" do
64-
checksum = OpenSSL::Digest::MD5.base64digest("Hello")
64+
checksum = ActiveStorage.checksum_implementation.base64digest("Hello")
6565
metadata = {
6666
"foo" => "bar",
6767
"my_key_1" => "my_value_1",
@@ -106,7 +106,7 @@ class ActiveStorage::AzureStorageDirectUploadsControllerTest < ActionDispatch::I
106106
end
107107

108108
test "creating new direct upload" do
109-
checksum = OpenSSL::Digest::MD5.base64digest("Hello")
109+
checksum = ActiveStorage.checksum_implementation.base64digest("Hello")
110110
metadata = {
111111
"foo" => "bar",
112112
"my_key_1" => "my_value_1",
@@ -136,7 +136,7 @@ class ActiveStorage::AzureStorageDirectUploadsControllerTest < ActionDispatch::I
136136

137137
class ActiveStorage::DiskDirectUploadsControllerTest < ActionDispatch::IntegrationTest
138138
test "creating new direct upload" do
139-
checksum = OpenSSL::Digest::MD5.base64digest("Hello")
139+
checksum = ActiveStorage.checksum_implementation.base64digest("Hello")
140140
metadata = {
141141
"foo" => "bar",
142142
"my_key_1" => "my_value_1",
@@ -161,7 +161,7 @@ class ActiveStorage::DiskDirectUploadsControllerTest < ActionDispatch::Integrati
161161
end
162162

163163
test "creating new direct upload does not include root in json" do
164-
checksum = OpenSSL::Digest::MD5.base64digest("Hello")
164+
checksum = ActiveStorage.checksum_implementation.base64digest("Hello")
165165
metadata = {
166166
"foo" => "bar",
167167
"my_key_1" => "my_value_1",

activestorage/test/controllers/disk_controller_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
7474

7575
test "directly uploading blob with integrity" do
7676
data = "Something else entirely!"
77-
blob = create_blob_before_direct_upload byte_size: data.size, checksum: OpenSSL::Digest::MD5.base64digest(data)
77+
blob = create_blob_before_direct_upload byte_size: data.size, checksum: ActiveStorage.checksum_implementation.base64digest(data)
7878

7979
put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "text/plain" }
8080
assert_response :no_content
@@ -83,7 +83,7 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
8383

8484
test "directly uploading blob without integrity" do
8585
data = "Something else entirely!"
86-
blob = create_blob_before_direct_upload byte_size: data.size, checksum: OpenSSL::Digest::MD5.base64digest("bad data")
86+
blob = create_blob_before_direct_upload byte_size: data.size, checksum: ActiveStorage.checksum_implementation.base64digest("bad data")
8787

8888
put blob.service_url_for_direct_upload, params: data
8989
assert_response :unprocessable_entity
@@ -92,7 +92,7 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
9292

9393
test "directly uploading blob with mismatched content type" do
9494
data = "Something else entirely!"
95-
blob = create_blob_before_direct_upload byte_size: data.size, checksum: OpenSSL::Digest::MD5.base64digest(data)
95+
blob = create_blob_before_direct_upload byte_size: data.size, checksum: ActiveStorage.checksum_implementation.base64digest(data)
9696

9797
put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "application/octet-stream" }
9898
assert_response :unprocessable_entity
@@ -102,7 +102,7 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
102102
test "directly uploading blob with different but equivalent content type" do
103103
data = "Something else entirely!"
104104
blob = create_blob_before_direct_upload(
105-
byte_size: data.size, checksum: OpenSSL::Digest::MD5.base64digest(data), content_type: "application/x-gzip")
105+
byte_size: data.size, checksum: ActiveStorage.checksum_implementation.base64digest(data), content_type: "application/x-gzip")
106106

107107
put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "application/x-gzip" }
108108
assert_response :no_content
@@ -111,7 +111,7 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
111111

112112
test "directly uploading blob with mismatched content length" do
113113
data = "Something else entirely!"
114-
blob = create_blob_before_direct_upload byte_size: data.size - 1, checksum: OpenSSL::Digest::MD5.base64digest(data)
114+
blob = create_blob_before_direct_upload byte_size: data.size - 1, checksum: ActiveStorage.checksum_implementation.base64digest(data)
115115

116116
put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "text/plain" }
117117
assert_response :unprocessable_entity

activestorage/test/models/attachment_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ActiveStorage::AttachmentTest < ActiveSupport::TestCase
4141
test "attaching a blob doesn't touch the record" do
4242
data = "Something else entirely!"
4343
io = StringIO.new(data)
44-
blob = create_blob_before_direct_upload byte_size: data.size, checksum: OpenSSL::Digest::MD5.base64digest(data)
44+
blob = create_blob_before_direct_upload byte_size: data.size, checksum: ActiveStorage.checksum_implementation.base64digest(data)
4545
blob.upload(io)
4646

4747
user = User.create!(

activestorage/test/models/blob_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
3838

3939
assert_equal data, blob.download
4040
assert_equal data.length, blob.byte_size
41-
assert_equal OpenSSL::Digest::MD5.base64digest(data), blob.checksum
41+
assert_equal ActiveStorage.checksum_implementation.base64digest(data), blob.checksum
4242
end
4343

4444
test "create_and_upload extracts content type from data" do
@@ -188,7 +188,7 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
188188

189189
test "open without integrity" do
190190
create_blob(data: "Hello, world!").tap do |blob|
191-
blob.update! checksum: OpenSSL::Digest::MD5.base64digest("Goodbye, world!")
191+
blob.update! checksum: ActiveStorage.checksum_implementation.base64digest("Goodbye, world!")
192192

193193
assert_raises ActiveStorage::IntegrityError do
194194
blob.open { |file| flunk "Expected integrity check to fail" }

0 commit comments

Comments
 (0)