Habilitar HTTPS en express.js

408

Estoy tratando de hacer que HTTPS funcione en express.js para el nodo, y no puedo entenderlo.

Este es mi app.jscodigo.

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

var privateKey = fs.readFileSync('sslcert/server.key');
var certificate = fs.readFileSync('sslcert/server.crt');

var credentials = {key: privateKey, cert: certificate};


var app = express.createServer(credentials);

app.get('/', function(req,res) {
    res.send('hello');
});

app.listen(8000);

Cuando lo ejecuto, parece que solo responde a las solicitudes HTTP.

Escribí una node.jsaplicación HTTPS simple basada en vainilla :

var   fs = require("fs"),
      http = require("https");

var privateKey = fs.readFileSync('sslcert/server.key').toString();
var certificate = fs.readFileSync('sslcert/server.crt').toString();

var credentials = {key: privateKey, cert: certificate};

var server = http.createServer(credentials,function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
});

server.listen(8000);

Y cuando corro esta aplicación, se hace responder a solicitudes HTTPS. Tenga en cuenta que no creo que toString () en el resultado fs importe, ya que he usado combinaciones de ambos y todavía no es bueno.


EDITAR PARA AGREGAR:

Para los sistemas de producción, probablemente sea mejor usar Nginx o HAProxy para enviar solicitudes a su aplicación nodejs. Puede configurar nginx para manejar las solicitudes ssl y simplemente hablar http a su nodo app.js.

EDITAR PARA AÑADIR (4/6/2015)

Para los sistemas que usan AWS, es mejor usar EC2 Elastic Load Balancers para manejar la terminación SSL y permitir el tráfico HTTP regular a sus servidores web EC2. Para mayor seguridad, configure su grupo de seguridad de modo que solo el ELB pueda enviar tráfico HTTP a las instancias EC2, lo que evitará que el tráfico HTTP externo no encriptado llegue a sus máquinas.


Alan
fuente
3
Respondido sucintamente aquí: stackoverflow.com/a/23894573/1882064
arcseldon
Con respecto al último comentario sobre AWS: ¿es que no es necesario crear un servidor con el módulo https? Mis certificados se cargan en AWS a través de Jenkins y se manejan con ARN; No tengo rutas de archivo para usar (en opciones https)
sqldoug
@sqldoug No estoy seguro de entender la pregunta. Los AWS ELB se pueden configurar para aceptar conexiones HTTPS y actuar como punto de terminación SSL. Es decir, hablan con sus servidores de aplicaciones a través de HTTP regular. Por lo general, no hay una razón para que los nodejs se ocupen de SSL, porque es solo una sobrecarga de procesamiento adicional que se puede manejar en la pila en el nivel ELB o en el nivel de proxy HTTP.
Alan
Gracias alan; Sí, me he dado cuenta de que Node no necesita lidiar con SSL cuando los AWS ELB pueden configurarse de esta manera.
sqldoug

Respuestas:

673

En express.js (desde la versión 3) debe usar esa sintaxis:

var fs = require('fs');
var http = require('http');
var https = require('https');
var privateKey  = fs.readFileSync('sslcert/server.key', 'utf8');
var certificate = fs.readFileSync('sslcert/server.crt', 'utf8');

var credentials = {key: privateKey, cert: certificate};
var express = require('express');
var app = express();

// your express configuration here

var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

httpServer.listen(8080);
httpsServer.listen(8443);

De esa forma, proporciona middleware rápido al servidor http / https nativo

Si desea que su aplicación se ejecute en puertos inferiores a 1024, deberá usar el sudocomando (no recomendado) o un proxy inverso (por ejemplo, nginx, haproxy).

nombre clave-
fuente
2
Todo está escrito aquí: github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x Párrafo Función de aplicación
nombre
74
Tenga en cuenta que aunque 443 es el puerto predeterminado para HTTPS, durante el desarrollo probablemente desee usar algo como 8443 porque la mayoría de los sistemas no permiten escuchas no root en puertos de bajo número.
ebohlman
1
Hombre, funciona como magia :) También acepta archivos .pem, como debería ser de todos modos
Marcelo Teixeira Ruggeri
55
express 4 no funciona, funciona localhost:80pero nohttps://localhost:443
Muhammad Umer
13
si va a usar nginx para proxy inverso, eso puede manejar los certificados SSL por usted en lugar de nodo
Gianfranco P.
48

Primero, debe crear los archivos selfsigned.key y selfsigned.crt . Vaya a Crear un certificado SSL autofirmado o siga los siguientes pasos.

Vaya a la terminal y ejecute el siguiente comando.

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./selfsigned.key -out selfsigned.crt

  • Después de eso pon la siguiente información
  • Nombre del país (código de 2 letras) [AU]: EE . UU.
  • Nombre del estado o provincia (nombre completo) [Algún estado]: NY
  • Nombre de la localidad (por ejemplo, ciudad) []: NY
  • Nombre de la organización (por ejemplo, empresa) [Internet Widgits Pty Ltd]: xyz (Su - Organización)
  • Nombre de la unidad organizativa (p. Ej., Sección) []: xyz (el nombre de su unidad)
  • Nombre común (por ejemplo, FQDN del servidor o SU nombre) []: www.xyz.com (Su URL)
  • Dirección de correo electrónico []: su correo electrónico

Después de la creación, agrega el archivo de clave y certificado en su código y pasa las opciones al servidor.

const express = require('express');
const https = require('https');
const fs = require('fs');
const port = 3000;

var key = fs.readFileSync(__dirname + '/../certs/selfsigned.key');
var cert = fs.readFileSync(__dirname + '/../certs/selfsigned.crt');
var options = {
  key: key,
  cert: cert
};

app = express()
app.get('/', (req, res) => {
   res.send('Now using https..');
});

var server = https.createServer(options, app);

server.listen(port, () => {
  console.log("server starting on port : " + port)
});
  • Finalmente ejecuta tu aplicación usando https .

Más información https://github.com/sagardere/set-up-SSL-in-nodejs

Dere Sagar
fuente
Se debe desaconsejar el uso de sudo a menos que sea necesario. Acabo de pasar por este proceso sin usar sudo, pero inicié sesión como administrador en la máquina.
jhickok
27

Me encontré con un problema similar al hacer que SSL funcione en un puerto que no sea el puerto 443. En mi caso, tenía un certificado de paquete, así como un certificado y una clave. El certificado de paquete es un archivo que contiene varios certificados, el nodo requiere que divida esos certificados en elementos separados de una matriz.

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

    var options = {
      ca: [fs.readFileSync(PATH_TO_BUNDLE_CERT_1), fs.readFileSync(PATH_TO_BUNDLE_CERT_2)],
      cert: fs.readFileSync(PATH_TO_CERT),
      key: fs.readFileSync(PATH_TO_KEY)
    };

    app = express()

    app.get('/', function(req,res) {
        res.send('hello');
    });

    var server = https.createServer(options, app);

    server.listen(8001, function(){
        console.log("server running at https://IP_ADDRESS:8001/")
    });

En app.js debe especificar https y crear el servidor en consecuencia. Además, asegúrese de que el puerto que está tratando de usar realmente permita el tráfico entrante.

eomoto
fuente
tengo una clave y un certificado incluido, no estoy seguro de qué cert: fs.readFileSync (PATH_TO_CERT) sería y cómo "romper" el certificado incluido, hay más de 20 claves en el certificado si me preguntas :)
Muhammad Umar
@MuhammadUmar no tiene que romper el paquete o incluso especificarlo si no tiene uno, tendrá un certificado de paquete si corresponde, y cert (clave pública) y clave (clave privada)
Hayden Thring
@eomoto gracias amigo! este es el mejor, has clavado totalmente el ejemplo que necesitaba
Hayden Thring
11

Incluyendo puntos:

  1. Configuración SSL
    1. En config / local.js
    2. En config / env / production.js

Manejo de HTTP y WS

  1. La aplicación debe ejecutarse en HTTP en desarrollo para que podamos depurar fácilmente nuestra aplicación.
  2. La aplicación debe ejecutarse en HTTPS en producción por motivos de seguridad.
  3. La solicitud HTTP de producción de aplicaciones siempre debe redirigirse a https.

Configuración SSL

En Sailsjs hay dos formas de configurar todas las cosas, primero es configurar en la carpeta de configuración con cada uno tiene sus archivos separados (como la conexión de la base de datos con respecto a la configuración se encuentra dentro de connections.js). Y el segundo es configurar la estructura del archivo base del entorno, cada archivo de entorno se presenta en una config/envcarpeta y cada archivo contiene configuraciones para un entorno particular.

Sails primero busca en la carpeta config / env y luego espera config / * .js

Ahora vamos a configurar ssl config/local.js.

var local = {
   port: process.env.PORT || 1337,
   environment: process.env.NODE_ENV || 'development'
};

if (process.env.NODE_ENV == 'production') {
    local.ssl = {
        secureProtocol: 'SSLv23_method',
        secureOptions: require('constants').SSL_OP_NO_SSLv3,
        ca: require('fs').readFileSync(__dirname + '/path/to/ca.crt','ascii'),
        key: require('fs').readFileSync(__dirname + '/path/to/jsbot.key','ascii'),
        cert: require('fs').readFileSync(__dirname + '/path/to/jsbot.crt','ascii')
    };
    local.port = 443; // This port should be different than your default port
}

module.exports = local;

Alternativa, también puede agregar esto en config / env / production.js . (Este fragmento también muestra cómo manejar múltiples certificados CARoot)

O en production.js

module.exports = {
    port: 443,
    ssl: {
        secureProtocol: 'SSLv23_method',
        secureOptions: require('constants').SSL_OP_NO_SSLv3,
        ca: [
            require('fs').readFileSync(__dirname + '/path/to/AddTrustExternalCARoot.crt', 'ascii'),
            require('fs').readFileSync(__dirname + '/path/to/COMODORSAAddTrustCA.crt', 'ascii'),
            require('fs').readFileSync(__dirname + '/path/to/COMODORSADomainValidationSecureServerCA.crt', 'ascii')
        ],
        key: require('fs').readFileSync(__dirname + '/path/to/jsbot.key', 'ascii'),
        cert: require('fs').readFileSync(__dirname + '/path/to/jsbot.crt', 'ascii')
    }
};

redirección http / https & ws / wss

Aquí ws es Web Socket y wss representan Secure Web Socket, ya que configuramos ssl y ahora http y ws ambas solicitudes se vuelven seguras y se transforman en https y wss respectivamente.

Hay muchas fuentes de nuestra aplicación que recibirán solicitudes como cualquier publicación de blog, publicación en redes sociales, pero nuestro servidor se ejecuta solo en https, por lo que cuando cualquier solicitud proviene de http, aparece el error "No se puede acceder a este sitio" en el navegador del cliente. Y perdemos el tráfico de nuestro sitio web. Por lo tanto, debemos redirigir la solicitud http a https, las mismas reglas permiten websocket; de lo contrario, el socket fallará.

Por lo tanto, debemos ejecutar el mismo servidor en el puerto 80 (http) y desviar todas las solicitudes al puerto 443 (https). Primero compila el archivo config / bootstrap.js antes de levantar el servidor. Aquí podemos iniciar nuestro servidor express en el puerto 80.

En config / bootstrap.js (Cree un servidor http y redirija todas las solicitudes a https)

module.exports.bootstrap = function(cb) {
    var express = require("express"),
        app = express();

    app.get('*', function(req, res) {  
        if (req.isSocket) 
            return res.redirect('wss://' + req.headers.host + req.url)  

        return res.redirect('https://' + req.headers.host + req.url)  
    }).listen(80);
    cb();
};

Ahora puede visitar http://www.yourdomain.com , redirigirá a https://www.yourdomain.com

Nishchit Dhanani
fuente
8

Utilice greenlock-express: SSL gratuito, HTTPS automatizado

Greenlock maneja la emisión y renovación de certificados (a través de Let's Encrypt) y la redirección http => https, lista para usar .

express-app.js:

var express = require('express');
var app = express();

app.use('/', function (req, res) {
  res.send({ msg: "Hello, Encrypted World!" })
});

// DO NOT DO app.listen()
// Instead export your app:
module.exports = app;

server.js:

require('greenlock-express').create({
  // Let's Encrypt v2 is ACME draft 11
  version: 'draft-11'
, server: 'https://acme-v02.api.letsencrypt.org/directory'

  // You MUST change these to valid email and domains
, email: '[email protected]'
, approveDomains: [ 'example.com', 'www.example.com' ]
, agreeTos: true
, configDir: "/path/to/project/acme/"

, app: require('./express-app.j')

, communityMember: true // Get notified of important updates
, telemetry: true       // Contribute telemetry data to the project
}).listen(80, 443);

Screencast

Vea la demostración de inicio rápido: https://youtu.be/e8vaR4CEZ5s

Para localhost

Solo respondo esto con anticipación porque es una pregunta de seguimiento común:

No puede tener certificados SSL en localhost. Sin embargo, puede usar algo como Telebit que le permitirá ejecutar aplicaciones locales como reales.

También puede usar dominios privados con Greenlock a través de los desafíos DNS-01, que se mencionan en el archivo README junto con varios complementos que lo admiten.

Puertos no estándar (es decir, no 80/443)

Lea la nota anterior sobre localhost: tampoco puede usar puertos no estándar con Let's Encrypt.

Sin embargo, puede exponer sus puertos internos no estándar como puertos estándar externos a través de reenvío de puertos, sni-route, o usar algo como Telebit que hace el enrutamiento SNI y el reenvío / retransmisión de puertos por usted.

También puede usar los desafíos de DNS-01, en cuyo caso no necesitará exponer puertos en absoluto y también puede asegurar dominios en redes privadas de esta manera.

CoolAJ86
fuente
"No puede tener certificados SSL en localhost". - Tengo SSL trabajando en mi aplicación React en localhost. Vine aquí buscando cómo hacerlo funcionar en Express. React es mi interfaz y Express es mi backend. Necesito que funcione para Stripe, ya que mi publicación en Stripe debe estar en SSL. Debería ser obvio, pero en localhost lo estoy probando, y en el servidor será producción.
Taersious
Corrección: "No puede tener certificados SSL válidos en localhost".
CoolAJ86
6

Así es como funciona para mí. La redirección utilizada redirigirá también todos los http normales.

const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const http = require('http');
const app = express();
var request = require('request');
//For https
const https = require('https');
var fs = require('fs');
var options = {
  key: fs.readFileSync('certificates/private.key'),
  cert: fs.readFileSync('certificates/certificate.crt'),
  ca: fs.readFileSync('certificates/ca_bundle.crt')
};

// API file for interacting with MongoDB
const api = require('./server/routes/api');

// Parsers
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// Angular DIST output folder
app.use(express.static(path.join(__dirname, 'dist')));

// API location
app.use('/api', api);

// Send all other requests to the Angular app
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist/index.html'));
});
app.use(function(req,resp,next){
  if (req.headers['x-forwarded-proto'] == 'http') {
      return resp.redirect(301, 'https://' + req.headers.host + '/');
  } else {
      return next();
  }
});


http.createServer(app).listen(80)
https.createServer(options, app).listen(443);
Shantanu Chandra
fuente
0

Este es mi código de trabajo para express 4.0 .

express 4.0 es muy diferente de 3.0 y otros.

4.0 tiene el archivo / bin / www, que va a agregar https aquí.

"npm start" es la forma estándar de iniciar el servidor express 4.0.

La función readFileSync () debe usar __dirname obtener el directorio actual

mientras require () use ./ consulte el directorio actual.

Primero coloca el archivo private.key y public.cert en la carpeta / bin, es la misma carpeta que el archivo WWW .

hoogw
fuente