@@ -1151,8 +1151,8 @@ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXIST
1151
1151
* exploit.php_.pps.
1152
1152
*
1153
1153
* Specifically, this function adds an underscore to all extensions that are
1154
- * between 2 and 5 characters in length, internal to the file name, and not
1155
- * included in $extensions.
1154
+ * between 2 and 5 characters in length, internal to the file name, and either
1155
+ * included in the list of unsafe extensions, or not included in $extensions.
1156
1156
*
1157
1157
* Function behavior is also controlled by the Drupal variable
1158
1158
* 'allow_insecure_uploads'. If 'allow_insecure_uploads' evaluates to TRUE, no
@@ -1161,7 +1161,8 @@ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXIST
1161
1161
* @param $filename
1162
1162
* File name to modify.
1163
1163
* @param $extensions
1164
- * A space-separated list of extensions that should not be altered.
1164
+ * A space-separated list of extensions that should not be altered. Note that
1165
+ * extensions that are unsafe will be altered regardless of this parameter.
1165
1166
* @param $alerts
1166
1167
* If TRUE, drupal_set_message() will be called to display a message if the
1167
1168
* file name was changed.
@@ -1179,6 +1180,10 @@ function file_munge_filename($filename, $extensions, $alerts = TRUE) {
1179
1180
1180
1181
$ whitelist = array_unique (explode (' ' , strtolower (trim ($ extensions ))));
1181
1182
1183
+ // Remove unsafe extensions from the list of allowed extensions. The list is
1184
+ // copied from file_save_upload().
1185
+ $ whitelist = array_diff ($ whitelist , explode ('| ' , 'php|phar|pl|py|cgi|asp|js ' ));
1186
+
1182
1187
// Split the filename up by periods. The first part becomes the basename
1183
1188
// the last part the final extension.
1184
1189
$ filename_parts = explode ('. ' , $ filename );
@@ -1546,25 +1551,35 @@ function file_save_upload($form_field_name, $validators = array(), $destination
1546
1551
$ validators ['file_validate_extensions ' ][0 ] = $ extensions ;
1547
1552
}
1548
1553
1549
- if (!empty ($ extensions )) {
1550
- // Munge the filename to protect against possible malicious extension hiding
1551
- // within an unknown file type (ie: filename.html.foo).
1552
- $ file ->filename = file_munge_filename ($ file ->filename , $ extensions );
1553
- }
1554
-
1555
- // Rename potentially executable files, to help prevent exploits (i.e. will
1556
- // rename filename.php.foo and filename.php to filename.php.foo.txt and
1557
- // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
1558
- // evaluates to TRUE.
1559
- if (!variable_get ('allow_insecure_uploads ' , 0 ) && preg_match ('/\.(php|phar|pl|py|cgi|asp|js)(\.|$)/i ' , $ file ->filename ) && (substr ($ file ->filename , -4 ) != '.txt ' )) {
1560
- $ file ->filemime = 'text/plain ' ;
1561
- // The destination filename will also later be used to create the URI.
1562
- $ file ->filename .= '.txt ' ;
1563
- // The .txt extension may not be in the allowed list of extensions. We have
1564
- // to add it here or else the file upload will fail.
1554
+ if (!variable_get ('allow_insecure_uploads ' , 0 )) {
1565
1555
if (!empty ($ extensions )) {
1566
- $ validators ['file_validate_extensions ' ][0 ] .= ' txt ' ;
1567
- drupal_set_message (t ('For security reasons, your upload has been renamed to %filename. ' , array ('%filename ' => $ file ->filename )));
1556
+ // Munge the filename to protect against possible malicious extension hiding
1557
+ // within an unknown file type (ie: filename.html.foo).
1558
+ $ file ->filename = file_munge_filename ($ file ->filename , $ extensions );
1559
+ }
1560
+
1561
+ // Rename potentially executable files, to help prevent exploits (i.e. will
1562
+ // rename filename.php.foo and filename.php to filename.php_.foo_.txt and
1563
+ // filename.php_.txt, respectively). Don't rename if 'allow_insecure_uploads'
1564
+ // evaluates to TRUE.
1565
+ if (preg_match ('/\.(php|phar|pl|py|cgi|asp|js)(\.|$)/i ' , $ file ->filename )) {
1566
+ // If the file will be rejected anyway due to a disallowed extension, it
1567
+ // should not be renamed; rather, we'll let file_validate_extensions()
1568
+ // reject it below.
1569
+ if (!isset ($ validators ['file_validate_extensions ' ]) || !file_validate_extensions ($ file , $ extensions )) {
1570
+ $ file ->filemime = 'text/plain ' ;
1571
+ if (substr ($ file ->filename , -4 ) != '.txt ' ) {
1572
+ // The destination filename will also later be used to create the URI.
1573
+ $ file ->filename .= '.txt ' ;
1574
+ }
1575
+ $ file ->filename = file_munge_filename ($ file ->filename , $ extensions , FALSE );
1576
+ drupal_set_message (t ('For security reasons, your upload has been renamed to %filename. ' , array ('%filename ' => $ file ->filename )));
1577
+ // The .txt extension may not be in the allowed list of extensions. We have
1578
+ // to add it here or else the file upload will fail.
1579
+ if (!empty ($ validators ['file_validate_extensions ' ][0 ])) {
1580
+ $ validators ['file_validate_extensions ' ][0 ] .= ' txt ' ;
1581
+ }
1582
+ }
1568
1583
}
1569
1584
}
1570
1585
@@ -1732,7 +1747,18 @@ function file_validate(stdClass &$file, $validators = array()) {
1732
1747
}
1733
1748
1734
1749
// Let other modules perform validation on the new file.
1735
- return array_merge ($ errors , module_invoke_all ('file_validate ' , $ file ));
1750
+ $ errors = array_merge ($ errors , module_invoke_all ('file_validate ' , $ file ));
1751
+
1752
+ // Ensure the file does not contain a malicious extension. At this point
1753
+ // file_save_upload() will have munged the file so it does not contain a
1754
+ // malicious extension. Contributed and custom code that calls this method
1755
+ // needs to take similar steps if they need to permit files with malicious
1756
+ // extensions to be uploaded.
1757
+ if (empty ($ errors ) && !variable_get ('allow_insecure_uploads ' , 0 ) && preg_match ('/\.(php|phar|pl|py|cgi|asp|js)(\.|$)/i ' , $ file ->filename )) {
1758
+ $ errors [] = t ('For security reasons, your upload has been rejected. ' );
1759
+ }
1760
+
1761
+ return $ errors ;
1736
1762
}
1737
1763
1738
1764
/**
0 commit comments