public function FileUploadTest::testFileUploadMaliciousExtension in Drupal 9
Same name and namespace in other branches
- 8 core/modules/jsonapi/tests/src/Functional/FileUploadTest.php \Drupal\Tests\jsonapi\Functional\FileUploadTest::testFileUploadMaliciousExtension()
Tests using the file upload POST route with malicious extensions.
File
- core/
modules/ jsonapi/ tests/ src/ Functional/ FileUploadTest.php, line 607
Class
- FileUploadTest
- Tests binary data file upload route.
Namespace
Drupal\Tests\jsonapi\FunctionalCode
public function testFileUploadMaliciousExtension() {
// Allow all file uploads but system.file::allow_insecure_uploads is set to
// FALSE.
$this->field
->setSetting('file_extensions', '')
->save();
$this
->rebuildAll();
$this
->setUpAuthorization('POST');
$this
->config('jsonapi.settings')
->set('read_only', FALSE)
->save(TRUE);
$uri = Url::fromUri('base:' . static::$postUri);
$php_string = '<?php print "Drupal"; ?>';
// Test using a masked exploit file.
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example.php"',
]);
// The filename is not munged because .txt is added and it is a known
// extension to apache.
$expected = $this
->getExpectedDocument(1, 'example.php_.txt', TRUE);
// Override the expected filesize.
$expected['data']['attributes']['filesize'] = strlen($php_string);
$this
->assertResponseData($expected, $response);
$this
->assertFileExists('public://foobar/example.php_.txt');
// Add .php and .txt as allowed extensions. Since 'allow_insecure_uploads'
// is FALSE, .php files should be renamed to have a .txt extension.
$this->field
->setSetting('file_extensions', 'php txt')
->save();
$this
->rebuildAll();
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example_2.php"',
]);
$expected = $this
->getExpectedDocument(2, 'example_2.php_.txt', TRUE);
// Override the expected filesize.
$expected['data']['attributes']['filesize'] = strlen($php_string);
$this
->assertResponseData($expected, $response);
$this
->assertFileExists('public://foobar/example_2.php_.txt');
$this
->assertFileDoesNotExist('public://foobar/example_2.php');
// Allow .doc file uploads and ensure even a mis-configured apache will not
// fallback to php because the filename will be munged.
$this->field
->setSetting('file_extensions', 'doc')
->save();
$this
->rebuildAll();
// Test using a masked exploit file.
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example_3.php.doc"',
]);
// The filename is munged.
$expected = $this
->getExpectedDocument(3, 'example_3.php_.doc', TRUE);
// Override the expected filesize.
$expected['data']['attributes']['filesize'] = strlen($php_string);
// The file mime should be 'application/msword'.
$expected['data']['attributes']['filemime'] = 'application/msword';
$this
->assertResponseData($expected, $response);
$this
->assertFileExists('public://foobar/example_3.php_.doc');
$this
->assertFileDoesNotExist('public://foobar/example_3.php.doc');
// Test that a dangerous extension such as .php is munged even if it is in
// the list of allowed extensions.
$this->field
->setSetting('file_extensions', 'doc php')
->save();
$this
->rebuildAll();
// Test using a masked exploit file.
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example_4.php.doc"',
]);
// The filename is munged.
$expected = $this
->getExpectedDocument(4, 'example_4.php_.doc', TRUE);
// Override the expected filesize.
$expected['data']['attributes']['filesize'] = strlen($php_string);
// The file mime should be 'application/msword'.
$expected['data']['attributes']['filemime'] = 'application/msword';
$this
->assertResponseData($expected, $response);
$this
->assertFileExists('public://foobar/example_4.php_.doc');
$this
->assertFileDoesNotExist('public://foobar/example_4.php.doc');
// Dangerous extensions are munged even when all extensions are allowed.
$this->field
->setSetting('file_extensions', '')
->save();
$this
->rebuildAll();
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example_5.php.png"',
]);
$expected = $this
->getExpectedDocument(5, 'example_5.php_.png', TRUE);
// Override the expected filesize.
$expected['data']['attributes']['filesize'] = strlen($php_string);
// The file mime should still see this as a PNG image.
$expected['data']['attributes']['filemime'] = 'image/png';
$this
->assertResponseData($expected, $response);
$this
->assertFileExists('public://foobar/example_5.php_.png');
// Dangerous extensions are munged if is renamed to end in .txt.
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example_6.cgi.png.txt"',
]);
$expected = $this
->getExpectedDocument(6, 'example_6.cgi_.png_.txt', TRUE);
// Override the expected filesize.
$expected['data']['attributes']['filesize'] = strlen($php_string);
// The file mime should also now be text.
$expected['data']['attributes']['filemime'] = 'text/plain';
$this
->assertResponseData($expected, $response);
$this
->assertFileExists('public://foobar/example_6.cgi_.png_.txt');
// Add .php as an allowed extension without .txt. Since insecure uploads are
// are not allowed, .php files will be rejected.
$this->field
->setSetting('file_extensions', 'php')
->save();
$this
->rebuildAll();
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example_7.php"',
]);
$this
->assertResourceErrorResponse(422, "Unprocessable Entity: file validation failed.\nFor security reasons, your upload has been rejected.", $uri, $response);
// Make sure that no file was saved.
$this
->assertFileDoesNotExist('public://foobar/example_7.php');
$this
->assertFileDoesNotExist('public://foobar/example_7.php.txt');
// Now allow insecure uploads.
\Drupal::configFactory()
->getEditable('system.file')
->set('allow_insecure_uploads', TRUE)
->save();
// Allow all file uploads. This is very insecure.
$this->field
->setSetting('file_extensions', '')
->save();
$this
->rebuildAll();
$response = $this
->fileRequest($uri, $php_string, [
'Content-Disposition' => 'filename="example_7.php"',
]);
$expected = $this
->getExpectedDocument(7, 'example_7.php', TRUE);
// Override the expected filesize.
$expected['data']['attributes']['filesize'] = strlen($php_string);
// The file mime should also now be PHP.
$expected['data']['attributes']['filemime'] = 'application/x-httpd-php';
$this
->assertResponseData($expected, $response);
$this
->assertFileExists('public://foobar/example_7.php');
}