Compare commits
	
		
			No commits in common. "c10a310c20f0db1cc453b64183aa65dc517b6b3c" and "7375a550eb936814a17f1db154a315579b28afad" have entirely different histories.
		
	
	
		
			c10a310c20
			...
			7375a550eb
		
	
		
							
								
								
									
										97
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								README.md
									
									
									
									
									
								
							@ -2,11 +2,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Secure-by-default redirects from HTTP to HTTPS.
 | 
					Secure-by-default redirects from HTTP to HTTPS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-   Browsers get a 301 + Location redirect
 | 
					* Browsers get a 301 + Location redirect
 | 
				
			||||||
-   Only developers, bots, and APIs see security warning (advising to use HTTPS)
 | 
					* Only developers, bots, and APIs see security warning (advising to use HTTPS)
 | 
				
			||||||
-   Always uses meta redirect as a fallback, for everyone
 | 
					* Always uses meta redirect as a fallback, for everyone
 | 
				
			||||||
-   '/' always gets a 301 (for `curl | bash` installers)
 | 
					* '/' always gets a 301 (for `curl | bash` installers)
 | 
				
			||||||
-   minimally configurable, don't get fancy
 | 
					* minimally configurable, don't get fancy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
See <https://coolaj86.com/articles/secure-your-redirects/>
 | 
					See <https://coolaj86.com/articles/secure-your-redirects/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -17,16 +17,14 @@ npm install --save redirect-https
 | 
				
			|||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```js
 | 
					```js
 | 
				
			||||||
"use strict";
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var express = require("express");
 | 
					var express = require('express');
 | 
				
			||||||
var app = express();
 | 
					var app = express();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var redirector = require("redirect-https")({
 | 
					app.use('/', require('redirect-https')({
 | 
				
			||||||
    body: "<!-- Hello Developer! Please use HTTPS instead: {{ URL }} -->"
 | 
					  body: '<!-- Hello Mr Developer! Please use HTTPS instead -->'
 | 
				
			||||||
});
 | 
					}));
 | 
				
			||||||
 | 
					 | 
				
			||||||
app.use("/", redirector);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = app;
 | 
					module.exports = app;
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
@ -42,37 +40,10 @@ module.exports = app;
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-   This module will call `next()` if the connection is already tls / https.
 | 
					* This module will call `next()` if the connection is already tls / https.
 | 
				
			||||||
-   If `trustProxy` is true, and `X-Forward-Proto` is https, `next()` will be called.
 | 
					* If `trustProxy` is true, and `X-Forward-Proto` is https, `next()` will be called.
 | 
				
			||||||
-   `{{ URL }}` in the body text will be replaced with a URI encoded and HTML escaped url (it'll look just like it is)
 | 
					* If you use `{{URL}}` in the body text it will be replaced with a URI encoded and HTML escaped url (it'll look just like it is)
 | 
				
			||||||
-   `{{ HTML_URL }}` in the body text will be replaced with a URI decoded and HTML escaped url (it'll look just like it would in Chrome's URL bar)
 | 
					* If you use `{{HTML_URL}}` in the body text it will be replaced with a URI decoded and HTML escaped url (it'll look just like it would in Chrome's URL bar)
 | 
				
			||||||
-   `{{ UNSAFE_URL }}` is the raw, original url
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Demo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```javascript
 | 
					 | 
				
			||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var http = require("http");
 | 
					 | 
				
			||||||
var server = http.createServer();
 | 
					 | 
				
			||||||
var securePort = process.argv[2] || 8443;
 | 
					 | 
				
			||||||
var insecurePort = process.argv[3] || 8080;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var redirector = require("redirect-https")({
 | 
					 | 
				
			||||||
    port: securePort,
 | 
					 | 
				
			||||||
    body: "<!-- Hello! Please use HTTPS instead: {{ URL }} -->",
 | 
					 | 
				
			||||||
    trustProxy: true // default is false
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
server.on("request", redirector);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
server.listen(insecurePort, function () {
 | 
					 | 
				
			||||||
    console.log(
 | 
					 | 
				
			||||||
        "Listening on http://localhost.rootprojects.org:" +
 | 
					 | 
				
			||||||
            server.address().port
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Advanced Options
 | 
					## Advanced Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -80,16 +51,40 @@ For the sake of `curl | bash` installers and the like there is also the option t
 | 
				
			|||||||
to get a certain redirect for an exact path match:
 | 
					to get a certain redirect for an exact path match:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```js
 | 
					```js
 | 
				
			||||||
{
 | 
					{ paths: [
 | 
				
			||||||
    paths: [
 | 
					    { match: '/'
 | 
				
			||||||
        { match: "/", redirect: 301 },
 | 
					    , redirect: 301
 | 
				
			||||||
        { match: /^\/$/, redirect: 301 }
 | 
					    }
 | 
				
			||||||
    ];
 | 
					  , { match: /^\/$/
 | 
				
			||||||
 | 
					    , redirect: 301
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you're using this, you're probably getting too fancy (but hey, I get too fancy sometimes too).
 | 
					If you're using this, you're probably getting too fancy (but hey, I get too fancy sometimes too).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```javascript
 | 
				
			||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var http = require('http');
 | 
				
			||||||
 | 
					var server = http.createServer();
 | 
				
			||||||
 | 
					var securePort = process.argv[2] || 8443;
 | 
				
			||||||
 | 
					var insecurePort = process.argv[3] || 8080;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					server.on('request', require('redirect-https')({
 | 
				
			||||||
 | 
					  port: securePort
 | 
				
			||||||
 | 
					, body: '<!-- Hello! Please use HTTPS instead -->'
 | 
				
			||||||
 | 
					, trustProxy: true // default is false
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					server.listen(insecurePort, function () {
 | 
				
			||||||
 | 
					  console.log('Listening on http://localhost.pplwink.com:' + server.address().port);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Meta redirect by default, but why?
 | 
					# Meta redirect by default, but why?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
When something is broken (i.e. insecure), you don't want it to kinda work, you want developers to notice.
 | 
					When something is broken (i.e. insecure), you don't want it to kinda work, you want developers to notice.
 | 
				
			||||||
@ -113,6 +108,6 @@ If your application is properly separated between static assets and api, then it
 | 
				
			|||||||
The incoming URL is already URI encoded by the browser but, just in case, I run an html escape on it
 | 
					The incoming URL is already URI encoded by the browser but, just in case, I run an html escape on it
 | 
				
			||||||
so that no malicious links of this sort will yield unexpected behavior:
 | 
					so that no malicious links of this sort will yield unexpected behavior:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-   `http://localhost.rootprojects.org:8080/"><script>alert('hi')</script>`
 | 
					  * `http://localhost.pplwink.com:8080/"><script>alert('hi')</script>`
 | 
				
			||||||
-   `http://localhost.rootprojects.org:8080/';URL=http://example.com`
 | 
					  * `http://localhost.pplwink.com:8080/';URL=http://example.com`
 | 
				
			||||||
-   `http://localhost.rootprojects.org:8080/;URL=http://example.com`
 | 
					  * `http://localhost.pplwink.com:8080/;URL=http://example.com`
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										179
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										179
									
								
								index.js
									
									
									
									
									
								
							@ -1,98 +1,97 @@
 | 
				
			|||||||
"use strict";
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (opts) {
 | 
					module.exports = function (opts) {
 | 
				
			||||||
    var escapeHtml = require("escape-html");
 | 
					  var escapeHtml = require('escape-html');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!opts) {
 | 
					  if (!opts) {
 | 
				
			||||||
        opts = {};
 | 
					    opts = {};
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (!isFinite(opts.port)) {
 | 
					  if (!isFinite(opts.port)) {
 | 
				
			||||||
        opts.port = 443;
 | 
					    opts.port = 443;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (!opts.browsers) {
 | 
					  if (!opts.browsers) {
 | 
				
			||||||
        opts.browsers = 301;
 | 
					    opts.browsers = 301;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (!opts.apis) {
 | 
					  if (!opts.apis) {
 | 
				
			||||||
        opts.apis = "meta";
 | 
					    opts.apis = 'meta';
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (!Array.isArray(opts.paths)) {
 | 
					  if (!Array.isArray(opts.paths)) {
 | 
				
			||||||
        opts.paths = [{ match: "/" }];
 | 
					    opts.paths = [ { match: '/' } ];
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (!("body" in opts)) {
 | 
					  if (!('body' in opts)) {
 | 
				
			||||||
        opts.body =
 | 
					    opts.body = "<!-- Hello Developer Person! We don't serve insecure resources around here."
 | 
				
			||||||
            "<!-- Hello Developer Person! We don't serve insecure resources around here." +
 | 
					      + "\n    Please use HTTPS instead. -->";
 | 
				
			||||||
            "\n    Please use HTTPS instead. -->";
 | 
					  }
 | 
				
			||||||
    }
 | 
					  opts.body = opts.body.replace(/{{\s+PORT\s+}}/ig, opts.port);
 | 
				
			||||||
    opts.body = opts.body.replace(/{{\s+PORT\s+}}/gi, opts.port);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return function (req, res, next) {
 | 
					  return function (req, res, next) {
 | 
				
			||||||
        if (
 | 
					    if (req.connection.encrypted
 | 
				
			||||||
            req.connection.encrypted ||
 | 
					      || 'https' === req.protocol
 | 
				
			||||||
            "https" === req.protocol ||
 | 
					      || (opts.trustProxy && 'https' === req.headers['x-forwarded-proto'])
 | 
				
			||||||
            (opts.trustProxy && "https" === req.headers["x-forwarded-proto"])
 | 
					    ) {
 | 
				
			||||||
        ) {
 | 
					      next();
 | 
				
			||||||
            next();
 | 
					      return;
 | 
				
			||||||
            return;
 | 
					    }
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var url = req.originalUrl || req.url;
 | 
					    var url = (req.originalUrl || req.url);
 | 
				
			||||||
        // We don't want chrome showing the "Not Secure" badge during the redirect.
 | 
					    // We don't want chrome showing the "Not Secure" badge during the redirect.
 | 
				
			||||||
        var probablyBrowser =
 | 
					    var probablyBrowser = (0 === (req.headers['user-agent']||'').indexOf('Mozilla/'));
 | 
				
			||||||
            0 === (req.headers["user-agent"] || "").indexOf("Mozilla/");
 | 
					    // But we don't want devs, APIs, or Bots to accidentally browse insecure.
 | 
				
			||||||
        // But we don't want devs, APIs, or Bots to accidentally browse insecure.
 | 
					    var redirect = probablyBrowser ? opts.browsers : opts.apis;
 | 
				
			||||||
        var redirect = probablyBrowser ? opts.browsers : opts.apis;
 | 
					    var host = req.headers.host || '';
 | 
				
			||||||
        var host = req.headers.host || "";
 | 
							if (!/:\d+/.test(host) && 443 !== opts.port) {
 | 
				
			||||||
        if (!/:\d+/.test(host) && 443 !== opts.port) {
 | 
								// we are using standard port 80, but we aren't using standard port 443
 | 
				
			||||||
            // we are using standard port 80, but we aren't using standard port 443
 | 
								host += ':80';
 | 
				
			||||||
            host += ":80";
 | 
							}
 | 
				
			||||||
        }
 | 
					    var newLocation = 'https://'
 | 
				
			||||||
        var newLocation =
 | 
					      + host.replace(/:\d+/, ':' + opts.port) + url
 | 
				
			||||||
            "https://" + host.replace(/:\d+/, ":" + opts.port) + url;
 | 
					      ;
 | 
				
			||||||
        //var encodedLocation = encodeURI(newLocation);
 | 
					 | 
				
			||||||
        var escapedLocation = escapeHtml(newLocation);
 | 
					 | 
				
			||||||
        var decodedLocation;
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            decodedLocation = decodeURIComponent(newLocation);
 | 
					 | 
				
			||||||
        } catch (e) {
 | 
					 | 
				
			||||||
            decodedLocation = newLocation; // "#/error/?error_message=" + e.toString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var body = opts.body
 | 
					    //var encodedLocation = encodeURI(newLocation);
 | 
				
			||||||
            .replace(/{{\s*HTML_URL\s*}}/gi, escapeHtml(decodedLocation))
 | 
					    var escapedLocation = escapeHtml(newLocation);
 | 
				
			||||||
            .replace(/{{\s*URL\s*}}/gi, escapedLocation)
 | 
					    var decodedLocation;
 | 
				
			||||||
            .replace(/{{\s*UNSAFE_URL\s*}}/gi, newLocation);
 | 
					    try {
 | 
				
			||||||
        var metaRedirect =
 | 
					      decodedLocation = decodeURIComponent(newLocation);
 | 
				
			||||||
            "" +
 | 
					    } catch(e) {
 | 
				
			||||||
            "<html>" +
 | 
					      decodedLocation = newLocation; // "#/error/?error_message=" + e.toString();
 | 
				
			||||||
            '\n<head><META http-equiv="refresh" content="0;URL=\'' +
 | 
					    }
 | 
				
			||||||
            escapedLocation +
 | 
					 | 
				
			||||||
            "'\"></head>" +
 | 
					 | 
				
			||||||
            "\n<body>" +
 | 
					 | 
				
			||||||
            body +
 | 
					 | 
				
			||||||
            "</body>" +
 | 
					 | 
				
			||||||
            "\n</html>\n";
 | 
					 | 
				
			||||||
        var pathMatch;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        opts.paths.some(function (p) {
 | 
					    var body = opts.body
 | 
				
			||||||
            if (!p.match) {
 | 
					          .replace(/{{\s*HTML_URL\s*}}/ig, escapeHtml(decodedLocation))
 | 
				
			||||||
                // ignore
 | 
					          .replace(/{{\s*URL\s*}}/ig, escapedLocation)
 | 
				
			||||||
            } else if ("string" === typeof p.match) {
 | 
					          .replace(/{{\s*UNSAFE_URL\s*}}/ig, newLocation)
 | 
				
			||||||
                pathMatch = url === p.match && (p.redirect || 301);
 | 
					          ;
 | 
				
			||||||
            } else {
 | 
					
 | 
				
			||||||
                pathMatch =
 | 
					    var metaRedirect = ''
 | 
				
			||||||
                    p.match.test && p.match.test(url) && (p.redirect || 301);
 | 
					      + '<html>\n'
 | 
				
			||||||
            }
 | 
					      + '<head>\n'
 | 
				
			||||||
            if (pathMatch) {
 | 
					      //+ '  <style>* { background-color: white; color: white; text-decoration: none; }</style>\n'
 | 
				
			||||||
                redirect = pathMatch;
 | 
					      + '  <META http-equiv="refresh" content="0;URL=\'' + escapedLocation + '\'">\n'
 | 
				
			||||||
            }
 | 
					      + '</head>\n'
 | 
				
			||||||
            return pathMatch;
 | 
					      + '<body>\n' + body + '\n</body>\n'
 | 
				
			||||||
        });
 | 
					      + '</html>\n'
 | 
				
			||||||
        // If it's not a non-0 number (because null is 0) then 'meta' is assumed.
 | 
					      ;
 | 
				
			||||||
        if (redirect && isFinite(redirect)) {
 | 
					    var pathMatch;
 | 
				
			||||||
            res.statusCode = redirect;
 | 
					
 | 
				
			||||||
            res.setHeader("Location", newLocation);
 | 
					    opts.paths.some(function (p) {
 | 
				
			||||||
        }
 | 
					      if (!p.match) {
 | 
				
			||||||
        res.setHeader("Content-Type", "text/html; charset=utf-8");
 | 
					        // ignore
 | 
				
			||||||
        res.end(metaRedirect);
 | 
					      } else if ('string' === typeof p.match) {
 | 
				
			||||||
    };
 | 
					        pathMatch = (url === p.match) && (p.redirect || 301);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        pathMatch = p.match.test && p.match.test(url) && (p.redirect || 301);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (pathMatch) {
 | 
				
			||||||
 | 
					        redirect = pathMatch;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return pathMatch;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // If it's not a non-0 number (because null is 0) then 'meta' is assumed.
 | 
				
			||||||
 | 
					    if (redirect && isFinite(redirect)) {
 | 
				
			||||||
 | 
					      res.statusCode = redirect;
 | 
				
			||||||
 | 
					      res.setHeader('Location', newLocation);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.setHeader('Content-Type', 'text/html; charset=utf-8');
 | 
				
			||||||
 | 
					    res.end(metaRedirect);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -1,13 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "name": "redirect-https",
 | 
					 | 
				
			||||||
  "version": "1.3.1",
 | 
					 | 
				
			||||||
  "lockfileVersion": 1,
 | 
					 | 
				
			||||||
  "requires": true,
 | 
					 | 
				
			||||||
  "dependencies": {
 | 
					 | 
				
			||||||
    "escape-html": {
 | 
					 | 
				
			||||||
      "version": "1.0.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "redirect-https",
 | 
					  "name": "redirect-https",
 | 
				
			||||||
  "version": "1.3.1",
 | 
					  "version": "1.3.0",
 | 
				
			||||||
  "description": "Redirect from HTTP to HTTPS using meta redirects",
 | 
					  "description": "Redirect from HTTP to HTTPS using meta redirects",
 | 
				
			||||||
  "main": "index.js",
 | 
					  "main": "index.js",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
@ -8,7 +8,7 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
    "type": "git",
 | 
					    "type": "git",
 | 
				
			||||||
    "url": "https://git.coolaj86.com/coolaj86/redirect-https.js.git"
 | 
					    "url": "git+https://git.coolaj86.com/coolaj86/redirect-https.js.git"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "keywords": [
 | 
					  "keywords": [
 | 
				
			||||||
    "https",
 | 
					    "https",
 | 
				
			||||||
@ -27,6 +27,5 @@
 | 
				
			|||||||
  "homepage": "https://git.coolaj86.com/coolaj86/redirect-https.js#readme",
 | 
					  "homepage": "https://git.coolaj86.com/coolaj86/redirect-https.js#readme",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "escape-html": "^1.0.3"
 | 
					    "escape-html": "^1.0.3"
 | 
				
			||||||
  },
 | 
					  }
 | 
				
			||||||
  "devDependencies": {}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user