웹에서 nodejs 소켓통신시 보안 관련 인증서 이슈가 있다.
클라이언트에서 node서버로 통신에는 문제 없겠으나,
node서버 내에서 통신시 ssl을 적용하지 않으면 문제가 되는 이슈가 있는데,
이를 해결 하기 위해서 node 서버측 스크립트(server.js)에 직접 ssl인증서 정보를 추가하는 방법이 있는데
이는 보안에 취약하다.
그래서, 가상호스트에 Reverse Proxy를 적용해
클라이언트와 서버간 데이타 전송은 ssl로 하고, node서버에서 내부 통신을 통해서 데이타 전송을 하면 보안 이슈가 해결 된다.
아래 클라이언트, 서버 관련 기본 소스와 가상호스트 내용 추가한다.
* 클라이언트 페이지
<script src="socket.io.js"></script>
<script>
var socket = io.connect('https://yourdomain.com', {
requestCert: true,
secure: true,
rejectUnauthorized: false,
transports: ['websocket'] //통신 방법
});
var socketid;
socket.on('connect', function(){
socketid = socket.id;
});
</script>
* node 서버측 스크립트
const os = require('os');
const nodeStatic = require('node-static');
const socketIO = require('socket.io');
const http = require('http');
// 💡 CORS 설정: 클라이언트가 접속하는 도메인으로 변경해야 합니다.
const ALLOWED_ORIGIN = "http://abc.com";
const options = {};
// 모든 네트워크 인터페이스(0.0.0.0)의 3000 포트에서 요청 수신
// Node.js 서버는 HTTP로만 응답하며, SSL 인증서는 Apache가 처리합니다.
var server = http.createServer(options, (req, res) => {
// '/socket.io/' 경로가 아니거나, 파일이 존재하지 않는 경우 등 필요한 경우에만 정적 파일을 제공
// Socket.IO는 '/socket.io/' 경로를 내부적으로 처리합니다.
}).listen(3000, '127.0.0.1', () => { // 'localhost' 대신 '0.0.0.0' 사용
console.log('✅ Socket.IO server started and listening on 127.0.0.1:3000...');
});
// Socket.IO 서버 초기화 및 CORS 옵션 적용
var io = socketIO.listen(server, {
cors: {
// 💡 중요: 클라이언트 웹페이지가 로드되는 도메인을 정확히 지정합니다.
origin: ALLOWED_ORIGIN,
methods: ["GET", "POST"],
credentials: true
}
});
io.sockets.on('connection', function(socket) {
function log() {
var array = ['[Server Log]'];
array.push.apply(array, arguments);
socket.emit('log', array);
}
// 클라이언트 연결 이벤트 (Apache 리버스 프록시를 통과한 후)
log('Client connected. Socket ID:', socket.id);
// 리버스 프록시를 통해 전달된 클라이언트 정보 확인
// Apache Vhost 설정에서 'X-Real-IP' 헤더를 전달했으므로, 이 정보를 확인할 수 있습니다.
const realIp = socket.request.headers['x-real-ip'] || socket.request.connection.remoteAddress;
log(`Client IP (via Proxy): ${realIp}`);
// 클라이언트가 보낸 연결 확인 이벤트 수신
socket.on('client_connected', function(data) {
log(`Client ID confirmation received: ${data.id}`);
// 여기에 추가적인 인증 또는 초기화 로직을 넣을 수 있습니다.
});
// 기타 소켓 이벤트 리스너 추가...
});
* 서버의 가상호스트
<VirtualHost *:80>
ServerName yourdomain.com
# Rewrite 모듈을 사용하여 HTTPS로 리다이렉트
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>
<VirtualHost *:443>
ServerName yourdomain.com
LogLevel warn
SSLEngine on
SSLProtocol -ALL +TLSv1.2
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/yourdomain.com/chain.pem
CustomLog "logs/yourdomain.com-ssl_access_log" combined
# ProxyRequests Off # 리버스 프록시 모드로 설정
ProxyPreserveHost On
ProxyTimeout 600
RequestHeader set X-Forwarded-Proto "https"
# Socket.IO 프록시 설정
ProxyPass /socket.io/ ws://127.0.0.1:3000/socket.io/
ProxyPassReverse /socket.io/ ws://127.0.0.1:3000/socket.io/
# 일반 HTTP(s) 프록시 설정
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>