safe-redirect.js 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. /**
  2. * Redirect with prevention from Open Redirect
  3. *
  4. * Usage: app.use(require('middleware/safe-redirect')(['example.com', 'some.example.com:8080']))
  5. */
  6. const loggerFactory = require('@alias/logger');
  7. const logger = loggerFactory('growi:middleware:safe-redirect');
  8. /**
  9. * Check whether the redirect url host is in specified whitelist
  10. * @param {Array<string>} whitelistOfHosts
  11. * @param {string} redirectToFqdn
  12. */
  13. function isInWhitelist(whitelistOfHosts, redirectToFqdn) {
  14. if (whitelistOfHosts == null || whitelistOfHosts.length === 0) {
  15. return false;
  16. }
  17. const redirectUrl = new URL(redirectToFqdn);
  18. return whitelistOfHosts.includes(redirectUrl.hostname) || whitelistOfHosts.includes(redirectUrl.host);
  19. }
  20. module.exports = (whitelistOfHosts) => {
  21. return function(req, res, next) {
  22. // extend res object
  23. res.safeRedirect = function(redirectTo) {
  24. if (redirectTo == null) {
  25. return res.redirect('/');
  26. }
  27. try {
  28. // check inner redirect
  29. const redirectUrl = new URL(redirectTo, `${req.protocol}://${req.get('host')}`);
  30. if (redirectUrl.hostname === req.hostname) {
  31. logger.debug(`Requested redirect URL (${redirectTo}) is local.`);
  32. return res.redirect(redirectUrl.href);
  33. }
  34. logger.debug(`Requested redirect URL (${redirectTo}) is NOT local.`);
  35. // check whitelisted redirect
  36. const isWhitelisted = isInWhitelist(whitelistOfHosts, redirectTo);
  37. if (isWhitelisted) {
  38. logger.debug(`Requested redirect URL (${redirectTo}) is in whitelist.`, `whitelist=${whitelistOfHosts}`);
  39. return res.redirect(redirectTo);
  40. }
  41. logger.debug(`Requested redirect URL (${redirectTo}) is NOT in whitelist.`, `whitelist=${whitelistOfHosts}`);
  42. }
  43. catch (err) {
  44. logger.warn(`Requested redirect URL (${redirectTo}) is invalid.`, err);
  45. }
  46. logger.warn(`Requested redirect URL (${redirectTo}) is UNSAFE, redirecting to root page.`);
  47. return res.redirect('/');
  48. };
  49. next();
  50. };
  51. };