[MEDIA] Removed blacklisted extensions, "trusts" upload extension (doesn't affect anything) and updated sysadmin documentation

Fixes bug which broke the UI. Oops
This commit is contained in:
Miguel Dantas 2019-06-16 00:33:12 +01:00 committed by Diogo Cordeiro
parent b9a0733062
commit ccebe536b3
6 changed files with 56 additions and 36 deletions

View File

@ -667,6 +667,12 @@ in php.ini to be large enough to handle your upload. In httpd.conf
(if you're using apache), check that the LimitRequestBody directive isn't (if you're using apache), check that the LimitRequestBody directive isn't
set too low (it's optional, so it may not be there at all). set too low (it's optional, so it may not be there at all).
* `extblacklist`: associative array to either deny certain extensions or
change them to a different one. For example:
$config['attachments']['extblacklist']['php'] = 'phps'; // this turns .php into .phps
$config['attachments']['extblacklist']['exe'] = false; // this would deny any uploads
// of files with the "exe" extension
* `process_links`: follow redirects and save all available file information * `process_links`: follow redirects and save all available file information
(mimetype, date, size, oembed, etc.). Defaults to true. (mimetype, date, size, oembed, etc.). Defaults to true.

View File

@ -1,4 +1,4 @@
# GNU social 1.21.x # GNU social 1.22.x
(c) 2010-2019 Free Software Foundation, Inc (c) 2010-2019 Free Software Foundation, Inc
This is the README file for GNU social, the free This is the README file for GNU social, the free

View File

@ -336,6 +336,31 @@ class File extends Managed_DataObject
return $filename; return $filename;
} }
/**
* @param string $filename
* @return string|bool Value from the 'extblacklist' array, in the config
*/
public static function getSafeExtension(string $filename) {
if (preg_match('/^.+?\.([A-Za-z0-9]+)$/', $filename, $matches)) {
// we matched on a file extension, so let's see if it means something.
$ext = mb_strtolower($matches[1]);
$blacklist = common_config('attachments', 'extblacklist');
// If we got an extension from $filename we want to check if it's in a blacklist
// so we avoid people uploading restricted files
if (array_key_exists($ext, $blacklist)) {
if (!is_string($blacklist[$ext])) {
return false;
}
// return a safe replacement extension ('php' => 'phps' for example)
return $blacklist[$ext];
}
// the attachment extension based on its filename was not blacklisted so it's ok to use it
return $ext;
} else {
return false;
}
}
/** /**
* @param $mimetype string The mimetype we've discovered for this file. * @param $mimetype string The mimetype we've discovered for this file.
* @param $filename string An optional filename which we can use on failure. * @param $filename string An optional filename which we can use on failure.
@ -351,28 +376,16 @@ class File extends Managed_DataObject
return $ext; return $ext;
} catch (UnknownMimeExtensionException $e) { } catch (UnknownMimeExtensionException $e) {
// We don't know the extension for this mimetype, but let's guess. // We don't know the extension for this mimetype, but let's guess.
// If we can't recognize the extension from the MIME, we try // If we can't recognize the extension from the MIME, we try
// to guess based on filename, if one was supplied. // to guess based on filename, if one was supplied.
if (!is_null($filename) && preg_match('/^.+\.([A-Za-z0-9]+)$/', $filename, $matches)) { if (!is_null($filename)) {
// we matched on a file extension, so let's see if it means something. $ext = getSafeExtension($filename);
$ext = mb_strtolower($matches[1]); if ($ext === false) {
// we don't have a safe replacement extension
$blacklist = common_config('attachments', 'extblacklist'); throw new ClientException(_('Blacklisted file extension.'));
// If we got an extension from $filename we want to check if it's in a blacklist } else {
// so we avoid people uploading .php files etc. return $ext;
if (array_key_exists($ext, $blacklist)) {
if (!is_string($blacklist[$ext])) {
// we don't have a safe replacement extension
throw new ClientException(_('Blacklisted file extension.'));
}
common_debug('Found replaced extension for filename '._ve($filename).': '._ve($ext));
// return a safe replacement extension ('php' => 'phps' for example)
return $blacklist[$ext];
} }
// the attachment extension based on its filename was not blacklisted so it's ok to use it
return $ext;
} }
} catch (Exception $e) { } catch (Exception $e) {
common_log(LOG_INFO, 'Problem when figuring out extension for mimetype: '._ve($e)); common_log(LOG_INFO, 'Problem when figuring out extension for mimetype: '._ve($e));
@ -381,9 +394,9 @@ class File extends Managed_DataObject
// If nothing else has given us a result, try to extract it from // If nothing else has given us a result, try to extract it from
// the mimetype value (this turns .jpg to .jpeg for example...) // the mimetype value (this turns .jpg to .jpeg for example...)
$matches = array(); $matches = array();
// FIXME: try to build a regexp that will get jpeg from image/jpeg as well as json from application/jrd+json // Will get jpeg from image/jpeg as well as json from application/jrd+json
if (!preg_match('/\/([a-z0-9]+)/', mb_strtolower($mimetype), $matches)) { if (!preg_match('/[\/+-\.]([a-z0-9]+)/', mb_strtolower($mimetype), $matches)) {
throw new Exception('Malformed mimetype: '.$mimetype); throw new Exception("Malformed mimetype: {$mimetype}");
} }
return mb_strtolower($matches[1]); return mb_strtolower($matches[1]);
} }

View File

@ -273,10 +273,7 @@ $default =
'show_html' => false, // show (filtered) text/html attachments (and oEmbed HTML etc.). Doesn't affect AJAX calls. 'show_html' => false, // show (filtered) text/html attachments (and oEmbed HTML etc.). Doesn't affect AJAX calls.
'show_thumbs' => true, // show thumbnails in notice lists for uploaded images, and photos and videos linked remotely that provide oEmbed info 'show_thumbs' => true, // show thumbnails in notice lists for uploaded images, and photos and videos linked remotely that provide oEmbed info
'process_links' => true, // check linked resources for embeddable photos and videos; this will hit referenced external web sites when processing new messages. 'process_links' => true, // check linked resources for embeddable photos and videos; this will hit referenced external web sites when processing new messages.
'extblacklist' => [ 'extblacklist' => [],
'php' => 'phps', // this turns .php into .phps
'exe' => false, // this would deny any uploads to keep the "exe" file extension
],
'memory_limit' => '1024M' // PHP's memory limit to use temporarily when handling images 'memory_limit' => '1024M' // PHP's memory limit to use temporarily when handling images
), ),
'thumbnail' => [ 'thumbnail' => [

View File

@ -32,12 +32,12 @@ defined('GNUSOCIAL') || die();
define('GNUSOCIAL_ENGINE', 'GNU social'); define('GNUSOCIAL_ENGINE', 'GNU social');
define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/'); define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
define('GNUSOCIAL_BASE_VERSION', '1.21.3'); define('GNUSOCIAL_BASE_VERSION', '1.22.0');
define('GNUSOCIAL_LIFECYCLE', 'dev'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release' define('GNUSOCIAL_LIFECYCLE', 'dev'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE); define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
define('GNUSOCIAL_CODENAME', 'The Invicta Crusade'); define('GNUSOCIAL_CODENAME', 'Undecided');
define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48); define('AVATAR_STREAM_SIZE', 48);

View File

@ -351,6 +351,12 @@ class MediaFile
File::respectsQuota($scoped, $_FILES[$param]['size']); File::respectsQuota($scoped, $_FILES[$param]['size']);
} }
// Gets a replacement extension if configured in the config, returns false if it's blocked
$ext = File::getSafeExtension($_FILES[$param]['name']);
if ($ext === false) {
throw new ClientException(_('Blacklisted file extension.'));
}
$mimetype = self::getUploadedMimeType($_FILES[$param]['tmp_name'], $_FILES[$param]['name']); $mimetype = self::getUploadedMimeType($_FILES[$param]['tmp_name'], $_FILES[$param]['name']);
$media = common_get_mime_media($mimetype); $media = common_get_mime_media($mimetype);
@ -371,7 +377,7 @@ class MediaFile
} }
// New file name format // New file name format
$original_filename = bin2hex("{$basename}.{$ext}"); $original_filename = bin2hex($basename);
$filename = "{$original_filename}-{$filehash}"; $filename = "{$original_filename}-{$filehash}";
$filepath = File::path($filename); $filepath = File::path($filename);
@ -609,22 +615,20 @@ class MediaFile
*/ */
public static function getDisplayName(File $file) : string { public static function getDisplayName(File $file) : string {
// New file name format is "{bin2hex(original_name.ext)}-{$hash}" // New file name format is "{bin2hex(original_name.ext)}-{$hash}"
$ret = preg_match('/^([^\-]+)-.+$/', $file->filename, $matches); $ret = preg_match('/^([^\.-]+)-.+$/', $file->filename, $matches);
// If there was an error in the match, something's wrong with some piece // If there was an error in the match, something's wrong with some piece
// of code (could be a file with utf8 chars in the name) // of code (could be a file with utf8 chars in the name)
$user_error_mesg = "Invalid file name ({$file->filename})."; $log_error_msg = "Invalid file name for File with id={$file->id} " .
$log_error_msg = "Invalid file name for File with id={$file->id} " . "({$file->filename}). Some plugin probably did something wrong.";
"({$file->filename}). Some plugin probably did something wrong.";
if ($ret === false) { if ($ret === false) {
common_log(LOG_ERR, $log_error_msg); common_log(LOG_ERR, $log_error_msg);
throw new ServerException($user_error_msg);
} elseif ($ret === 1) { } elseif ($ret === 1) {
$filename = hex2bin($matches[1]); $filename = hex2bin($matches[1]);
} else { } else {
// The old file name format was "{hash}.{ext}" // The old file name format was "{hash}.{ext}"
// This estracts the extension // This estracts the extension
$ret = preg_match('/^[^\.]+\.(.+)$/', $file->filename, $matches); $ret = preg_match('/^.+?\.(.+)$/', $file->filename, $matches);
if ($ret !== 1) { if ($ret !== 1) {
common_log(LOG_ERR, $log_error_msg); common_log(LOG_ERR, $log_error_msg);
return _('Untitled attachment'); return _('Untitled attachment');