Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential minor security risk (upload/spread arbitrary data) with ActiveStorage #76

Closed
gr8bit opened this issue Sep 10, 2022 · 1 comment

Comments

@gr8bit
Copy link

gr8bit commented Sep 10, 2022

Situation
ActiveStorage uses Marcel#for to determine the mime type of an uploaded file.
Marcel#for tries to determine the mime type from the given io object or path (in the case of ActiveStorage an io object is always supplied). If that fails (because Marcel cannot determine the type using its magic headers), it falls back to using the file's extension or content type - in case of ActiveStorage both of these are user supplied.
So as long as an uploaded file doesn't match any of the magic headers, the user is free to supply any mime type he chooses, posing a potential way to upload malicious files and disguising them as different mime types (also concerns mime type validations)

This is the method in question:

def for(pathname_or_io = nil, name: nil, extension: nil, declared_type: nil)
type_from_data = for_data(pathname_or_io)
fallback_type = for_declared_type(declared_type) || for_name(name) || for_extension(extension) || BINARY
if type_from_data
most_specific_type type_from_data, fallback_type
else
fallback_type
end
end

The else-block always falls back to the mime times derived from user supplied information about the file, even if an io object or path was supplied.

What do you think about this? Am I missing something?

Fix
From my current point of view, I'd suggest returning the BINARY (constant) type if no mime type could be extracted from a supplied io object or path.

        ...
        else
          # when an io object or pathname were supplied but type could not be determined from it,
          # don't use any fallback type extracted from other supplied information.
          pathname_or_io ? BINARY : fallback_type
        end
@gr8bit gr8bit changed the title Potential minor security risk (upload arbitrary data) with ActiveStorage Potential minor security risk (upload/spread arbitrary data) with ActiveStorage Sep 10, 2022
@jeremy
Copy link
Member

jeremy commented Mar 1, 2024

Magic byte sniffing is woefully incomplete, I'm afraid.

Any user-provided data must comport with the sniffed type, so you can't upload PDF content, for example, and call it image.png.

For those who need content sniffing alone without fallback, simply omit the declared type and filename. Then they'll be excluded from the heuristic.

@jeremy jeremy closed this as completed Mar 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants