Search code examples
node.jssecurityexpressowasp

Is preventing open redirects attack in nodejs secure?


I'm trying to prevent open redirect attack. Please look at the code below and check for security:

var = require('url');

// http://example.com/login?redirect=http://example.com/dashboard
app.route('/login', function (req, res, next) {
   var redirect = req.query.redirect,
        paths = url.parse(redirect); 

   if (paths.host !== req.headers.host) {
      return next(new Error('Open redirect attack detected'));
   }

   return res.redirect(redirect);
});

Is it enough for preventing open redirect attack or should I add anything else?


Solution

  • CWE-601: URL Redirection to Untrusted Site ('Open Redirect')

    Description of Open Redirect:

    An http parameter may contain a URL value and could cause the web application to redirect the request to the specified URL. By modifying the URL value to a malicious site, an attacker may successfully launch a phishing scam and steal user credentials. Because the server name in the modified link is identical to the original site, phishing attempts have a more trustworthy appearance.

    The suggestion of input validation strategy to prevent open redirect attack:

    Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a whitelist of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. Do not rely exclusively on looking for malicious or malformed inputs (i.e., do not rely on a blacklist). A blacklist is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, blacklists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright. Use a whitelist of approved URLs or domains to be used for redirection.

    Use req.headers.host, req.host or req.hostname is insecure, because req.headers can be forged (eg. a HTTP request have a custom Host header to access a express app written in code below)

    var url = require('url');
    
    app.get('/login', function (req, res, next) {
        var redirect = req.query.redirect,
            targetUrl = url.parse(redirect);
        console.log('req.headers.host: [%s]', req.headers.host);
        console.log('req.host: [%s]', req.host);
        console.log('req.hostname: [%s]', req.hostname);
        if (targetUrl.host != req.headers.host) {
            return next(new Error('Open redirect attack detected'));
        }
        return res.redirect(redirect);
    });
    

    Use curl to make a request:

    $ curl -H 'Host: malicious.example.com' 'http://localhost:3012/login?redirect=http://malicious.example.com' -i
    HTTP/1.1 302 Found
    X-Powered-By: Express
    Location: http://malicious.example.com
    Vary: Accept
    Content-Type: text/plain; charset=utf-8
    Content-Length: 54
    Date: Mon, 13 Jun 2016 06:30:55 GMT
    Connection: keep-alive
    
    $ #server output
    req.headers.host: [malicious.example.com]
    req.host: [malicious.example.com]
    req.hostname: [malicious.example.com]
    

    I suggest you use whitelist to validate input, a example code below:

    const WHITELIST_TO_REDIRECT = new Set(["localhost:3012", "www.realdomain.com"]);
    
    app.get('/login', function (req, res, next) {
       var redirect = req.query.redirect,
            targetUrl = url.parse(redirect);
       console.log("req.hostname: [%s]", req.hostname);
       console.log("url.host: [%s]", targetUrl.host);
       if (!WHITELIST_TO_REDIRECT.has(targetUrl.host)) {
          return next(new Error('Open redirect attack detected'));
       }
    
       return res.redirect(redirect);
    });