IT TIP

Express.js에서 SSL / https를 강제하는 방법

itqueen 2020. 12. 29. 08:12
반응형

Express.js에서 SSL / https를 강제하는 방법


모든 비보안 (포트 80) 트래픽을 보안 SSL 포트 (443)로 리디렉션하도록 Express.js 용 미들웨어를 만들려고합니다. 불행히도 Express.js 요청에는 요청이 http 또는 https를 통해 오는지 확인할 수있는 정보가 없습니다.

한 가지 해결책은 모든 요청 을 리디렉션 하는 것이지만 이것은 나를위한 옵션이 아닙니다.

메모:

  1. Apache 또는 다른 것으로 처리 할 가능성이 없습니다. 그것은 노드에서 수행되어야한다.

  2. 응용 프로그램에서 하나의 서버 시작할 수 있습니다.

어떻게 해결할까요?


Heroku에서 호스팅하고 포트에 관계없이 HTTPS로 리디렉션하려는 경우를 대비하여 우리가 사용하는 미들웨어 솔루션은 다음과 같습니다.

로컬에서 개발하는 경우 리디렉션을 신경 쓰지 않습니다.

function requireHTTPS(req, res, next) {
  // The 'x-forwarded-proto' check is for Heroku
  if (!req.secure && req.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV !== "development") {
    return res.redirect('https://' + req.get('host') + req.url);
  }
  next();
}

다음과 같이 Express (2.x 및 4.x)와 함께 사용할 수 있습니다.

app.use(requireHTTPS);

질문이 1 년 전처럼 보이지만 다른 사람들에게 도움이 될 수 있으므로 대답하고 싶습니다. 최신 버전의 expressjs (2.x)에서는 실제로 정말 간단합니다. 먼저이 코드를 사용하여 키와 인증서를 만듭니다.

openssl genrsa -out ssl-key.pem 1024

$ openssl req -new -key ssl-key.pem -out certrequest.csr .. 여러 프롬프트

$ openssl x509 -req -in certrequest.csr -signkey ssl-key.pem -out ssl-cert.pem

app.js가 포함 된 폴더에 인증서 및 키 파일을 저장합니다. 그런 다음 app.js 파일을 편집하고 express.createServer () 전에 다음 코드를 작성하십시오.

var https = require('https');
var fs = require('fs');

var sslkey = fs.readFileSync('ssl-key.pem');
var sslcert = fs.readFileSync('ssl-cert.pem')

var options = {
    key: sslkey,
    cert: sslcert
};

이제 optionscreateServer () 함수에 객체를 전달합니다.

express.createServer(options);

끝난!


먼저 문제를 명확히 할 수 있는지 살펴 보겠습니다. 하나의 node.js 프로세스로 제한되어 있지만 해당 프로세스는 80과 443의 두 네트워크 포트에서 수신 할 수 있습니다. 맞습니까? ( 하나의 서버 라고 말할 때 하나의 프로세스를 의미하는지 아니면 하나의 네트워크 포트만을 의미하는지 명확하지 않습니다).

그 제약 조건을 감안할 때 문제는 제공하지 않는 이유로 클라이언트가 잘못된 포트에 연결하는 것 같습니다. 이것은 기본적으로 클라이언트가 포트 80에 HTTP 요청을하고 포트 443에 HTTPS를 요청하기 때문에 기이 한 경우입니다. 그리고 "기본적으로"라고 말하면 URL에 특정 포트가 포함되어 있지 않다는 의미입니다. 따라서 http://example.com:443https://example.com:80 과 같은 교차 URL을 명시 적으로 사용하지 않는 한 , 사이트에 교차 트래픽이 발생하지 않아야합니다. 그러나 질문을했기 때문에 80/443 기본값이 아닌 비표준 포트를 사용하고 있다고 확신하지만 반드시 있어야한다고 생각합니다.

따라서 배경 : 예 일부 웹 서버는 이것을 합리적으로 잘 처리합니다. 예를 들어 nginx에 http://example.com:443수행하면 "일반 HTTP 요청이 HTTPS 포트로 전송되었습니다"를 나타내는 HTTP 400 "잘못된 요청"응답으로 응답합니다. 예, 동일한 node.js 프로세스에서 80과 443을 모두 수신 할 수 있습니다. 의 2 개의 개별 인스턴스를 생성 express.createServer()하면되므로 문제 없습니다. 다음은 두 프로토콜을 모두 처리하는 방법을 보여주는 간단한 프로그램입니다.

var fs = require("fs");
var express = require("express");

var http = express.createServer();

var httpsOptions = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
};

var https = express.createServer(httpsOptions);

http.all('*', function(req, res) {
  console.log("HTTP: " + req.url);
  return res.redirect("https://" + req.headers["host"] + req.url);
});

http.error(function(error, req, res, next) {
  return console.log("HTTP error " + error + ", " + req.url);
});

https.error(function(error, req, res, next) {
  return console.log("HTTPS error " + error + ", " + req.url);
});

https.all('*', function(req, res) {
  console.log("HTTPS: " + req.url);
  return res.send("Hello, World!");
});

http.listen(80);

그리고 다음과 같이 cURL을 통해 이것을 테스트 할 수 있습니다.

$ curl --include --silent http://localhost/foo
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo
Connection: keep-alive
Transfer-Encoding: chunked

<p>Moved Temporarily. Redirecting to <a href="https://localhost/foo">https://localhost/foo</a></p>

$ curl --include --silent --insecure https://localhost:443/foo
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

HTTP에서 HTTPS 로의 리디렉션을 표시합니다.

curl --include --silent --location --insecure 'http://localhost/foo?bar=bux'
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo?bar=bux
Connection: keep-alive
Transfer-Encoding: chunked

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

So that will work to serve both protocols for the regular case and redirect properly. However, criss-crosses don't work at all. I believe a criss-crossed request hitting an express server isn't going to get routed through the middleware stack because it will be treated as an error from the very beginning and won't even get the request URI parsed properly, which is necessary to send it through the route middleware chain. The express stack doesn't even get them I think because they are not valid requests, so they get ignored somewhere in the node TCP stack. It's probably possible to write a server to do this, and there may already be a module out there, but you'd have to write it at the TCP layer directly. And you'd have to detect a regular HTTP request in the first chunk of client data that hits the TCP port and wire that connection to an HTTP server instead of your normal TLS handshake.

When I do either of these, my express error handlers do NOT get called.

curl  --insecure https://localhost:80/foo
curl: (35) Unknown SSL protocol error in connection to localhost:80

curl http://localhost:443/foo
curl: (52) Empty reply from server

Based on Elias's answer but with inline code. This works if you have node behind nginx or a load balancer. Nginx or the load balancer will always hit node with plain old http, but it sets a header so you can distinguish.

app.use(function(req, res, next) {
  var schema = req.headers['x-forwarded-proto'];

  if (schema === 'https') {
    // Already https; don't do anything special.
    next();
  }
  else {
    // Redirect to https.
    res.redirect('https://' + req.headers.host + req.url);
  }
});

http.createServer(app.handle).listen(80)

https.createServer(options, app.handle).listen(443)

for express 2x


This code looks like it does what you need: https://gist.github.com/903596


Try this example :

var express = require('express');
        var app = express();
        // set up a route to redirect http to https
        app.use(function (req, res, next) {
        if (!/https/.test(req.protocol)) {
            res.redirect("https://" + req.headers.host + req.url);
        } else {
            return next();
        }
        });
        var webServer = app.listen(port, function () {
            console.log('Listening on port %d', webServer.address().port);
        });

Since I was working on nginx, I had access to the header's x-forwarded-proto property so I could write a tiny middleware to redirect all traffic as described here: http://elias.kg/post/14971446990/force-ssl-with-express-js-on-heroku-nginx

Edit: Updated the url

ReferenceURL : https://stackoverflow.com/questions/8605720/how-to-force-ssl-https-in-express-js

반응형