Headline
CVE-2023-22474: fix: The client IP address may be determined incorrectly in some case… · parse-community/parse-server@e016d81
Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. Parse Server uses the request header x-forwarded-for
to determine the client IP address. If Parse Server doesn’t run behind a proxy server, then a client can set this header and Parse Server will trust the value of the header. The incorrect client IP address will be used by various features in Parse Server. This allows to circumvent the security mechanism of the Parse Server option masterKeyIps
by setting an allowed IP address as the x-forwarded-for
header value. This issue has been patched in version 5.4.1. The mechanism to determine the client IP address has been rewritten. The correct IP address determination now requires to set the Parse Server option trustProxy
.
@@ -158,78 +158,6 @@ describe('middlewares’, () => { }); });
it('should not succeed if the connection.remoteAddress does not belong to masterKeyIps list’, () => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: ['ip1’, ‘ip2’], }); fakeReq.connection = { remoteAddress: ‘ip3’ }; fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); });
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list’, done => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: ['ip1’, ‘ip2’], }); fakeReq.connection = { remoteAddress: ‘ip1’ }; fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); });
it('should not succeed if the socket.remoteAddress does not belong to masterKeyIps list’, () => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: ['ip1’, ‘ip2’], }); fakeReq.socket = { remoteAddress: ‘ip3’ }; fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); });
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list’, done => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: ['ip1’, ‘ip2’], }); fakeReq.socket = { remoteAddress: ‘ip1’ }; fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); });
it('should not succeed if the connection.socket.remoteAddress does not belong to masterKeyIps list’, () => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: ['ip1’, ‘ip2’], }); fakeReq.connection = { socket: { remoteAddress: ‘ip3’ } }; fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); });
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list’, done => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: ['ip1’, ‘ip2’], }); fakeReq.connection = { socket: { remoteAddress: ‘ip1’ } }; fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); });
it('should allow any ip to use masterKey if masterKeyIps is empty’, done => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, @@ -243,52 +171,9 @@ describe('middlewares’, () => { }); });
it('should succeed if xff header does belong to masterKeyIps’, done => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: [‘ip1’], }); fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; fakeReq.headers[‘x-forwarded-for’] = 'ip1, ip2, ip3’; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); });
it('should succeed if xff header with one ip does belong to masterKeyIps’, done => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: [‘ip1’], }); fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; fakeReq.headers[‘x-forwarded-for’] = 'ip1’; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); });
it('should not succeed if xff header does not belong to masterKeyIps’, () => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: [‘ip4’], }); fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; fakeReq.headers[‘x-forwarded-for’] = 'ip1, ip2, ip3’; middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); });
it('should not succeed if xff header is empty and masterKeyIps is set’, () => { AppCache.put(fakeReq.body._ApplicationId, { masterKey: 'masterKey’, masterKeyIps: [‘ip1’], }); fakeReq.headers[‘x-parse-master-key’] = 'masterKey’; fakeReq.headers[‘x-forwarded-for’] = '’; middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); it('can set trust proxy’, async () => { const server = await reconfigureServer({ trustProxy: 1 }); expect(server.app.parent.settings[‘trust proxy’]).toBe(1); });
it('should properly expose the headers’, () => {
Related news
### Impact Parse Server uses the request header `x-forwarded-for` to determine the client IP address. If Parse Server doesn't run behind a proxy server, then a client can set this header and Parse Server will trust the value of the header. The incorrect client IP address will be used by various features in Parse Server. This allows to circumvent the security mechanism of the Parse Server option `masterKeyIps` by setting an allowed IP address as the `x-forwarded-for` header value. ### Patches The mechanism to determine the client IP address has been rewritten. The correct IP address determination now requires to set the Parse Server option `trustProxy` accordingly, see the express framework's [trust proxy](https://expressjs.com/en/guide/behind-proxies.html) setting. ### References - https://github.com/parse-community/parse-server/security/advisories/GHSA-vm5r-c87r-pf6x - https://expressjs.com/en/guide/behind-proxies.html