0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-01 02:41:39 -05:00

Remove open redirect by removing double slashes from redirects (#7247)

no issue

Double slashes are treated as a HTTP calls as specified in [RFC1801](http://www.ietf.org/rfc/rfc1808.txt). Because of this behaviour the uncapitalise created an open redirect. By removing double slashes in the path we ensure open redirects cannot be created.

As an example, please click the following URL: https://dev.ghost.org///Google.com/.

This issue  has been reported by pentesters of our product [LearningSpaces.io](http://learningspaces.io).
This commit is contained in:
Jesse Dijkstra 2016-08-23 13:47:59 +02:00 committed by Katharina Irrgang
parent 6a1c10516e
commit f546a5ce1d
4 changed files with 50 additions and 5 deletions

View file

@ -13,7 +13,8 @@ uncapitalise = function uncapitalise(req, res, next) {
/*jslint unparam:true*/
var pathToTest = req.path,
isSignupOrReset = req.path.match(/(\/ghost\/(signup|reset)\/)/i),
isAPI = req.path.match(/(\/ghost\/api\/v[\d\.]+\/.*?\/)/i);
isAPI = req.path.match(/(\/ghost\/api\/v[\d\.]+\/.*?\/)/i),
redirectPath;
if (isSignupOrReset) {
pathToTest = isSignupOrReset[1];
@ -29,9 +30,14 @@ uncapitalise = function uncapitalise(req, res, next) {
* That encoding isn't useful here, as it triggers an extra uncapitalise redirect, so we decode the path first
*/
if (/[A-Z]/.test(decodeURIComponent(pathToTest))) {
res.set('Cache-Control', 'public, max-age=' + utils.ONE_YEAR_S);
// Adding baseUrl ensures subdirectories are kept
res.redirect(301, (req.baseUrl ? req.baseUrl : '') + req.url.replace(pathToTest, pathToTest.toLowerCase()));
redirectPath = (
(req.baseUrl ? req.baseUrl : '') +
utils.removeOpenRedirectFromUrl(req.url.replace(pathToTest, pathToTest.toLowerCase()))
);
res.set('Cache-Control', 'public, max-age=' + utils.ONE_YEAR_S);
res.redirect(301, redirectPath);
} else {
next();
}

View file

@ -1,7 +1,7 @@
var unidecode = require('unidecode'),
_ = require('lodash'),
readCSV = require('./read-csv'),
removeOpenRedirectFromUrl = require('./remove-open-redirect-from-url'),
utils,
getRandomInt;
@ -102,7 +102,8 @@ utils = {
res.redirect(301, path);
},
readCSV: readCSV
readCSV: readCSV,
removeOpenRedirectFromUrl: removeOpenRedirectFromUrl
};
module.exports = utils;

View file

@ -0,0 +1,30 @@
var url = require('url');
function removeDoubleCharacters(character, string) {
var stringArray = string.split('');
return stringArray.reduce(function (newString, currentCharacter, index) {
if (
currentCharacter === character &&
stringArray[index + 1] === character
) {
return newString;
}
return newString + currentCharacter;
}, '');
}
function removeOpenRedirectFromUrl(urlString) {
var parsedUrl = url.parse(urlString);
return (
(parsedUrl.protocol ? parsedUrl.protocol + '//' : '') + // http://
(parsedUrl.auth || '') +
(parsedUrl.host || '') +
removeDoubleCharacters('/', parsedUrl.path) +
(parsedUrl.hash || '')
);
}
module.exports = removeOpenRedirectFromUrl;

View file

@ -113,6 +113,14 @@ describe('Frontend Routing', function () {
.end(doEnd(done));
});
it('should sanitize double slashes when redirecting uppercase', function (done) {
request.get('///Google.com/')
.expect('Location', '/google.com/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEnd(done));
});
it('should respond with html for valid url', function (done) {
request.get('/welcome-to-ghost/')
.expect('Content-Type', /html/)