WEB/웹통신 관련

WebSocket 웹소켓 https, SSL 적용 관련

AlrepondTech 2018. 5. 11. 11:46
반응형

 

 

 

 

=======================

=======================

=======================

 

 

 

 

 

 

 

 

출처ㅣ http://nerobong2.blogspot.kr/2017/01/nodejs-websocket-ssl.html

 

 

[nodejs] webSocket ssl 적용.

 
사실상 소스코드는 복잡하지가 않다.
 
 
//의존모듈들 없는것을 설치
 
var https = require('https');
var fs   = require('fs');
var WebSocketServer = require('websocket').server;
 
//ca, key, cert 세가지 인자가 필요한데. 각파일의 자세한 기능은 솔직히 잘모르겟음.
 
var https_options = {
 ca: fs.readFileSync("/디렉토리/xxxx.pem 인증서정보 여겨짐"),
 key: fs.readFileSync("/디렉토리/xxxx.key 공개키정보로 여겨짐"),
 cert: fs.readFileSync("/디렉토리/xxxx.crt" 도메인정보 로 여겨짐)
};
 
//위에서 생성한 옵션을 넘겨서 https 서버를 생성하한다.
var httpsServer = https.createServer( https_options, function(request, response) {
    console.log((new Date()) + ' Received request for ' + request.url);
    response.writeHead(404);
    response.end();
});
 
//포트설정
httpsServer.listen(8887);
 
//ws 모듈이 내부적으로 없을경우 설치
var WebSocketServer = require('ws').Server;
 
//웹소켓 서버 생성
var wss = new WebSocketServer({
    server: httpsServer,
    // You should not use autoAcceptConnections for production
    // applications, as it defeats all standard cross-origin protection
    // facilities built into the protocol and the browser.  You should
    // *always* verify the connection's origin and decide whether or not
    // to accept it.
    autoAcceptConnections: false
});
 
function originIsAllowed(origin) {
  // put logic here to detect whether the specified origin is allowed.
  return true;
}
 
 
 
wss.on('connection', function connection(ws) {
 
        //.수신처리 
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
        // 발신
        ws.send('something');
});
 
일반 http 프로토콜 기반 웹소켓 서버를 생성하는것과 그렇게 큰차이는 없다. 
 
 
 
그치만 ssl에 대한 기본 적인 개념이 없다면 아마 막히는 부분이 많을 것이다.
 
ssl 통신.
 
클라이언트 측에서 https://aaa.com 요청을 보내면

1. aaa.com 도메인에 연결된 웹서버에서 인증서 정보화 공개키 정보를 담아서 클라이언트에게 전달한다.

2. 전달받은 클라이언트는 브라우저 내부적으로 인증기관정보 및 도메인 정보를 가지고 신뢰할수 있는 도메인 유무를 확인후 랜덤으로 생성 한 대칭키를 클라이언트에게 전달한다.

3. 클라이언트는 전달받은 대칭키로 데이터및 url 을 암호화 하고 대칭키를 처음에 서버로부터 받은 공개키로 또 암호화를해서 서버로 데이터를 전송한다.

4. 서버는 받은 대칭키를 개인키로 복호화하고 복호화된 대칭키로 데이터를 다시 복호화 한다.

5. 서버에서 데이터 전송시 데이터를 개인키로 암호화후 전송한고 클라이언트는 공개키로 복호화한다.


예를들면 aaa.com  에 적용된 ssl 인증서 정보로 wss://아이피:포트 웹소켓서버에 ssl을 동일 하게 적용해도 인증업체에  도메인 등록이 되어있지 않으면 통신이 불가능 하다. 따라서 인증업체에 도메인 추가를 한후 다시 인증서를 받아서 처리하거나(그다지 추천하는 방식은아님) ssl 터널링 기술을 이용해야한다. 


 

 

 

 

=======================

=======================

=======================

 

 

 

 

 

 

출처: http://leelsm.tistory.com/2

 

1. 프로세스
     (1) 서버가 실행되면서 websocket 준비
     - setAllowedOrigins("*") 을 작성해야 wss 통신 가능
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
      registry.addHandler(videoDataHandler(), "/livevideo").setAllowedOrigins("*");
}
 
     (2) 호출 ws (or) wss://domain:port/livevideo
var wsketSrc = "";
if(protocol == "https:") {
    wsketSrc = 'wss://' + domain + '8443/livevideo?DeviceId=' + device_id + '&Channel=' + channel;
} else {
    wsketSrc = 'ws://' + domain + ':'+ port + '/livevideo?DeviceId=' + device_id + '&Channel=' + channel;
}
wsInfo.ws = new WebSocket(wsketSrc);
 
     (3) afterConnectionEstablished 메소드 실행 http로 api 호출
VideoStream stream = streamMap.get(channel);
if (dvr.getNetwork_connected()) {
    if (stream == null) {
        stream = new VideoStream();
        streamMap.put(channel, stream);
        String cmdString = "acms://livevideo.cgi?Uri=http://"+dvr.getAcms_host()+"/dvr/livestream/web&DeviceId="
                + device_id + "&Channel=" + channel+"&Audio=0\r\n\r\n";
        dvr.pushCmd(cmdString);
        System.out.println("###cmdString### : "+ cmdString);
    }
}else{
    session.close(CloseStatus.PROTOCOL_ERROR);
    return;
}
VideoConsumer dispatcher = stream.getVideoConsumer();
dispatcher.addSession(session.getId(), session);
 
 
2. apache 세팅
     (1) apache2 설정
     - /etc/apache2/ports.conf
Listen 80
Listen 8443
 
<IfModule ssl_module>
        Listen 443
</IfModule>
 
<IfModule mod_gnutls.c>
        Listen 443
</IfModule>
 
 
     - /etc/apache2/sites-enabled/default-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
        ServerAdmin webmaster@localhost
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html/
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
        ProxyPreserveHost On
        # Servers to proxy the connection, or
        # List of application servers Usage
        ProxyPass / http://localhost:8080/
        ProxyPassReverse / http://localhost:8080/
        ServerName localhost
 
        SSLEngine on
        SSLCertificateFile      /etc/ssl/certs/acms.bft-ltd.co.uk.crt
        SSLCertificateKeyFile /etc/ssl/private/acms.bft-ltd.co.uk.key
        SSLCertificateChainFile /etc/ssl/certs/intermediate.crt
</VirtualHost>
<VirtualHost _default_:8443>
        ServerAdmin webmaster@localhost
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html/
        ErrorLog ${APACHE_LOG_DIR}/error_ws.log
        CustomLog ${APACHE_LOG_DIR}/access.ws.log combined
        ProxyPreserveHost On
        # Servers to proxy the connection, or
        # List of application servers Usage
        ProxyPass / ws://localhost:8080/
        ProxyPassReverse / ws://localhost:8080/
        ServerName localhost
 
        SSLEngine on
        SSLCertificateFile      /etc/ssl/certs/acms.bft-ltd.co.uk.crt
        SSLCertificateKeyFile /etc/ssl/private/acms.bft-ltd.co.uk.key
        SSLCertificateChainFile /etc/ssl/certs/intermediate.crt
 
</VirtualHost>
</IfModule>
 
     (2) 80, 8443 포트포워딩
 

 

 

반응형

 

728x90

 

 

 

=======================

=======================

=======================

 

 

 

 

 

 

출처: https://xetown.com/qna/780401

 

 

안녕하세요. 개인적으로 프로젝트 때문에

react를 이용한 SPA를 제작중인데, SSL을 이용해서 react 웹앱을 https 환경에서 제공해주려고 바꿨습니다.

 

여기서 Mix content 문제나 소켓 연결이 안되는

많은 문제가 발생하는데요..

 

api 요청도 'http://api서버주소/...' 로 이루어져 있고, 웹소켓도 'ws://소켓서버주소/...' 로 이루어져 있는데

 

서버도 https 환경으로 바꾸고 단순히 이것들을 'https://api서버주소/...' 와 'wss://소켓서버주소/...' 로 바꾸는게 해결 방법인가요?

 

즉, [클라이언트]https -> [서버]http 문제를 해결 하려면
서버도 https로 바꿔서 https -> https 로 해야 하는건가요? (그다음 'https://api서버주소/...' 로 요청하겠지요..?)

아니면 서버단은 http 환경으로 그대로 두고 https로 온 요청을 따로 받아서 잘 처리가 가능한가요?

 

아직 모르는게 많아서 초보 같은 질문이지만
답변 주시면 감사하겠습니다 __

 

(추가로 nginx를 이용해서 https를 적용하여 웹서버를 돌리고 있습니다)

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 메인 웹페이지 자체가 https라면 모든 api 요청, 이미지 로딩, 웹소켓 등에도 동일하게 SSL이 적용되어야 합니다. 웹소켓은 wss, 다른 모든 요청은 https가 되겠지요.

     

    https를 사용하는 이유가 세션 쿠키나 api 토큰 등 민감한 정보가 노출되지 않도록 하기 위해서인데, http를 사용하는 api 요청이나 웹소켓이 단 한 개라도 있다면 거기에 접속할 때 세션 쿠키나 토큰이 노출될 테니 https를 쓰는 의미가 없어집니다. 그래서 최근 브라우저들은 mix content 발생시 이미지 외에는 아예 접속 시도조차 하지 않습니다. (원칙적으로는 이미지도 로딩하면 안되지만, 다 깨져보인다고 사용자들이 너무 싫어해서 타협한 것 같습니다.)

     
  • profile
    기진곰 2017.10.31 01:47:07

    nginx에 이미 https를 세팅해 두셨을 테니 무조건 거기로 접속하시면 됩니다. 웹소켓도 https로 일단 nginx에 접속한 후 /socket.io/ 등 일부 경로만 proxy_pass로 실제 포트에 넘겨주도록 설정하면 인증서 하나로 일괄 적용 가능하고, 별도의 포트를 외부에 노출시킬 필요가 없으니 웹소켓 주소도 훨씬 깔끔해집니다.

     
  • ?
    hizz 2017.10.31 01:52:09

    답변 감사합니다!
    궁금한게 하나 더 있는데요, 웹앱을 제공해주는 클라이언트 서버에는 nginx에 https를 적용 해 둔 상태인데, api, socket 통신을 제공하는 백엔드 서버에는 아직 적용을 안한 상태입니다.
    (즉, 아직 https -> http 로 api, socket이 mixed content 오류로 request가 불가능한 상태입니다)

    저는 똑같이 api, 웹소켓에도 동일하게 SSL이 적용되어야 한다는 말씀을
    백엔드 서버에도 nginx를 설치하고 인증서 발급 및 https 등록 (letsencrypt라는 무료 CA를 사용하였습니다), 그리고 nginx 설정 파일에서 proxy_pass 설정을 하면 된다는 것으로 이해 했는데 이것이 맞나요?
    (백엔드에도 똑같이 https를 적용해서 https -> https가 가능케 하도록)

    그리고 인증서 하나로 일괄 적용이라는 부분은 이해가 가지 않습니다 ㅠ
    클라이언트 서버에 발급했던 인증서를 백엔드 서버에서 https 적용 할 때 동일한 인증서로 재사용이 가능하다는 말씀이신가요?

     
  • profile
    기진곰 2017.10.31 02:15:47

    "클라이언트 서버"라는 명칭이 좀 이상하긴 합니다만
    아무튼 현재 nginx에 letsencrypt 인증서를 사용해서 https를 구현해놓은 서버가 한대 있는 거죠?
    그러면 그 서버에서 모든 요청을 https나 wss로 받아서 백엔드 서버로 넘겨주면 됩니다.

    브라우저 또는 어플 -----(https, wss)---→ 앞단 서버 -----(http, ws)---→ 백엔드 서버
                                                                          ↑                                              ↑
                                                              nginx, ssl 인증서                      api, 소켓(node.js)

    백엔드 서버는 전혀 건드릴 필요가 없습니다. 인증서 두 군데에서 관리하려면 귀찮잖아요.

    브라우저나 어플에서는 백엔드 서버의 존재 자체를 알 필요가 없도록 구성하세요.

     

    nginx proxy_pass websocket이라고 검색해 보시면 예제가 많이 나올 거예요.

     
  • ?
    hizz 2017.10.31 02:27:15

    헉! 감사합니다 클라이언트 서버는 제가 잘못 말씀드린 것 같네요
    react app을 포함한 bundle.js를 index.html과 같이 제공해주기 위해서 따로 node.js를 돌리고 있는 서버를 뜻하는 거였습니다.

    이런 웹앱(bundle.js)을 제공해주는 도메인을 A.com 이라고 하면 api는 B.com/api/... 로 구성되어 있는데요
    api request를 https://A.com/ 인 ssl이 적용된 클라이언트단에서 http://B.com/api/... 인 서버로 요청하고 있는 상태입니다.

    https, wss 요청을 받아서 http, ws로 요청하도록 하게 하는 작업(proxy)을 해줘야 한다는것으로 이해했습니다만
    nginx가 설치된 서버(https://A.com)에 proxy_pass 설정을 하면 B주소로 api request를 할 때 그러한 처리를 자동으로 해준다는 것이 제대로 이해 한 것인가요?

     
  • profile
    기진곰 2017.10.31 02:43:13

    네, 웹앱을 제공하고 계신 (SSL 인증서가 설치되어 있는) A.com 서버가 "앞단"입니다.
    브라우저 또는 어플에서는 백엔드 주소로 직접 요청하지 않는다는 것이 핵심이고요.

    백엔드 주소는 SSL을 지원하지 않거나, 만약 지원하더라도 nginx에서 사용하는 인증서를
    node.js에 갖다 붙이고 갱신할 때마다 매번 재설정하려면 매우 귀찮아지기 때문에
    브라우저 또는 어플과의 모든 통신은 nginx가 전담하는 구조로 바꾸셔야 합니다.

    제가 말씀드린 것은 A.com과 B.com을 모두 "앞단" 서버로 연결한 후
    앞단 서버에서 백엔드 서버로 proxy_pass해주는 방법인데요.
    브라우저 또는 어플과 앞단 서버 사이에서 사용하는 프로토콜(https, wss)과
    앞단 서버와 백엔드 서버 사이에서 사용하는 프로토콜(http, ws)은
    전혀 달라도 무방합니다. proxy_pass 설정만 잘 하시면 됩니다.

    단, 위의 답변은 도메인이 같을 거라고 추정해서 말씀드렸던 거고요,
    도메인이 다르다면 B.com 서버에 nginx를 설치하고 SSL 인증서를 구해서
    위와 같은 방법으로 proxy_pass로 구현하셔도 무방합니다.
    같은 서버에 nginx가 있으면 다른 서버로 proxy_pass하는 것보다는 성능이 좋아지니까요.

    물론 이 경우에도 nginx를 거치면서 프로토콜이 변경되는 것은 마찬가지고요.

    양쪽 중 어떤 방법을 쓰더라도 일단 nginx가 요청을 받아서 넘겨준다는 원리는 동일합니다.

     
  • ?
    hizz 2017.10.31 02:49:17

    앞단 서버와 백엔드 서버는 도메인이 달라서
    후자의 방법으로 해야 할 것 같은데, 백엔드 단에도 https 설정을 한다면 (B.com 이 api 요청하는 백엔드 서버입니다)

    그때는 https -> https 통신이라 proxy 설정이 필요없는 것이 아닌가요?

     

    늦은 새벽에 답변해주시는데 계속 질문해서 많이 죄송하네요..

     
  • profile
    기진곰 2017.10.31 03:05:18

    어느 서버에 무엇이 설치되어 있는지 저도 헷갈려서 정확히 말씀드리기 곤란합니다만...

    만약 B.com에서 api 및 웹소켓 구동을 위해 사용하시는 것이 node.js라면
    https/wss를 http/ws로 바꿔야 하니 nginx를 거쳐서 proxy하는 것이 좋겠고요.
    반면, 만약 이미 nginx를 거치도록 되어 있다면 거기에 https만 설치하시면 되겠습니다.

     
  • ?
    hizz 2017.10.31 03:12:01

    넵. 앞단 A에는 node.js 사용으로 웹앱만 제공하도록 했고
    백엔드 서버라고 말한 B에는 rest api와 소켓 구현이 되어있고 동일하게 node.js를 사용하였습니다. (따로 거쳐서 가는 것이 아니라 B 내부에서 db를 조회한다던가, socket emit을 한다던가.. 등이 구현이 모두 되어있습니다)
    제가 그래서 구조가 좀 이해가 안갔던 것 같네요..

    이 경우에는 B에 https 환경을 세팅하면 A로 부터 B로 바로 https -> https 요청이 가능하기 때문에 프록시 설정이 필요 없지 않냐는 질문이었습니다..!

    아니면 node.js 환경에서 사용하려면 https/wss로 들어온 요청을 http/ws로 변경을 해주어야 하는 것인가요?

     
  • profile
    기진곰 2017.10.31 13:09:25
    node.js에서 https와 wss를 직접 처리할 수도 있습니다만, 권장하지는 않습니다.
    게다가 2개의 어플리케이션이 돌아가고 있다면 (rest api + websocket)
    두 군데 따로 인증서를 설정해 줘야 할 가능성이 높기 때문에
    그냥 nginx를 앞에다 붙이고 proxy해주는 것이 유지보수하기 편합니다.
    성능에는 거의 영향이 없고, 보안면에서도 이쪽이 유리하고요.
     
  • ?
    hizz 2017.10.31 14:04:36
    감사합니다..! 이해가 좀 부족한 상태에서
    너무 많은 질문을 한 것 같은데
    이렇게 상세한 답변을 계속 받으니 너무 감사하네요ㅠㅠ
    도움 많이 되었습니다. 좋은 하루 되세요 (_ _)

 

 

 

 

=======================

=======================

=======================

 

 

 

 

 

반응형