0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-06 22:40:14 -05:00

Added middleware to prevent other sites' content from being served (#20922)

ref ONC-294

---------

Co-authored-by: Daniel Lockyer <hi@daniellockyer.com>
This commit is contained in:
Sam Lord 2024-09-05 17:15:09 +01:00 committed by GitHub
parent dd183cf25e
commit 46a4f7bc36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 131 additions and 0 deletions

View file

@ -1,6 +1,7 @@
const sentry = require('./shared/sentry');
const express = require('./shared/express');
const config = require('./shared/config');
const logging = require('@tryghost/logging');
const urlService = require('./server/services/url');
const fs = require('fs');
@ -27,12 +28,33 @@ const maintenanceMiddleware = function maintenanceMiddleware(req, res, next) {
fs.createReadStream(path.resolve(__dirname, './server/views/maintenance.html')).pipe(res);
};
// Used by Ghost (Pro) to ensure that requests cannot be served by the wrong site
const siteIdMiddleware = function siteIdMiddleware(req, res, next) {
const configSiteId = config.get('hostSettings:siteId');
const headerSiteId = req.headers['x-site-id'];
if (`${configSiteId}` === `${headerSiteId}`) {
return next();
}
logging.warn(`Mismatched site id (expected ${configSiteId}, got ${headerSiteId})`);
res.set({
'Cache-Control': 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
});
res.writeHead(500);
res.end();
};
const rootApp = () => {
const app = express('root');
app.use(sentry.requestHandler);
if (config.get('sentry')?.tracing?.enabled === true) {
app.use(sentry.tracingHandler);
}
if (config.get('hostSettings:siteId')) {
app.use(siteIdMiddleware);
}
app.enable('maintenance');
app.use(maintenanceMiddleware);

View file

@ -0,0 +1,109 @@
const sinon = require('sinon');
const supertest = require('supertest');
const testUtils = require('../utils');
const configUtils = require('../utils/configUtils');
describe('Site id middleware execution', function () {
let request;
describe('Using site-id middleware', function () {
before(async function () {
configUtils.set('hostSettings:siteId', '123123');
// Ensure we do a forced start so that spy is in place when the server starts
await testUtils.startGhost({forceStart: true});
request = supertest.agent(configUtils.config.get('url'));
});
after(async function () {
sinon.restore();
configUtils.restore();
await testUtils.stopGhost();
});
it('should allow requests with the correct site id header', function () {
return request.get('/')
.set('x-site-id', '123123')
.expect(200);
});
it('should allow requests with numeric site id header', function () {
return request.get('/')
.set('x-site-id', 123123)
.expect(200);
});
it('should prevent requests with incorrect numeric site id header', function () {
return request.get('/')
.set('x-site-id', 1231230)
.expect(500);
});
it('should allow static asset requests with the correct site id header', function () {
return request.get('/content/images/ghost.png')
.set('x-site-id', '123123')
.expect(404);
});
it('should reject static asset requests without a site id header', function () {
return request.get('/content/images/ghost.png')
.expect(500);
});
it('should reject requests with an incorrect site id header', function () {
return request.get('/')
.set('x-site-id', '456456')
.expect(500);
});
it('should reject requests without a site id header', function () {
return request.get('/')
.expect(500);
});
it('should reject requests with an empty site id header', function () {
return request.get('/')
.set('x-site-id', '')
.expect(500);
});
it('should reject requests with a malformed site id header', function () {
return request.get('/')
.set('x-ghost-site-id', 'not-a-valid-id')
.expect(500);
});
});
describe('Not using site-id middleware', function () {
before(async function () {
configUtils.set('hostSettings:siteId', undefined);
// Ensure we do a forced start so that spy is in place when the server starts
await testUtils.startGhost({forceStart: true});
request = supertest.agent(configUtils.config.get('url'));
});
after(async function () {
sinon.restore();
configUtils.restore();
await testUtils.stopGhost();
});
it('should allow requests without a site id header', function () {
return request.get('/')
.expect(200);
});
it('should allow requests with a site id header', function () {
return request.get('/')
.set('x-site-id', '123123')
.expect(200);
});
});
});