node.js, socket.io mit SSL


163

Ich versuche, socket.io mit meinem SSL-Zertifikat zum Laufen zu bringen, es wird jedoch keine Verbindung hergestellt.

Ich habe meinen Code anhand des Chat-Beispiels erstellt:

var https = require('https');
var fs = require('fs');
/**
 * Bootstrap app.
 */
var sys = require('sys')
require.paths.unshift(__dirname + '/../../lib/');

/**
* Module dependencies.
*/

var express = require('express')
  , stylus = require('stylus')
  , nib = require('nib')
  , sio = require('socket.io');

/**
 * App.
 */
var privateKey = fs.readFileSync('../key').toString();
var certificate = fs.readFileSync('../crt').toString();
var ca = fs.readFileSync('../intermediate.crt').toString();

var app = express.createServer({key:privateKey,cert:certificate,ca:ca });


/**
 * App configuration.
 */

...

/**
 * App routes.
 */

app.get('/', function (req, res) {
  res.render('index', { layout: false });
});

/**
 * App listen.
 */

app.listen(443, function () {
  var addr = app.address();
  console.log('   app listening on http://' + addr.address + ':' + addr.port);
});

/**
 * Socket.IO server (single process only)
 */

var io = sio.listen(app,{key:privateKey,cert:certificate,ca:ca});
...

Wenn ich den SSL-Code entferne, läuft er einwandfrei, erhalte jedoch eine Anfrage an http://domain.com/socket.io/1/?t=1309967919512

Beachten Sie, dass https nicht versucht wird, was dazu führt, dass es fehlschlägt.

Ich teste auf Chrome, da es der Zielbrowser für diese Anwendung ist.

Ich entschuldige mich, wenn dies eine einfache Frage ist, ich bin ein Node / Socket.io-Neuling.

Vielen Dank!


Versucht Ihr Client, eine Verbindung zu einem URI mit dem Präfix 'wss: //' herzustellen?
Kanaka

Nein, es kommt nicht dorthin. Es sendet die Anfrage an domain.com/socket.io/1/?t=1309967919512 und stirbt dann.
Jenseits des

Wie geben Sie die Adresse an, zu der eine Verbindung hergestellt werden soll? "domain.com" klingt wie ein Platzhalter in der clientseitigen Bibliothek socket.io. Können Sie Ihren Client-Javascript-Code veröffentlichen, mit dem Sie eine Verbindung herstellen?
Kanaka

1
Das Projekt ist auf Github: Github.com/BCCasino/BCCasino
Beyond

Grundsätzlich, weil seine node.js socket.io magisch die clientseitigen Sachen behandelt, ist alles, was Sie tun, socket.connect
Beyond

Antworten:


186

Verwenden Sie eine sichere URL für Ihre erste Verbindung, dh verwenden Sie anstelle von "http: //" "https: //". Wenn der WebSocket-Transport ausgewählt ist, sollte Socket.IO auch für die WebSocket-Verbindung automatisch "wss: //" (SSL) verwenden.

Update :

Sie können auch versuchen, die Verbindung mit der Option "Sicher" herzustellen:

var socket = io.connect('https://localhost', {secure: true});

wir machen das. Wir gehen zu https: // www.thebitcoinwheel.com und es wird immer noch automatisch eine Anfrage an http gestellt. Dies ist etwas mit dem Code socket.io und der Punkt der Frage.
Jenseits des

1
Ihr habt mir das Leben gerettet! Ich konnte diese Optionen in der Dokumentation nicht finden.
Paulo Cesar

14
{secure: true}sollte nicht benötigt werden, wenn Sie in der URL 'https' angeben. Hier ist ein Auszug aus der Client-Quelle von socket.io secure: 'https' == uri.protocol(Version 0.9.16). Er setzt die sichere Option auf true, wenn https in der URL erkannt wird.
XiaoChuan Yu

4
Ich habe dies mit einer https-URL versucht und musste in der Tat {secure: true}nicht richtig funktionieren.
D Coetzee

4
Ich halte es für ratsam, sicherzustellen, dass die Verbindung sicher ist, indem Sie sowohl Secure: True als auch eine https-URL an die Clientseite senden. Auf diese Weise wird unabhängig davon, was Sie wissen, eine sichere Verbindung hergestellt.
gabeio

53

So habe ich es mit Express eingerichtet:

var fs = require( 'fs' );
var app = require('express')();
var https        = require('https');
var server = https.createServer({
    key: fs.readFileSync('./test_key.key'),
    cert: fs.readFileSync('./test_cert.crt'),
    ca: fs.readFileSync('./test_ca.crt'),
    requestCert: false,
    rejectUnauthorized: false
},app);
server.listen(8080);

var io = require('socket.io').listen(server);

io.sockets.on('connection',function (socket) {
    ...
});

app.get("/", function(request, response){
    ...
})


Ich hoffe, dass dies jemandem Zeit spart.

Update: Für diejenigen, die verwenden, können wir dies verschlüsseln

var server = https.createServer({ 
                key: fs.readFileSync('privkey.pem'),
                cert: fs.readFileSync('fullchain.pem') 
             },app);

2
Dies ist die einzige Lösung, die für mich funktioniert hat. Danke, dass du mir Zeit gespart hast.
Francisco Hodge

funktionierte gut für mich nach ein bisschen Versuch und Irrtum mit den Zertifikaten
RozzA

3
Diese Lösung hat perfekt für mich funktioniert, danke. Wenn Sie die kostenlosen Zertifikate von letsencrypt.org verwenden, können Sie den folgenden Code verwenden. var server = https.createServer({ key: fs.readFileSync('/etc/letsencrypt/live/domain.name/privkey.pem'), cert: fs.readFileSync('/etc/letsencrypt/live/domain.name/cert.pem'), ca: fs.readFileSync('/etc/letsencrypt/live/domain.name/chain.pem'), requestCert: false, rejectUnauthorized: false },app); server.listen(3000);
Hugo Rune

2
Vielen Dank für diese Antwort. Es hat mir sehr geholfen.
Harsha Jasti

2
Danke, hat wie ein Zauber mit letsencrypt- und .pem-Dateien funktioniert
Eric

33

Wenn Ihr Server beide unterstützt httpund httpsSie eine Verbindung herstellen können mit:

var socket = io.connect('//localhost');

zum Auto des Browsers Schema erkennt mit http / https entsprechend und verbinden. In https wird der Transport standardmäßig als Verbindung mit gesichert

var socket = io.connect('https://localhost');

verwendet sichere Web-Sockets - wss://(das {secure: true}ist redundant).

Weitere Informationen dazu, wie Sie sowohl http als auch https problemlos mit demselben Knotenserver bereitstellen können, finden Sie in dieser Antwort .


10

Wenn Ihre serverzertifizierte Datei nicht vertrauenswürdig ist (z. B. können Sie den Schlüsselspeicher selbst mit dem Befehl keytool in Java generieren ), sollten Sie die zusätzliche Option "RejectUnauthorized" hinzufügen

var socket = io.connect('https://localhost', {rejectUnauthorized: false});

Wir würden uns freuen, wenn Sie ein Beispiel hinzufügen würden, in dem erläutert wird, wie Sie das Keytool zum Erstellen dieses Schlüssels für den Knoten verwendet haben. Weil die Tasten so kompliziert sind und es einfach nicht genug Tutorials gibt.
bvdb

keytool ist ein Tool im Java Development Kit (JDK). Sie können diese docs.oracle.com/javase/10/tools/…
clevertension

4

Überprüfen Sie diese Konfiguration.

app = module.exports = express();
var httpsOptions = { key: fs.readFileSync('certificates/server.key'), cert: fs.readFileSync('certificates/final.crt') };        
var secureServer = require('https').createServer(httpsOptions, app);
io = module.exports = require('socket.io').listen(secureServer,{pingTimeout: 7000, pingInterval: 10000});
io.set("transports", ["xhr-polling","websocket","polling", "htmlfile"]);
secureServer.listen(3000);

2

Serverseitig:

import http from 'http';
import https from 'https';
import SocketIO, { Socket } from 'socket.io';
import fs from 'fs';
import path from 'path';

import { logger } from '../../utils';

const port: number = 3001;

const server: https.Server = https.createServer(
  {
    cert: fs.readFileSync(path.resolve(__dirname, '../../../ssl/cert.pem')),
    key: fs.readFileSync(path.resolve(__dirname, '../../../ssl/key.pem'))
  },
  (req: http.IncomingMessage, res: http.ServerResponse) => {
    logger.info(`request.url: ${req.url}`);

    let filePath = '.' + req.url;
    if (filePath === './') {
      filePath = path.resolve(__dirname, './index.html');
    }

    const extname = String(path.extname(filePath)).toLowerCase();
    const mimeTypes = {
      '.html': 'text/html',
      '.js': 'text/javascript',
      '.json': 'application/json'
    };

    const contentType = mimeTypes[extname] || 'application/octet-stream';

    fs.readFile(filePath, (error: NodeJS.ErrnoException, content: Buffer) => {
      if (error) {
        res.writeHead(500);
        return res.end(error.message);
      }
      res.writeHead(200, { 'Content-Type': contentType });
      res.end(content, 'utf-8');
    });
  }
);

const io: SocketIO.Server = SocketIO(server);

io.on('connection', (socket: Socket) => {
  socket.emit('news', { hello: 'world' });
  socket.on('updateTemplate', data => {
    logger.info(data);
    socket.emit('updateTemplate', { random: data });
  });
});

server.listen(port, () => {
  logger.info(`Https server is listening on https://localhost:${port}`);
});

Client-Seite:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Websocket Secure Connection</title>
</head>

<body>
  <div>
    <button id='btn'>Send Message</button>
    <ul id='messages'></ul>
  </div>
  <script src='../../../node_modules/socket.io-client/dist/socket.io.js'></script>
  <script>
    window.onload = function onload() {
      const socket = io('https://localhost:3001');
      socket.on('news', function (data) {
        console.log(data);
      });

      socket.on('updateTemplate', function onUpdateTemplate(data) {
        console.log(data)
        createMessage(JSON.stringify(data));
      });
      const $btn = document.getElementById('btn');
      const $messages = document.getElementById('messages');

      function sendMessage() {
        socket.emit('updateTemplate', Math.random());
      }

      function createMessage(msg) {
        const $li = document.createElement('li');
        $li.textContent = msg;
        $messages.appendChild($li);
      }

      $btn.addEventListener('click', sendMessage);
    }
  </script>
</body>

</html>

2

Abhängig von Ihren Anforderungen können Sie sowohl sichere als auch unsichere Verbindungen zulassen und dennoch nur eine Socket.io-Instanz verwenden.

Sie müssen lediglich zwei Server instanziieren, einen für HTTP und einen für HTTPS, und diese Server dann an die Socket.io-Instanz anschließen.

Serverseite:

// needed to read certificates from disk
const fs          = require( "fs"    );

// Servers with and without SSL
const http        = require( "http"  )
const https       = require( "https" );
const httpPort    = 3333;
const httpsPort   = 3334;
const httpServer  = http.createServer();
const httpsServer = https.createServer({
    "key" : fs.readFileSync( "yourcert.key" ),
    "cert": fs.readFileSync( "yourcert.crt" ),
    "ca"  : fs.readFileSync( "yourca.crt"   )
});
httpServer.listen( httpPort, function() {
    console.log(  `Listening HTTP on ${httpPort}` );
});
httpsServer.listen( httpsPort, function() {
    console.log(  `Listening HTTPS on ${httpsPort}` );
});

// Socket.io
const ioServer = require( "socket.io" );
const io       = new ioServer();
io.attach( httpServer );
io.attach( httpsServer );

io.on( "connection", function( socket ) {

    console.log( "user connected" );
    // ... your code

});

Client-Seite:

var url    = "//example.com:" + ( window.location.protocol == "https:" ? "3334" : "3333" );
var socket = io( url, {
    // set to false only if you use self-signed certificate !
    "rejectUnauthorized": true
});
socket.on( "connect", function( e ) {
    console.log( "connect", e );
});

Wenn sich Ihr NodeJS-Server von Ihrem Webserver unterscheidet, müssen Sie möglicherweise einige CORS-Header festlegen. Ersetzen Sie also auf der Serverseite:

httpServer.listen( httpPort, function() {
    console.log(  `Listening HTTP on ${httpPort}` );
});
httpsServer.listen( httpsPort, function() {
    console.log(  `Listening HTTPS on ${httpsPort}` );
});

Mit:

const httpServer  = http.createServer( (req, res) => {
    res.setHeader( "Access-Control-Allow-Origin"  , "*" );
    res.setHeader( "Access-Control-Request-Method", "*" );
    res.setHeader( "Access-Control-Allow-Methods" , "*" );
    res.setHeader( "Access-Control-Allow-Headers" , "*" );
    if ( req.method === "OPTIONS" ) {
        res.writeHead(200);
        res.end();
        return;
    }
});
const httpsServer = https.createServer({
        "key" : fs.readFileSync( "yourcert.key" ),
        "cert": fs.readFileSync( "yourcert.crt" )
    }, (req, res) => {
    res.setHeader( "Access-Control-Allow-Origin"  , "*" );
    res.setHeader( "Access-Control-Request-Method", "*" );
    res.setHeader( "Access-Control-Allow-Methods" , "*" );
    res.setHeader( "Access-Control-Allow-Headers" , "*" );
    if ( req.method === "OPTIONS" ) {
        res.writeHead(200);
        res.end();
        return;
    }
});

Und natürlich passen Sie die Werte der Header an Ihre Bedürfnisse an.


1

Bei Unternehmensanwendungen sollte beachtet werden, dass Sie in Ihrem Code kein https verarbeiten sollten. Es sollte automatisch über IIS oder Nginx aktualisiert werden. Die App sollte nicht wissen, welche Protokolle verwendet werden.


0

Dies ist meine Nginx-Konfigurationsdatei und mein Iosocket-Code. Server (Express) überwacht Port 9191. Es funktioniert gut: Nginx-Konfigurationsdatei:

server {
    listen       443 ssl;
    server_name  localhost;
    root   /usr/share/nginx/html/rdist;

    location /user/ {
        proxy_pass   http://localhost:9191;
    }
    location /api/ {
        proxy_pass   http://localhost:9191;
    }
    location /auth/ {
        proxy_pass   http://localhost:9191;
    }

    location / {
        index  index.html index.htm;
        if (!-e $request_filename){
          rewrite ^(.*)$ /index.html break;
        }
    }
    location /socket.io/ {
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass   http://localhost:9191/socket.io/;
    }


    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    ssl_certificate /etc/nginx/conf.d/sslcert/xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/sslcert/xxx.key;

}

Server:

const server = require('http').Server(app)
const io = require('socket.io')(server)
io.on('connection', (socket) => {
    handleUserConnect(socket)

  socket.on("disconnect", () => {
   handleUserDisConnect(socket)
  });
})

server.listen(9191, function () {
  console.log('Server listening on port 9191')
})

Kunde (reagieren):

    const socket = io.connect('', { secure: true, query: `userId=${this.props.user._id}` })

        socket.on('notifications', data => {
            console.log('Get messages from back end:', data)
            this.props.mergeNotifications(data)
        })
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.