Projet

Général

Profil

Paste
Télécharger (8,36 ko) Statistiques
| Branche: | Révision:

root / drupal7 / misc / typo3 / phar-stream-wrapper / README.md @ 6b24a280

1 cd5c298a Geoffroy Desvernay
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/TYPO3/phar-stream-wrapper/badges/quality-score.png?b=v2)](https://scrutinizer-ci.com/g/TYPO3/phar-stream-wrapper/?branch=v2)
2
[![Travis CI Build Status](https://travis-ci.org/TYPO3/phar-stream-wrapper.svg?branch=v2)](https://travis-ci.org/TYPO3/phar-stream-wrapper)
3 6b24a280 Assos Assos
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/q4ls5tg4w1d6sf4i/branch/v2?svg=true)](https://ci.appveyor.com/project/ohader/phar-stream-wrapper)
4 cd5c298a Geoffroy Desvernay
5
# PHP Phar Stream Wrapper
6
7
## Abstract & History
8
9
Based on Sam Thomas' findings concerning
10
[insecure deserialization in combination with obfuscation strategies](https://blog.secarma.co.uk/labs/near-phar-dangerous-unserialization-wherever-you-are)
11
allowing to hide Phar files inside valid image resources, the TYPO3 project
12
decided back then to introduce a `PharStreamWrapper` to intercept invocations
13
of the `phar://` stream in PHP and only allow usage for defined locations in
14
the file system.
15
16
Since the TYPO3 mission statement is **inspiring people to share**, we thought
17
it would be helpful for others to release our `PharStreamWrapper` as standalone
18
package to the PHP community.
19
20
The mentioned security issue was reported to TYPO3 on 10th June 2018 by Sam Thomas
21
and has been addressed concerning the specific attack vector and for this generic
22
`PharStreamWrapper` in TYPO3 versions 7.6.30 LTS, 8.7.17 LTS and 9.3.1 on 12th
23
July 2018.
24
25
* https://blog.secarma.co.uk/labs/near-phar-dangerous-unserialization-wherever-you-are
26
* https://youtu.be/GePBmsNJw6Y
27 6b24a280 Assos Assos
* https://typo3.org/security/advisory/typo3-psa-2018-001/
28
* https://typo3.org/security/advisory/typo3-psa-2019-007/
29
* https://typo3.org/security/advisory/typo3-psa-2019-008/
30 cd5c298a Geoffroy Desvernay
31
## License
32
33
In general the TYPO3 core is released under the GNU General Public License version
34
2 or any later version (`GPL-2.0-or-later`). In order to avoid licensing issues and
35
incompatibilities this `PharStreamWrapper` is licenced under the MIT License. In case
36
you duplicate or modify source code, credits are not required but really appreciated.
37
38
## Credits
39
40
Thanks to [Alex Pott](https://github.com/alexpott), Drupal for creating
41
back-ports of all sources in order to provide compatibility with PHP v5.3.
42
43
## Installation
44
45
The `PharStreamWrapper` is provided as composer package `typo3/phar-stream-wrapper`
46
and has minimum requirements of PHP v5.3 ([`v2`](https://github.com/TYPO3/phar-stream-wrapper/tree/v2) branch) and PHP v7.0 ([`master`](https://github.com/TYPO3/phar-stream-wrapper) branch).
47
48
### Installation for PHP v7.0
49
50
```
51
composer require typo3/phar-stream-wrapper ^3.0
52
```
53
54
### Installation for PHP v5.3
55
56
```
57
composer require typo3/phar-stream-wrapper ^2.0
58
```
59
60
## Example
61
62
The following example is bundled within this package, the shown
63
`PharExtensionInterceptor` denies all stream wrapper invocations files
64
not having the `.phar` suffix. Interceptor logic has to be individual and
65
adjusted to according requirements.
66
67
```
68
$behavior = new \TYPO3\PharStreamWrapper\Behavior();
69 fbb66ca6 Assos Assos
\TYPO3\PharStreamWrapper\Manager::initialize(
70 cd5c298a Geoffroy Desvernay
    $behavior->withAssertion(new PharExtensionInterceptor())
71
);
72
73
if (in_array('phar', stream_get_wrappers())) {
74
    stream_wrapper_unregister('phar');
75
    stream_wrapper_register('phar', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper');
76
}
77
```
78
79
* `PharStreamWrapper` defined as class reference will be instantiated each time
80
  `phar://` streams shall be processed.
81
* `Manager` as singleton pattern being called by `PharStreamWrapper` instances
82
  in order to retrieve individual behavior and settings.
83
* `Behavior` holds reference to interceptor(s) that shall assert correct/allowed
84
  invocation of a given `$path` for a given `$command`. Interceptors implement
85
  the interface `Assertable`. Interceptors can act individually on following
86
  commands or handle all of them in case not defined specifically:  
87
  + `COMMAND_DIR_OPENDIR`
88
  + `COMMAND_MKDIR`
89
  + `COMMAND_RENAME`
90
  + `COMMAND_RMDIR`
91
  + `COMMAND_STEAM_METADATA`
92
  + `COMMAND_STREAM_OPEN`
93
  + `COMMAND_UNLINK`
94
  + `COMMAND_URL_STAT`
95
96 fbb66ca6 Assos Assos
## Interceptors
97 cd5c298a Geoffroy Desvernay
98
The following interceptor is shipped with the package and ready to use in order
99
to block any Phar invocation of files not having a `.phar` suffix. Besides that
100
individual interceptors are possible of course.
101
102
```
103
class PharExtensionInterceptor implements Assertable
104
{
105
    /**
106
     * Determines whether the base file name has a ".phar" suffix.
107
     *
108
     * @param string $path
109
     * @param string $command
110
     * @return bool
111
     * @throws Exception
112
     */
113
    public function assert($path, $command)
114
    {
115
        if ($this->baseFileContainsPharExtension($path)) {
116
            return true;
117
        }
118
        throw new Exception(
119
            sprintf(
120
                'Unexpected file extension in "%s"',
121
                $path
122
            ),
123
            1535198703
124
        );
125
    }
126
127
    /**
128
     * @param string $path
129
     * @return bool
130
     */
131
    private function baseFileContainsPharExtension($path)
132
    {
133
        $baseFile = Helper::determineBaseFile($path);
134
        if ($baseFile === null) {
135
            return false;
136
        }
137
        $fileExtension = pathinfo($baseFile, PATHINFO_EXTENSION);
138
        return strtolower($fileExtension) === 'phar';
139
    }
140
}
141
```
142
143 fbb66ca6 Assos Assos
### ConjunctionInterceptor
144
145
This interceptor combines multiple interceptors implementing `Assertable`.
146
It succeeds when all nested interceptors succeed as well (logical `AND`).
147
148
```
149
$behavior = new \TYPO3\PharStreamWrapper\Behavior();
150
\TYPO3\PharStreamWrapper\Manager::initialize(
151
    $behavior->withAssertion(new ConjunctionInterceptor(array(
152
        new PharExtensionInterceptor(),
153
        new PharMetaDataInterceptor()
154
    )))
155
);
156
```
157
158
### PharExtensionInterceptor
159
160
This (basic) interceptor just checks whether the invoked Phar archive has
161
an according `.phar` file extension. Resolving symbolic links as well as
162
Phar internal alias resolving are considered as well.
163
164
```
165
$behavior = new \TYPO3\PharStreamWrapper\Behavior();
166
\TYPO3\PharStreamWrapper\Manager::initialize(
167
    $behavior->withAssertion(new PharExtensionInterceptor())
168
);
169
```
170
171
### PharMetaDataInterceptor
172
173
This interceptor is actually checking serialized Phar meta-data against
174
PHP objects and would consider a Phar archive malicious in case not only
175
scalar values are found. A custom low-level `Phar\Reader` is used in order to
176
avoid using PHP's `Phar` object which would trigger the initial vulnerability.
177
178
```
179
$behavior = new \TYPO3\PharStreamWrapper\Behavior();
180
\TYPO3\PharStreamWrapper\Manager::initialize(
181
    $behavior->withAssertion(new PharMetaDataInterceptor())
182
);
183
```
184
185
## Reader
186
187
* `Phar\Reader::__construct(string $fileName)`: Creates low-level reader for Phar archive
188
* `Phar\Reader::resolveContainer(): Phar\Container`: Resolves model representing Phar archive
189
* `Phar\Container::getStub(): Phar\Stub`: Resolves (plain PHP) stub section of Phar archive
190
* `Phar\Container::getManifest(): Phar\Manifest`: Resolves parsed Phar archive manifest as
191
  documented at http://php.net/manual/en/phar.fileformat.manifestfile.php
192
* `Phar\Stub::getMappedAlias(): string`: Resolves internal Phar archive alias defined in stub
193
  using `Phar::mapPhar('alias.phar')` - actually the plain PHP source is analyzed here
194
* `Phar\Manifest::getAlias(): string` - Resolves internal Phar archive alias defined in manifest
195
  using `Phar::setAlias('alias.phar')`
196
* `Phar\Manifest::getMetaData(): string`: Resolves serialized Phar archive meta-data
197
* `Phar\Manifest::deserializeMetaData(): mixed`: Resolves deserialized Phar archive meta-data
198
  containing only scalar values - in case an object is determined, an according
199
  `Phar\DeserializationException` will be thrown
200
201
```
202
$reader = new Phar\Reader('example.phar');
203
var_dump($reader->resolveContainer()->getManifest()->deserializeMetaData());
204
```
205
206 cd5c298a Geoffroy Desvernay
## Helper
207
208 fbb66ca6 Assos Assos
* `Helper::determineBaseFile(string $path): string`: Determines base file that can be
209 cd5c298a Geoffroy Desvernay
  accessed using the regular file system. For instance the following path
210
  `phar:///home/user/bundle.phar/content.txt` would be resolved to
211
  `/home/user/bundle.phar`.
212
* `Helper::resetOpCache()`: Resets PHP's OPcache if enabled as work-around for
213
  issues in `include()` or `require()` calls and OPcache delivering wrong
214
  results. More details can be found in PHP's bug tracker, for instance like
215
  https://bugs.php.net/bug.php?id=66569
216
217
## Security Contact
218
219
In case of finding additional security issues in the TYPO3 project or in this
220
`PharStreamWrapper` package in particular, please get in touch with the
221
[TYPO3 Security Team](mailto:security@typo3.org).