Overview
Comment:Add some protection against malicious uploads
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA3-256: c7e2f12dfb65f9b9509afe54057aa51a3021bad451b6a79664f4cdeac32d8dd7
User & Date: bohwaz on 2021-04-05 14:04:14
Other Links: branch diff | manifest | tags
Context
2021-04-05
14:05
Fix typo check-in: b0721cd17b user: bohwaz tags: dev
14:04
Add some protection against malicious uploads check-in: c7e2f12dfb user: bohwaz tags: dev
13:36
Force cache directory mkdir in case it has been hard cleared, fix [084a09bbff324c08102f015692ed98431bef3285] check-in: 1f23b0d518 user: bohwaz tags: dev
Changes

Modified src/config.dist.php from [9300d4dd65] to [d4d9a98eb8].

388
389
390
391
392
393
394




395
396
397
398
399
400
401
 *
 * Indiquer NULL si vous souhaitez stocker les fichier dans la base
 * de données SQLite (valeur par défaut).
 *
 * Classes de stockage possibles :
 * - SQLite : enregistre dans la base de données (défaut)
 * - FileSystem : enregistrement des fichiers dans le système de fichier




 *
 * Défaut : null
 */

//const FILE_STORAGE_BACKEND = null;

/**







>
>
>
>







388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
 *
 * Indiquer NULL si vous souhaitez stocker les fichier dans la base
 * de données SQLite (valeur par défaut).
 *
 * Classes de stockage possibles :
 * - SQLite : enregistre dans la base de données (défaut)
 * - FileSystem : enregistrement des fichiers dans le système de fichier
 *
 * ATTENTION : activer FileSystem ET ne pas utiliser de sous-domaine (vhost dédié)
 * ferait courir de graves risques de piratage à votre serveur web si vous ne protégez
 * pas correctement le répertoire de stockage des fichiers !
 *
 * Défaut : null
 */

//const FILE_STORAGE_BACKEND = null;

/**

Modified src/include/lib/Garradin/Entities/Files/File.php from [6c19806068] to [2d5f69b0ed].

120
121
122
123
124
125
126



127
128
129
130
131
132
133
		'image/jpeg',
		'image/webp',
		'image/svg+xml',
		'text/plain',
		'text/html',
	];




	static public function getColumns(): array
	{
		return array_keys((new self)->_types);
	}

	public function selfCheck(): void
	{







>
>
>







120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
		'image/jpeg',
		'image/webp',
		'image/svg+xml',
		'text/plain',
		'text/html',
	];

	// https://book.hacktricks.xyz/pentesting-web/file-upload
	const FORBIDDEN_EXTENSIONS = '!cgi|exe|sh|bash|com|pif|jspx?|js[wxv]|action|do|php(?:s|\d+)?|pht|phtml?|shtml|phar|htaccess|inc|cfml?|cfc|dbm|swf|pl|perl|py|pyc|asp|so!i';

	static public function getColumns(): array
	{
		return array_keys((new self)->_types);
	}

	public function selfCheck(): void
	{
375
376
377
378
379
380
381

382
383
384
385
386
387
388

	static public function create(string $path, string $name, ?string $source_path, ?string $source_content): self
	{
		if (!isset($source_path) && !isset($source_content)) {
			throw new \InvalidArgumentException('Either source path or source content should be set but not both');
		}


		self::ensureDirectoryExists($path);

		$finfo = \finfo_open(\FILEINFO_MIME_TYPE);
		$file = new self;
		$file->set('path', $path . '/' . $name);
		$file->set('parent', $path);
		$file->set('name', $name);







>







378
379
380
381
382
383
384
385
386
387
388
389
390
391
392

	static public function create(string $path, string $name, ?string $source_path, ?string $source_content): self
	{
		if (!isset($source_path) && !isset($source_content)) {
			throw new \InvalidArgumentException('Either source path or source content should be set but not both');
		}

		self::validateFileName($name);
		self::ensureDirectoryExists($path);

		$finfo = \finfo_open(\FILEINFO_MIME_TYPE);
		$file = new self;
		$file->set('path', $path . '/' . $name);
		$file->set('parent', $path);
		$file->set('name', $name);
846
847
848
849
850
851
852

















853
854
855
856
857
858
859
860
861
862
863
864
865
866
		return null;
	}

	static public function filterName(string $name): string
	{
		return preg_replace('/[^\w\d\p{L}_. -]+/iu', '-', $name);
	}


















	static public function validatePath(string $path): array
	{
		$path = explode('/', $path);

		if (count($path) < 1) {
			throw new ValidationException('Invalid file path');
		}

		if (!array_key_exists($path[0], self::CONTEXTS_NAMES)) {
			throw new ValidationException('Chemin invalide');
		}

		$context = array_shift($path);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|







850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
		return null;
	}

	static public function filterName(string $name): string
	{
		return preg_replace('/[^\w\d\p{L}_. -]+/iu', '-', $name);
	}

	static public function validateFileName(string $name): void
	{
		if (substr($name[0], 0, 1)) {
			throw new ValidationException('Le nom de fichier ne peut commencer par un point');
		}

		if (strpos($name, "\0") !== false) {
			throw new ValidationException('Nom de fichier invalide');
		}

		$extension = strtolower(substr($name, strrpos($name, '.')));

		if (preg_match(self::FORBIDDEN_EXTENSIONS, $extension))
			throw new ValidationException('Extension de fichier non autorisée, merci de renommer le fichier avant envoi.');
		}
	}

	static public function validatePath(string $path): array
	{
		$path = explode('/', $path);

		if (count($path) < 1) {
			throw new ValidationException('Chemin invalide');
		}

		if (!array_key_exists($path[0], self::CONTEXTS_NAMES)) {
			throw new ValidationException('Chemin invalide');
		}

		$context = array_shift($path);