|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace WebPConvert\Helpers; |
|
4
|
|
|
|
|
5
|
|
|
use WebPConvert\Exceptions\InvalidInputException; |
|
6
|
|
|
use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException; |
|
7
|
|
|
|
|
8
|
|
|
/** |
|
9
|
|
|
* Functions for sanitizing. |
|
10
|
|
|
* |
|
11
|
|
|
* @package WebPConvert |
|
12
|
|
|
* @author Bjørn Rosell <[email protected]> |
|
13
|
|
|
* @since Class available since Release 2.0.6 |
|
14
|
|
|
*/ |
|
15
|
|
|
class PathChecker |
|
16
|
|
|
{ |
|
17
|
|
|
|
|
18
|
|
|
/** |
|
19
|
|
|
* Check absolute file path to prevent attacks. |
|
20
|
|
|
* |
|
21
|
|
|
* - Prevents non printable characters |
|
22
|
|
|
* - Prevents stream wrappers |
|
23
|
|
|
* - Prevents directory traversal |
|
24
|
|
|
* |
|
25
|
|
|
* Preventing non printable characters is especially done to prevent the NUL character, which can be used |
|
26
|
|
|
* to bypass other tests. See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP. |
|
27
|
|
|
* |
|
28
|
|
|
* Preventeng stream wrappers is especially done to protect against Phar Deserialization. |
|
29
|
|
|
* See https://blog.ripstech.com/2018/new-php-exploitation-technique/ |
|
30
|
|
|
* |
|
31
|
|
|
* @param string $absFilePath |
|
32
|
|
|
* @return string sanitized file path |
|
33
|
|
|
*/ |
|
34
|
36 |
|
public static function checkAbsolutePath($absFilePath, $text = 'file') |
|
35
|
|
|
{ |
|
36
|
36 |
|
if (empty($absFilePath)) { |
|
37
|
|
|
throw new InvalidInputException('Empty filepath for ' . $text); |
|
38
|
|
|
} |
|
39
|
|
|
|
|
40
|
|
|
// Prevent non printable characters |
|
41
|
|
|
/* |
|
42
|
|
|
if (!ctype_print($absFilePath)) { |
|
43
|
|
|
throw new InvalidInputException('Non-printable characters are not allowed in ' . $text); |
|
44
|
|
|
}*/ |
|
45
|
|
|
|
|
46
|
|
|
// Prevent control characters (at least the first 32 (#0 - #1f) |
|
47
|
36 |
|
if (preg_match('#[\x{0}-\x{1f}]#', $absFilePath)) { |
|
48
|
|
|
throw new InvalidInputException('Non-printable characters are not allowed'); |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
// Prevent directory traversal |
|
52
|
|
|
/* Disabled. We DO allow it again (#203) |
|
53
|
|
|
if (preg_match('#\.\.\/#', $absFilePath)) { |
|
54
|
|
|
throw new InvalidInputException('Directory traversal is not allowed in ' . $text . ' path'); |
|
55
|
|
|
}*/ |
|
56
|
|
|
|
|
57
|
|
|
// Prevent stream wrappers ("phar://", "php://" and the like) |
|
58
|
|
|
// https://www.php.net/manual/en/wrappers.phar.php |
|
59
|
36 |
|
if (preg_match('#^\\w+://#', $absFilePath)) { |
|
60
|
|
|
throw new InvalidInputException('Stream wrappers are not allowed in ' . $text . ' path'); |
|
61
|
|
|
} |
|
62
|
36 |
|
} |
|
63
|
|
|
|
|
64
|
36 |
|
public static function checkAbsolutePathAndExists($absFilePath, $text = 'file') |
|
65
|
|
|
{ |
|
66
|
36 |
|
if (empty($absFilePath)) { |
|
67
|
|
|
throw new TargetNotFoundException($text . ' argument missing'); |
|
68
|
|
|
} |
|
69
|
36 |
|
self::checkAbsolutePath($absFilePath, $text); |
|
70
|
36 |
|
if (@!file_exists($absFilePath)) { |
|
71
|
5 |
|
throw new TargetNotFoundException($text . ' file was not found'); |
|
72
|
|
|
} |
|
73
|
35 |
|
if (@is_dir($absFilePath)) { |
|
74
|
|
|
throw new InvalidInputException($text . ' is a directory'); |
|
75
|
|
|
} |
|
76
|
35 |
|
} |
|
77
|
|
|
|
|
78
|
|
|
/** |
|
79
|
|
|
* Checks that source path is secure, file exists and it is not a dir. |
|
80
|
|
|
* |
|
81
|
|
|
* To also check mime type, use InputValidator::checkSource |
|
82
|
|
|
*/ |
|
83
|
31 |
|
public static function checkSourcePath($source) |
|
84
|
|
|
{ |
|
85
|
31 |
|
self::checkAbsolutePathAndExists($source, 'source'); |
|
86
|
30 |
|
} |
|
87
|
|
|
|
|
88
|
30 |
|
public static function checkDestinationPath($destination) |
|
89
|
|
|
{ |
|
90
|
30 |
|
if (empty($destination)) { |
|
91
|
|
|
throw new InvalidInputException('Destination argument missing'); |
|
92
|
|
|
} |
|
93
|
30 |
|
self::checkAbsolutePath($destination, 'destination'); |
|
94
|
|
|
|
|
95
|
30 |
|
if (!preg_match('#\.webp$#i', $destination)) { |
|
96
|
|
|
// Prevent overriding important files. |
|
97
|
|
|
// Overriding an .htaccess file would lay down the website. |
|
98
|
|
|
throw new InvalidInputException( |
|
99
|
|
|
'Destination file must end with ".webp". ' . |
|
100
|
|
|
'If you deliberately want to store the webp files with another extension, you must rename ' . |
|
101
|
|
|
'the file after successful conversion' |
|
102
|
|
|
); |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
30 |
|
if (@is_dir($destination)) { |
|
106
|
|
|
throw new InvalidInputException('Destination is a directory'); |
|
107
|
|
|
} |
|
108
|
30 |
|
} |
|
109
|
|
|
|
|
110
|
|
|
public static function checkSourceAndDestinationPaths($source, $destination) |
|
111
|
|
|
{ |
|
112
|
|
|
self::checkSourcePath($source); |
|
113
|
|
|
self::checkDestinationPath($destination); |
|
114
|
|
|
} |
|
115
|
|
|
} |
|
116
|
|
|
|