← All changes
|
app/Services/FileSystem/Drivers/S3/S3FileDeleter.php
+21
-3
1.6.3
→
1.6.5
View file →
| @@ -29,12 +29,13 @@ | ||
| 29 | 29 | $this->httpMethod = "DELETE"; |
| 30 | 30 | $this->timeStamp = gmdate('Ymd\THis\Z'); |
| 31 | 31 | $this->date = substr($this->timeStamp, 0, 8); |
| 32 | 32 | $hasDot = strpos($this->bucket, '.') !== false; |
| 33 | + $encodedFilePath = $this->encodeS3ObjectKey($this->s3FilePath); | |
| 33 | 34 | if ($hasDot) { |
| 34 | - $this->requestUrl = "https://s3.{$this->region}.amazonaws.com/{$this->bucket}/{$this->s3FilePath}"; | |
| 35 | + $this->requestUrl = "https://s3.{$this->region}.amazonaws.com/{$this->bucket}/{$encodedFilePath}"; | |
| 35 | 36 | } else { |
| 36 | - $this->requestUrl = "https://{$this->bucket}.s3.{$this->region}.amazonaws.com/{$this->s3FilePath}"; | |
| 37 | + $this->requestUrl = "https://{$this->bucket}.s3.{$this->region}.amazonaws.com/{$encodedFilePath}"; | |
| 37 | 38 | } |
| 38 | 39 | $this->signature = $this->generateSignature(); |
| 39 | 40 | } |
| 40 | 41 | |
| @@ -83,13 +84,30 @@ | ||
| 83 | 84 | } |
| 84 | 85 | |
| 85 | 86 | private function createCanonicalUrl(): string |
| 86 | 87 | { |
| 87 | - $s3FilePath = '/' . ltrim($this->s3FilePath, '/'); | |
| 88 | + $s3FilePath = '/' . $this->encodeS3ObjectKey($this->s3FilePath); | |
| 88 | 89 | $hasDot = strpos($this->bucket, '.') !== false; |
| 89 | 90 | $canonicalUri = $hasDot ? '/' . $this->bucket . $s3FilePath : $s3FilePath; |
| 90 | 91 | |
| 91 | 92 | return "{$this->httpMethod}\n{$canonicalUri}\n\nhost:{$this->getHost()}\nx-amz-content-sha256:{$this->getContentHash()}\nx-amz-date:{$this->timeStamp}\n\nhost;x-amz-content-sha256;x-amz-date\n{$this->getContentHash()}"; |
| 93 | + } | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * Percent-encodes each "/"-separated segment of an S3 object key using | |
| 97 | + * AWS's UriEncode rules (rawurlencode leaves "/" alone). This keeps the | |
| 98 | + * canonical signing path and the actual request URL identical to what | |
| 99 | + * S3 receives on the wire, so multi-byte UTF-8 characters, spaces, and | |
| 100 | + * reserved characters ("+", "#", "?", literal "%") all round-trip to | |
| 101 | + * the exact key that was requested instead of a different object. | |
| 102 | + * | |
| 103 | + * Deliberately does not ltrim() leading slashes: "foo", "/foo", and | |
| 104 | + * "//foo" are three distinct S3 keys, and stripping the slash made a | |
| 105 | + * delete/upload targeting "/foo" silently operate on "foo" instead. | |
| 106 | + */ | |
| 107 | + private function encodeS3ObjectKey(string $path): string | |
| 108 | + { | |
| 109 | + return implode('/', array_map('rawurlencode', explode('/', $path))); | |
| 92 | 110 | } |
| 93 | 111 | |
| 94 | 112 | private function getHost(): string |
| 95 | 113 | { |