Mercurial > p > roundup > code
comparison roundup/cgi/client.py @ 7155:89a59e46b3af
improve REST interface security
When using REST, we reflect the client's origin. If the wildcard '*'
is used in allowed_api_origins all origins are allowed. When this is
done, it also added an 'Access-Control-Allow-Credentials: true'
header.
This Credentials header should not be added if the site is matched
only by '*'. This header should be provided only for explicit origins
(e.g. https://example.org) not for the wildcard.
This is now fixed for CORS preflight OPTIONS request as well as normal
GET, PUT, DELETE, POST, PATCH and OPTIONS requests.
A missing Access-Control-Allow-Credentials will prevent the tracker
from being accessed using credentials. This prevents an unauthorized
third party web site from using a user's credentials to access
information in the tracker that is not publicly available.
Added test for this specific case.
In addition, allowed_api_origins can include explicit origins in
addition to '*'. '*' must be first in the list.
Also adapted numerous tests to work with these changes.
Doc updates.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Thu, 23 Feb 2023 12:01:33 -0500 |
| parents | 1181157d7cec |
| children | 765222ef4cec |
comparison
equal
deleted
inserted
replaced
| 7154:f614176903d0 | 7155:89a59e46b3af |
|---|---|
| 1277 if self.user == 'anonymous': | 1277 if self.user == 'anonymous': |
| 1278 if not self.db.security.hasPermission('Web Access', self.userid): | 1278 if not self.db.security.hasPermission('Web Access', self.userid): |
| 1279 raise Unauthorised(self._("Anonymous users are not " | 1279 raise Unauthorised(self._("Anonymous users are not " |
| 1280 "allowed to use the web interface")) | 1280 "allowed to use the web interface")) |
| 1281 | 1281 |
| 1282 def is_origin_header_ok(self, api=False): | 1282 def is_origin_header_ok(self, api=False, credentials=False): |
| 1283 """Determine if origin is valid for the context | 1283 """Determine if origin is valid for the context |
| 1284 | 1284 |
| 1285 Allow (return True) if ORIGIN is missing and it is a GET. | 1285 Header is ok (return True) if ORIGIN is missing and it is a GET. |
| 1286 Allow if ORIGIN matches the base url. | 1286 Header is ok if ORIGIN matches the base url. |
| 1287 If this is a API call: | 1287 If this is a API call: |
| 1288 Allow if ORIGIN matches an element of allowed_api_origins. | 1288 Header is ok if ORIGIN matches an element of allowed_api_origins. |
| 1289 Allow if allowed_api_origins includes '*' as first element.. | 1289 Header is ok if allowed_api_origins includes '*' as first |
| 1290 Otherwise disallow. | 1290 element and credentials is False. |
| 1291 Otherwise header is not ok. | |
| 1292 | |
| 1293 In a credentials context, if we match * we will return | |
| 1294 header is not ok. All credentialed requests must be | |
| 1295 explicitly matched. | |
| 1291 """ | 1296 """ |
| 1292 | 1297 |
| 1293 try: | 1298 try: |
| 1294 origin = self.env['HTTP_ORIGIN'] | 1299 origin = self.env['HTTP_ORIGIN'] |
| 1295 except KeyError: | 1300 except KeyError: |
| 1310 allowed_origins = self.db.config['WEB_ALLOWED_API_ORIGINS'] | 1315 allowed_origins = self.db.config['WEB_ALLOWED_API_ORIGINS'] |
| 1311 # find a match for other possible origins | 1316 # find a match for other possible origins |
| 1312 # Original spec says origin is case sensitive match. | 1317 # Original spec says origin is case sensitive match. |
| 1313 # Living spec doesn't address Origin value's case or | 1318 # Living spec doesn't address Origin value's case or |
| 1314 # how to compare it. So implement case sensitive.... | 1319 # how to compare it. So implement case sensitive.... |
| 1315 if allowed_origins: | 1320 if origin in allowed_origins: |
| 1316 if allowed_origins[0] == '*' or origin in allowed_origins: | 1321 return True |
| 1317 return True | 1322 # Block use of * when origin match is used for |
| 1323 # allowing credentials. See: | |
| 1324 # https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS | |
| 1325 # under Credentials Requests and Wildcards | |
| 1326 if ( allowed_origins and allowed_origins[0] == '*' | |
| 1327 and not credentials): | |
| 1328 return True | |
| 1318 | 1329 |
| 1319 return False | 1330 return False |
| 1320 | 1331 |
| 1321 def is_referer_header_ok(self, api=False): | 1332 def is_referer_header_ok(self, api=False): |
| 1322 referer = self.env['HTTP_REFERER'] | 1333 referer = self.env['HTTP_REFERER'] |
