I’ll keep this short and sweet, but we thought this would be a useful tip to share with the world as a potential security issue with the combined use of File::getFileUri() and FileSystem::realpath().
Consider the following code excerpt :
$file = File::load($some_file_uri);
if ($file) {
$uri = $file->getFileUri();
$file_realpath = Drupal::service('file_system')->realpath($uri);
}
Seems pretty harmless right? Load up the file from $some_file_uri , If we have a valid file then get the URI and then grab the real path.
Wrong (potentially, depending on what you do with $file_realpath).
If $file is a valid file, but for whatever reason the file is no longer physically located on disk, then $file->getFileUri() will return a blank string.
It turns out that passing this blank string $uri into Drupal::service(‘file_system’)->realpath($uri) will return the full webroot of your site!
Depending on what you were doing with said $file_realpath, it could then be a security issue.
We were handling a user webform submission and then sending the submission over to a CRM system… because $file_realpath was now the webroot of the site, then code that followed to archive the user submitted file ended up archiving the entire webroot and sending this over to the client’s CRM system.
Luckily in this instance, the archive was only ever available temporarily server side and then went directly to the clients own CRM system, but in another circumstance this could have easily been a very serious security issue.
Fortunately the fix is quite simple, ensure the the $uri returned from ->getFileUri() is valid by some method, before passing through realpath(). Here, I now validate the uri matches what I know it should be for the current webform submission.
if ($file) {
$uri = $file->getFileUri();
$webform_id = $webform->get('id');
$submission_id = $webform_submission->get('sid')->getValue()[0]['value'];
$valid_file_scheme = strpos($uri, 'private://webform/' . $webform_id . '/' . $submission_id . '/') !== FALSE;
if ($valid_file_scheme) {
// Proceed with the rest of the code..
}
}