การเปิดใช้งาน HTTPS บน express.js


408

ฉันพยายามทำให้ HTTPS ทำงานบน express.js สำหรับโหนดและฉันไม่สามารถหามันได้

นี่คือapp.jsรหัสของฉัน

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);

เมื่อฉันเรียกใช้ดูเหมือนว่าจะตอบกลับคำขอ HTTP เท่านั้น

ฉันเขียนnode.jsแอพ HTTPS ที่ใช้วานิลลาอย่างง่าย:

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);

และเมื่อฉันรันแอพนี้มันจะตอบสนองต่อคำขอ HTTPS โปรดทราบว่าฉันไม่คิดว่า toString () ในผลลัพธ์ของ fs มีความสำคัญเนื่องจากฉันใช้ทั้งสองอย่างร่วมกันและยังไม่มี es bueno


แก้ไขเพื่อเพิ่ม:

สำหรับระบบที่ใช้งานจริงคุณควรใช้ Nginx หรือ HAProxy เพื่อขอพร็อกซีไปยังแอป nodejs ของคุณ คุณสามารถตั้งค่า nginx เพื่อจัดการการร้องขอ ssl และเพียงแค่พูด http กับโหนด app.js.

แก้ไขเพื่อเพิ่ม (4/6/2015)

สำหรับระบบที่ใช้ AWS คุณควรใช้ EC2 Elastic Load Balancer เพื่อจัดการกับการเลิกจ้าง SSL และให้การรับส่งข้อมูล HTTP ปกติไปยังเว็บเซิร์ฟเวอร์ EC2 ของคุณ เพื่อความปลอดภัยเพิ่มเติมให้ตั้งค่ากลุ่มความปลอดภัยของคุณเพื่อให้เฉพาะ ELB เท่านั้นที่ได้รับอนุญาตให้ส่งทราฟฟิก HTTP ไปยังอินสแตนซ์ EC2 ซึ่งจะป้องกันการรับส่งข้อมูล HTTP ภายนอกที่ไม่เข้ารหัสจากการกดปุ่มเครื่องของคุณ



3
ตอบอย่างกระชับที่นี่: stackoverflow.com/a/23894573/1882064
arcseldon

เกี่ยวกับความคิดเห็นล่าสุดใน AWS: มันไม่จำเป็นต้องสร้างเซิร์ฟเวอร์ด้วยโมดูล https หรือไม่? ใบรับรองของฉันถูกอัปโหลดไปยัง AWS ผ่าน Jenkins และจัดการกับ ARN; ฉันไม่มีเส้นทางไฟล์ที่จะใช้ (ในตัวเลือก https)
sqldoug

@sqldoug ฉันไม่แน่ใจว่าฉันเข้าใจคำถาม AWS ELB สามารถกำหนดค่าให้ยอมรับการเชื่อมต่อ HTTPS และทำหน้าที่เป็นจุดยุติ SSL นั่นคือพวกเขาพูดคุยกับเซิร์ฟเวอร์แอปของคุณผ่าน HTTP ปกติ โดยทั่วไปไม่มีเหตุผลที่จะให้ nodejs จัดการกับ SSL เพราะเป็นเพียงการประมวลผลเพิ่มเติมซึ่งสามารถจัดการกับ stack ได้ทั้งในระดับ ELB หรือระดับ HTTP Proxy
อลัน

ขอบคุณอลัน; ใช่ตั้งแต่ฉันรู้ว่าโหนดไม่จำเป็นต้องจัดการกับ SSL เมื่อ AWS ELB สามารถกำหนดค่าได้
sqldoug

คำตอบ:


672

ใน express.js (ตั้งแต่รุ่น 3) คุณควรใช้ไวยากรณ์นั้น:

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);

ด้วยวิธีนี้คุณจะแสดงมิดเดิลแวร์ด่วนให้กับเซิร์ฟเวอร์ http / https ดั้งเดิม

หากคุณต้องการให้แอปของคุณทำงานบนพอร์ตที่ต่ำกว่า 1024 คุณจะต้องใช้sudoคำสั่ง (ไม่แนะนำ) หรือใช้ reverse proxy (เช่น nginx, haproxy)


2
ทั้งหมดเขียนไว้ที่นี่: github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x ฟังก์ชันแอปพลิเคชัน
codename-

74
โปรดทราบว่าแม้ว่า 443 เป็นพอร์ตเริ่มต้นสำหรับ HTTPS ในระหว่างการพัฒนาคุณอาจต้องการใช้บางอย่างเช่น 8443 เนื่องจากระบบส่วนใหญ่ไม่อนุญาตให้ผู้ฟังที่ไม่ใช่รูทบนพอร์ตที่มีหมายเลขต่ำ
ebohlman

1
คนมันใช้งานได้เหมือนเวทมนตร์ :) มันรับไฟล์. pem ได้ดีเท่าที่ควร
Marcelo Teixeira Ruggeri

5
ด่วน 4 มันใช้งานไม่ได้localhost:80แต่ไม่ได้https://localhost:443
Muhammad Umer

13
หากคุณกำลังจะใช้ nginx สำหรับ reverse proxy นั่นสามารถจัดการ ssl certs ให้คุณแทนโหนด
Gianfranco P.

48

ครั้งแรกที่คุณจำเป็นต้องสร้างselfsigned.keyและselfsigned.crtไฟล์ ไปที่สร้างใบรับรอง SSL ที่ลงชื่อด้วยตนเองหรือทำตามขั้นตอน

ไปที่เทอร์มินัลแล้วเรียกใช้คำสั่งต่อไปนี้

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

  • หลังจากนั้นให้ใส่ข้อมูลต่อไปนี้
  • ชื่อประเทศ (รหัสตัวอักษร 2 ตัว) [AU]: US
  • ชื่อรัฐหรือจังหวัด (ชื่อเต็ม) [บางรัฐ]: NY
  • ชื่อท้องถิ่น (เช่นเมือง) []: NY
  • ชื่อองค์กร (เช่น บริษัท ) [Internet Widgits Pty Ltd]: xyz (คุณ - องค์กร)
  • ชื่อหน่วยองค์กร (เช่นส่วน) []: xyz (ชื่อหน่วยของคุณ)
  • ชื่อสามัญ (เช่นเซิร์ฟเวอร์ FQDN หรือชื่อของคุณ) []: www.xyz.com (URL ของคุณ)
  • ที่อยู่อีเมล []: อีเมลของคุณ

หลังจากการสร้างเพิ่มไฟล์คีย์ & ใบรับรองในรหัสของคุณและส่งตัวเลือกไปยังเซิร์ฟเวอร์

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)
});
  • ทำงานในที่สุดโปรแกรมของคุณใช้https

ข้อมูลเพิ่มเติมhttps://github.com/sagardere/set-up-SSL-in-nodejs


การใช้งาน sudo ควรจะหมดกำลังใจหากไม่จำเป็น ฉันเพิ่งผ่านขั้นตอนนี้โดยไม่ใช้ sudo แต่ฉันเข้าสู่ระบบในฐานะผู้ดูแลระบบของเครื่อง
jhickok

27

ฉันพบปัญหาคล้ายกันกับการให้ SSL ทำงานบนพอร์ตอื่นที่ไม่ใช่พอร์ต 443 ในกรณีของฉันฉันมีใบรับรองบันเดิลรวมถึงใบรับรองและคีย์ ใบรับรองบันเดิลเป็นไฟล์ที่เก็บหลายใบรับรองโหนดต้องการให้คุณแบ่งใบรับรองเหล่านั้นออกเป็นองค์ประกอบแยกต่างหากของอาร์เรย์

    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/")
    });

ใน app.js คุณต้องระบุ https และสร้างเซิร์ฟเวอร์ตามนั้น ตรวจสอบให้แน่ใจว่าพอร์ตที่คุณพยายามใช้นั้นอนุญาตการรับส่งข้อมูลขาเข้า


ฉันมีคีย์และใบรับรองที่รวมมาฉันไม่แน่ใจว่าใบรับรองอะไร: fs.readFileSync (PATH_TO_CERT) จะเป็นอย่างไรและจะ "ทำลาย" ใบรับรองที่รวมอยู่นั้นมีใบรับรอง 20+ คีย์ในกรณีที่คุณถามฉัน :)
Muhammad Umar

@ MuhammadUmar คุณไม่ต้องแยกบันเดิลหรือแม้แต่ระบุมันถ้าคุณไม่มีมันคุณจะได้ใบรับรองมัดถ้ามีและใบรับรอง (พับลิกคีย์) และคีย์ (ไพรเวตคีย์)
Hayden Thring

@eomoto ขอบคุณตา! นี่เป็นสิ่งที่ดีที่สุดคุณตอกย้ำตัวอย่างที่ฉันต้องการ
Hayden Thring

11

รวมถึงคะแนน:

  1. การตั้งค่า SSL
    1. ใน config / local.js
    2. ใน config / env / production.js

การจัดการ HTTP และ WS

  1. แอปต้องทำงานบน HTTP ในการพัฒนาเพื่อให้เราสามารถดีบักแอปของเราได้อย่างง่ายดาย
  2. แอปต้องทำงานบน HTTPS ในการผลิตเพื่อความปลอดภัย
  3. คำขอ HTTP การผลิตแอปควรเปลี่ยนเส้นทางไปที่ https เสมอ

การกำหนดค่า SSL

ใน Sailsjs มีสองวิธีในการกำหนดค่าเนื้อหาทั้งหมดสิ่งแรกคือการกำหนดค่าในโฟลเดอร์ config โดยแต่ละไฟล์มีไฟล์แยกต่างหาก (เช่นการเชื่อมต่อฐานข้อมูลเกี่ยวกับการตั้งค่าอยู่ภายใน connections.js) และที่สองคือการกำหนดค่าในโครงสร้างไฟล์ฐานสภาพแวดล้อมแต่ละไฟล์สภาพแวดล้อมแสดงในconfig/envโฟลเดอร์และแต่ละไฟล์มีการตั้งค่าสำหรับ env โดยเฉพาะ

เริ่มต้นการค้นหาในโฟลเดอร์ config / env ก่อนแล้วจึงตั้งค่าไปที่ config / * .js

ตอนนี้ให้ 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;

ทางเลือกที่คุณสามารถเพิ่มได้ในconfig / env / production.jsเช่นกัน (ตัวอย่างนี้ยังแสดงวิธีการจัดการใบรับรอง CARoot หลายรายการ)

หรือใน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')
    }
};

http / https & ws / wss การเปลี่ยนเส้นทาง

นี่คือ Web Socket และ wss เป็นตัวแทนของ Secure Web Socket ในขณะที่เราตั้งค่า ssl แล้วตอนนี้ http และ ws คำขอทั้งสองจะปลอดภัยและเปลี่ยนเป็น https และ wss ตามลำดับ

มีหลายแหล่งที่มาจากแอพของเราจะได้รับการร้องขอเช่นโพสต์บล็อกใด ๆ โพสต์โซเชียลมีเดีย แต่เซิร์ฟเวอร์ของเราทำงานบน https เท่านั้นดังนั้นเมื่อคำขอใด ๆ มาจาก http มันจะทำให้เกิดข้อผิดพลาด และเราสูญเสียการเข้าชมเว็บไซต์ของเรา ดังนั้นเราจะต้องเปลี่ยนเส้นทางคำขอ http ไปที่ https กฎเดียวกันอนุญาตให้ websocket มิฉะนั้นซ็อกเก็ตจะล้มเหลว

ดังนั้นเราจำเป็นต้องเรียกใช้เซิร์ฟเวอร์เดียวกันบนพอร์ต 80 (http) และโอนการร้องขอทั้งหมดไปยังพอร์ต 443 (https) เริ่มต้นรวบรวมไฟล์ config / bootstrap.js ก่อนทำการยกเซิร์ฟเวอร์ ที่นี่เราสามารถเริ่มต้นเซิร์ฟเวอร์ด่วนของเราที่พอร์ต 80

ใน config / bootstrap.js (สร้างเซิร์ฟเวอร์ http และเปลี่ยนเส้นทางคำขอทั้งหมดไปยัง 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();
};

ตอนนี้คุณสามารถเยี่ยมชมhttp://www.yourdomain.com ได้แล้วมันจะเปลี่ยนเส้นทางไปที่https://www.yourdomain.com


8

ใช้ greenlock-express: SSL ฟรี, HTTPS อัตโนมัติ

Greenlockจัดการการออกใบรับรองและการต่ออายุ (ผ่าน Let's Encrypt) และ http => การเปลี่ยนเส้นทาง https ออกจากกล่อง

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: 'john.doe@example.com'
, 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

ชมการสาธิต QuickStart: https://youtu.be/e8vaR4CEZ5s

สำหรับ Localhost

เพียงตอบคำถามนี้ล่วงหน้าเพราะเป็นคำถามที่ตามมา:

คุณไม่สามารถมีใบรับรอง SSL บน localhost อย่างไรก็ตามคุณสามารถใช้บางอย่างเช่นTelebitซึ่งจะช่วยให้คุณเรียกใช้แอพในพื้นที่ได้เหมือนจริง

คุณยังสามารถใช้โดเมนส่วนตัวที่มี Greenlock ผ่านการท้าทาย DNS-01 ซึ่งกล่าวถึงใน README พร้อมด้วยปลั๊กอินต่างๆที่รองรับ

พอร์ตที่ไม่ได้มาตรฐาน (เช่นไม่มี 80/443)

อ่านหมายเหตุด้านบนเกี่ยวกับ localhost - คุณไม่สามารถใช้พอร์ตที่ไม่ได้มาตรฐานด้วย Let's Encrypt ก็ได้

อย่างไรก็ตามคุณสามารถเปิดเผยพอร์ตที่ไม่ได้มาตรฐานภายในเป็นพอร์ตมาตรฐานภายนอกผ่านทางพอร์ตไปข้างหน้า, sni-route หรือใช้บางอย่างเช่น Telebit ที่ใช้การกำหนดเส้นทาง SNI และการส่งต่อ / ส่งต่อพอร์ตสำหรับคุณ

คุณยังสามารถใช้ความท้าทาย DNS-01 ในกรณีที่คุณไม่จำเป็นต้องเปิดเผยพอร์ตเลยและคุณสามารถรักษาความปลอดภัยโดเมนบนเครือข่ายส่วนตัวด้วยวิธีนี้


"คุณไม่มีใบรับรอง SSL บน localhost" - ฉันใช้ SSL ทำงานบนแอป React บน localhost มาที่นี่เพื่อหาวิธีทำให้มันใช้งานได้ใน Express ปฏิกิริยาคือส่วนหน้าของฉันและ Express คือส่วนแบ็คเอนด์ของฉัน ต้องใช้เพื่อทำงานกับ Stripe เนื่องจากโพสต์ของฉันไปที่ Stripe ต้องอยู่ใน SSL ควรชัดเจน แต่ใน localhost ฉันกำลังทดสอบและบนเซิร์ฟเวอร์จะเป็นการผลิต
Taersious

การแก้ไข: "คุณไม่สามารถมีใบรับรอง SSL ที่ถูกต้องบน localhost"
CoolAJ86

6

นี่คือวิธีการทำงานสำหรับฉัน การเปลี่ยนเส้นทางที่ใช้จะเปลี่ยนเส้นทาง http ปกติทั้งหมดเช่นกัน

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);

0

นี้เป็นของฉันทำงานรหัสสำหรับด่วน 4.0

ด่วน 4.0 แตกต่างจาก 3.0 และอื่น ๆ อย่างมาก

4.0 คุณมีไฟล์ / bin / www ซึ่งคุณจะเพิ่ม https ที่นี่

"npm start" เป็นวิธีมาตรฐานในการเริ่มเซิร์ฟเวอร์ Express 4.0

ฟังก์ชัน readFileSync () ควรใช้__dirnameรับไดเรกทอรีปัจจุบัน

ในขณะที่ต้องการ () ใช้. /อ้างถึงไดเรกทอรีปัจจุบัน

ครั้งแรกที่คุณใส่ private.key และ public.cert ไฟล์ภายใต้โฟลเดอร์ bin / มันเป็นโฟลเดอร์เดียวกันเป็นแฟ้ม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.