95 GET https://example.com/upload/foo/bar.jpg |
95 GET https://example.com/upload/foo/bar.jpg |
96 ``` |
96 ``` |
97 |
97 |
98 The only tricky logic is in validation of the PUT request. Firstly, don't overwrite existing files (return 409 Conflict). |
98 The only tricky logic is in validation of the PUT request. Firstly, don't overwrite existing files (return 409 Conflict). |
99 |
99 |
100 Then you need to validate the auth token. This will be in the URL query parameter 'v'. If it is absent, fail with 403 Forbidden. |
100 Then you need to validate the auth token. |
|
101 |
|
102 ### Validating the auth token |
|
103 |
|
104 |
|
105 | Version | Supports | |
|
106 |:--------|:--------------------------------------------------------------------------------------------------------| |
|
107 | v | Validates only filename and size. Does not support file type restrictions by the XMPP server. | |
|
108 | v2 | Validates the filename, size and MIME type. This allows the server to implement MIME type restrictions. | |
|
109 |
|
110 It is probable that a future v3 will be specified that allows carrying information about the uploader identity, allowing |
|
111 the implementation of per-user quotas and limits. |
|
112 |
|
113 Implementations may implement one or more versions of the protocol simultaneously. The XMPP server generates the URLs and ultimately selects which version will be used. |
|
114 |
|
115 #### Version 1 (v) |
|
116 |
|
117 The token will be in the URL query parameter 'v'. If it is absent, fail with 403 Forbidden. |
101 |
118 |
102 Calculate the expected auth token by reading the value of the Content-Length header of the PUT request. E.g. for a 1MB file |
119 Calculate the expected auth token by reading the value of the Content-Length header of the PUT request. E.g. for a 1MB file |
103 will have a Content-Length of '1048576'. Append this to the uploaded file name, separated by a space (0x20) character. |
120 will have a Content-Length of '1048576'. Append this to the uploaded file name, separated by a space (0x20) character. |
104 |
121 |
105 For the above example, you would end up with the following string: "foo/bar.jpg 1048576" |
122 For the above example, you would end up with the following string: "foo/bar.jpg 1048576" |
110 calculated_auth_token = hmac_sha256("foo/bar.jpg 1048576", "secret string") |
127 calculated_auth_token = hmac_sha256("foo/bar.jpg 1048576", "secret string") |
111 ``` |
128 ``` |
112 |
129 |
113 If this is not equal to the 'v' parameter provided in the upload URL, reject the upload with 403 Forbidden. |
130 If this is not equal to the 'v' parameter provided in the upload URL, reject the upload with 403 Forbidden. |
114 |
131 |
115 Note: your language/environment may provide a function for doing a constant-time comparison of these, to guard against |
132 **Security note:** When comparing `calculated_auth_token` with the token provided in the URL, you must use a constant-time string |
116 timing attacks that may be used to discover the secret key. |
133 comparison, otherwise an attacker may be able to discover your secret key. Most languages/environments provide such a function, such |
|
134 as `hash_equals()` in PHP, `hmac.compare_digest()` in Python, or `ConstantTimeCompare()` from `crypto/subtle` in Go. |
|
135 |
|
136 #### Version 2 (v2) |
|
137 |
|
138 The token will be in the URL query parameter 'v2'. If it is absent, fail with 403 Forbidden. |
|
139 |
|
140 | Input | Example |Read from | |
|
141 |:--------------|:------------|:--------------------------------------------------------------------| |
|
142 |`file_path` | foo/bar.jpg | The URL of the PUT request, with the service's base prefix removed. | |
|
143 |`content_size` | 1048576 | Content-Size header | |
|
144 |`content_type` | image/jpeg | Content-Type header | |
|
145 |
|
146 The parameters should be joined into a single string, separated by NUL bytes (`\0`): |
|
147 |
|
148 ``` |
|
149 signed_string = ( file_path + '\0' + content_size + '\0' + content_type ) |
|
150 ``` |
|
151 |
|
152 ``` |
|
153 signed_string = "foo/bar.jpg\01048576\0image/jpeg" |
|
154 ``` |
|
155 |
|
156 The expected auth token is the SHA256 HMAC of this string, using the configured secret key as the key. E.g.: |
|
157 |
|
158 ``` |
|
159 calculated_auth_token = hmac_sha256(signed_string, "secret string") |
|
160 ``` |
|
161 |
|
162 If this is not equal to the 'v2' parameter provided in the upload URL, reject the upload with 403 Forbidden. |
|
163 |
|
164 **Security note:** When comparing `calculated_auth_token` with the token provided in the URL, you must use a constant-time string |
|
165 comparison, otherwise an attacker may be able to discover your secret key. Most languages/environments provide such a function, such |
|
166 as `hash_equals()` in PHP, `hmac.compare_digest()` in Python, or `ConstantTimeCompare()` from `crypto/subtle` in Go. |
|
167 |
|
168 ### Security considerations |
|
169 |
|
170 #### HTTPS |
|
171 |
|
172 All uploads and downloads should only be over HTTPS. The security of the served content is protected only |
|
173 by the uniqueness present in the URLs themselves, and not using HTTPS may leak the URLs and contents to third-parties. |
|
174 |
|
175 Implementations should consider including HSTS and HPKP headers, with consent of the administrator. |
|
176 |
|
177 #### MIME types |
|
178 |
|
179 If the upload Content-Type header matches any of the following MIME types, it MUST be preserved and included in the Content-Type |
|
180 of any GET requests made to download the file: |
|
181 |
|
182 - `image/*` |
|
183 - `video/*` |
|
184 - `audio/*` |
|
185 - `text/plain` |
|
186 |
|
187 It is recommended that other MIME types are preserved, but served with the addition of the following header: |
|
188 |
|
189 ``` |
|
190 Content-Disposition: attachment |
|
191 ``` |
|
192 |
|
193 This prevents the browser interpreting scripts and other resources that may potentially be malicious. |
|
194 |
|
195 Some browsers may also benefit from explicitly telling them not to try guessing the type of a file: |
|
196 |
|
197 ``` |
|
198 X-Content-Type-Options "nosniff" |
|
199 ``` |
|
200 |
|
201 #### Security headers |
|
202 |
|
203 The following headers should be included to provide additional sandboxing of resources, considering the uploaded |
|
204 content is not understood or trusted by the upload service: |
|
205 |
|
206 ``` |
|
207 Content-Security-Policy: "default-src 'none'" |
|
208 X-Content-Security-Policy: "default-src 'none'" |
|
209 X-WebKit-CSP: "default-src 'none'" |
|
210 ``` |