SSL 인증서와 인증 방식

Network 2020. 6. 4. 14:08 Posted by 김한별 behonestar

댓글을 달아 주세요

tcp_tw_reuse와 SO_REUSEADDR의 차이

Network 2017. 2. 8. 14:30 Posted by 김한별 behonestar

tcp_tw_reuse

클라이언트에서 서버로 connect()하는 과정에서 커널은 내부적으로 가용 포트를 로컬 포트로 선정합니다. 이 때 tcp_tw_reuse 옵션이 활성화되어 있으면 TIME_WAIT 상태의 포트도 로컬포트로 선정될 수 있습니다. (일반적인 경우를 가정하여 bind()를 수행하지 않고 connect()하는 쪽을 클라이언트라고 지칭하였습니다.)


SO_REUSEADDR

서버에서 TIME_WAIT 상태의 특정 포트에 bind()하려면 소켓에 SO_REUSEADDR 옵션을 적용해야 합니다. bind()를 사용한 경우 해당 로컬 포트는 tcp_tw_reuse 설정이 적용되지 않습니다.


// 소켓 생성

sockSvr = socket(AF_INET, SOCK_STREAM, 0);

addrSockSvr.sin_family = AF_INET;

addrSockSvr.sin_port = htons(333);

addrSockSvr.sin_addr.S_un.S_addr = INADDR_ANY;


// 소켓 옵션 설정

setsockopt(sockSvr, SOL_SOCKET, SO_REUSEADDR, (const char *)&bValid, sizeof(bValid));          


bind(sockSvr, (struct sockaddr *)&addrSockSvr, sizeof(addrSockSvr));



출처


댓글을 달아 주세요

OAuth2.0

Network 2017. 1. 19. 16:48 Posted by 김한별 behonestar

사전 작업

인증을 제공하는 서버에 App을 등록해야 한다.

- domain을 입력해야 한다. (redirect_uri를 검증할 때 domain 포함 여부를 확인한다.)

- client id(app_id)가 생성된다.

- client secret(app_secret)이 생성된다. (외부에 노출되면 안되는 키이다.)


인증 유형

1. 서버에서 인증 (JSP, PHP, ...)

웹서버는 app_secret키을 사용하여 인증서버로부터 인증을 받는다.

웹서버가 들고있는 app_secret값은 외부에서 사용자가 알 수 없다.


<인증 단계>

① 유저 ID, PW를 아는 사람만 code 획득이 가능하다.

② app_secret을 아는 서버만 access_token 획득이 가능하다.





2. 클라이언트에서 인증 (Javascript, Desktop app, Mobile app)

클라이언트에서 모든 처리가 이루어지는 경우 사용한다.

인증과정에서 app_secret을 요구하지 않는다. (디컴파일이 가능하기 때문)


<인증 단계>

① 유저 ID, PW를 아는 사람만 access_token 획득이 가능하다.




 

3. Password 인증

인증서버가 App이 속한 서비스의 구성원일 때 사용한다.

ID, PW를 App이 직접 전달 받아서 인증서버로부터 인증을 받는다.

AWS Cognito User Pool에 대해 USER_SRP_AUTH 방식으로 인증할 때 이 방식을 사용한다.


<인증 단계>

① 유저 ID, PW를 아는 사람만 access_token 획득이 가능하다.




더 알아볼 것

- Refresh Token

- Long-lived Token



참고


댓글을 달아 주세요

HTTP Digest 인증과 토큰 기반 인증

Network 2015. 5. 10. 01:28 Posted by 김한별 behonestar

HTTP Digest 인증



출처 : David Gourley and Brian Totty, "HTTP: The Definitive Guide (Definitive Guides)", O'Reilly, 2002


특징

  1. HTTP header 내에 유저네임과 비밀번호를 암호화하여 전달하는 방식
  2. nonce를 사용하기 때문에 인증 체결을 가로채서 재현하지 못함
  3. 구현 방법에 따라 메시지 내용 위조 방지 가능


단점

  1. 해쉬 알고리즘으로 MD5를 사용하기 때문에 보안레벨이 낮음
    1. MD5 Dictonary 사이트
      1. MD5 해쉬값 입력 : 939e7578ed9e3c518a452acee763bce9
      2. 복호화된 해쉬값 : Mufasa:testrealm@host.com:Circle Of Life

  2. CSRF 공격에 취약
    1. 계정별 API 실행 권한을 설정하여 보완

  3. 로드 밸런싱 등의 목적으로 여러 대의 서버가 존재할 경우 세션 관리 기법 필요
    1. Sticky Session 사용 (AWS의 ELB가 지원)
    2. 세션 클러스팅 사용



토큰 기반 인증



출처 : 조대협의 블로그, http://bcho.tistory.com/887



특징

  1. 최초 1회만 계정 정보를 전송하고 이후에는 서버가 생성한 토큰을 사용하는 방식
    1. Digest 인증 사용하거나 더 강화된 Hash 알고리즘을 사용하여 비밀번호 관리
  2. 토큰이 노출되더라도 계정 정보는 보호할 수 있음
  3. OAuth 등의 인증 방식에서 사용


단점

  1. 제 3자가 토큰을 탈취하여 API 실행 가능 (세션 하이재킹)
    1. 토큰 만료 시간, 토큰에 매칭되는 IP 검사 등을 통해 보완
    2. 계정별 API 실행 권한 설정하여 보완
    3. Secure Cookie(Signed Cookie)로 토큰 전달하여 노큰 노출 방지
      1. 토큰을 Response Body가 아닌 Secure Cookie에 넣어서 전달
      2. Secure Cookie는 HTTPS를 통해서만 전송 가능
      3. Cookie에 HTTP ONLY 옵션을 추가하면 스크립트단에서 Cookie를 읽거나 조작할 수 없음
        Cookie는 Request 시 웹브라우저에 의해 자동 전송됨

  2. 로드 밸런싱 등의 목적으로 여러 대의 서버가 존재할 경우 토큰 관리 기법 필요




참고자료

  1. REST API의 이해와 설계-#3 API 보안
  2. 빠르게 훝어 보는 node.js - #4 웹개발 프레임웍 Express 1/2
  3. 보안: 크로스 사이트 요청 위조(Cross-Site Request Forgery) 공격 방지하기
  4. 오픈소스 데이터그리드를 이용한 서로 다른 WAS 간의 세션 클러스터링


댓글을 달아 주세요

[TCP] window size full

Network 2015. 3. 18. 11:20 Posted by 김한별 behonestar

TCP receive window size?

수신자가 한 번에 버퍼링할 수 있는 최대 데이터 크기(bytes)를 의미합니다. 

송신자는 수신자의 window size만큼 ACK를 기다리지 않고 데이터를 전송할 수 있습니다.


Window Size Full?
수신자측에서 Read()를 수행하지 않아 수신자쪽 소켓 버퍼에 데이터가 쌓이게 되면 수신자의 window size는 점차적으로 감소하게 됩니다.

수신자측의 버퍼가 가득차면 송신자에게 window size full을 알려서 데이터를 더 이상 전송하지 못하도록 합니다.

즉, window size full은 아직 Read() 하지 않는 데이터가 수신자측 버퍼에 가득함을 의미합니다.


참조

  1. why would tcp full window happen
  2. TCP 세션 당 이론적인 max throughput 계산
  3. TCP 윈도우 사이즈 감소 문제
  4. 윈도 사이즈와 소켓 버퍼


댓글을 달아 주세요

증상
HTTPS 사용 시 ONVIF 응답 지연 발생


원인

[gsoap] gsoap은 하나의 Response를 위해 여러 번의 send를 한다.

soap_send_raw(soap, "<", 1);
soap_send_raw(soap, "SOAP-ENV:Envelope", ...);
soap_send_raw(soap, ">", 1);
...


[lighttpd] gsoap으로부터 recv한 패킷들을 각각 SSL_write → 오버헤드로 인한 전송 지연

[963760976.648370]: ssl write begin
[963760976.649320]: SSL_write
[963760976.650925]: SSL_write
[963760976.652421]: SSL_write
[963760976.653860]: SSL_write
[963760976.655395]: SSL_write
[963760976.657133]: SSL_write
......
[963760977.318255]: SSL_write
[963760977.320038]: SSL_write
[963760977.321517]: SSL_write
[963760977.324003]: ssl write end // (676ms 소요)


대책

1. gsoap 초기화 시 SOAP_IO_BUFFER 옵션 적용

-# define SOAP_IO_DEFAULT               (SOAP_IO_FLUSH | SOAP_XML_DEFAULTNS)

+# define SOAP_IO_DEFAULT               (SOAP_IO_BUFFER | SOAP_XML_DEFAULTNS)


2. [gsoap] Response 문자열을 버퍼링하여 한 번에 send

/* stdsoap2.c */

...

#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
#ifdef KHB_FEATURE_SOAP_BUFFER
soap_send_raw_(struct soap *soap, const char *s, size_t n)
#else
soap_send_raw(struct soap *soap, const char *s, size_t n)
#endif
{ if (!n)
    return SOAP_OK;
#ifndef WITH_LEANER
  if (soap->fpreparesend && (soap->mode & SOAP_IO) != SOAP_IO_STORE && (soap->mode & SOAP_IO_LENGTH) && (soap->error = soap->fpreparesend(soap, s, n)))
    return soap->error;
  if (soap->ffiltersend && (soap->error = soap->ffiltersend(soap, &s, &n)))
    return soap->error;
#endif
  if (soap->mode & SOAP_IO_LENGTH)
    soap->count += n;
  else if (soap->mode & SOAP_IO)
  { register size_t i = SOAP_BUFLEN - soap->bufidx;
    while (n >= i)
    { memcpy(soap->buf + soap->bufidx, s, i);
      soap->bufidx = SOAP_BUFLEN;
      if (soap_flush(soap))
        return soap->error;
      s += i;
      n -= i;
      i = SOAP_BUFLEN;
    }
    memcpy(soap->buf + soap->bufidx, s, n);
    soap->bufidx += n;
  }
  else
    return soap_flush_raw(soap, s, n);
  return SOAP_OK;
}

#ifdef KHB_FEATURE_SOAP_BUFFER
struct _gsoap
{ char buf[SOAP_BUFLEN];
  char *pbuf;
  size_t bufidx;
};
static struct _gsoap gsoap;

SOAP_FMAC1
int
SOAP_FMAC2
soap_send_final(struct soap *soap)
{
  int r = soap_send_raw_(soap, gsoap.buf, gsoap.bufidx);
  memset(&gsoap, 0, sizeof(gsoap));
  gsoap.pbuf = gsoap.buf;
  return r;
}

SOAP_FMAC1
int
SOAP_FMAC2
soap_send_raw(struct soap *soap, const char *s, size_t n)
{ static int init = 0;
  if (!init)
  { memset(&gsoap, 0, sizeof(gsoap));
    gsoap.pbuf = gsoap.buf;
    init = 1;
  }
  if (!n)
    return SOAP_OK;
  if(gsoap.bufidx + n >= SOAP_BUFLEN)
  { int r = soap_send_final(soap);
    if(r != SOAP_OK)
    return r;
  }   
  memcpy(gsoap.pbuf, s, n);
  gsoap.pbuf += n;
  gsoap.bufidx += n;
  return SOAP_OK;
}
#endif
#endif


...

#ifndef PALM_2
SOAP_FMAC1
int
SOAP_FMAC2
soap_envelope_end_out(struct soap *soap)
{ if (soap_element_end_out(soap, "SOAP-ENV:Envelope")
   || soap_send_raw(soap, "\r\n", 2))    /* 2.8: always emit \r\n */
    return soap->error;
#ifdef KHB_FEATURE_SOAP_BUFFER
  if (soap_send_final(soap))
    return soap->error;  
#endif
#ifndef WITH_LEANER
  if ((soap->mode & SOAP_IO_LENGTH) && (soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
  { soap->dime.size = soap->count - soap->dime.size;    /* DIME in MIME correction */
    sprintf(soap->id, soap->dime_id_format, 0);
    soap->dime.id = soap->id;
    if (soap->local_namespaces)
    { if (soap->local_namespaces[0].out)
        soap->dime.type = (char*)soap->local_namespaces[0].out;
      else
        soap->dime.type = (char*)soap->local_namespaces[0].ns;
    }
    soap->dime.options = NULL;
    soap->dime.flags = SOAP_DIME_MB | SOAP_DIME_ABSURI;
    if (!soap->dime.first)
      soap->dime.flags |= SOAP_DIME_ME;
    soap->count += 12 + ((strlen(soap->dime.id)+3)&(~3)) + (soap->dime.type ? ((strlen(soap->dime.type)+3)&(~3)) : 0);
  }
  if ((soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM))
    return soap_send_raw(soap, SOAP_STR_PADDING, -(long)soap->dime.size&3);
#endif
  soap->part = SOAP_END_ENVELOPE;
  return SOAP_OK;
}
#endif


결과

[963786468.956911]: ssl write begin
[963786468.957803]: SSL_write // contents-length를 위한 Response
[963786468.964595]: SSL_write // xml response
[963786468.969947]: ssl write end // (13ms 소요)


댓글을 달아 주세요

[gsoap] recv, send 함수 흐름

Network 2015. 2. 27. 16:01 Posted by 김한별 behonestar

Request

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:wsdl="http://www.onvif.org/ver10/device/wsdl">
   <soap:Header/>
   <soap:Body>
      <wsdl:GetNetworkProtocols/>
   </soap:Body>
</soap:Envelope>


Response

<SOAP-ENV:Envelope xmlns:SOAP-ENV="...">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <tds:GetNetworkProtocolsResponse>
         <tds:NetworkProtocols>
            <tt:Name>RTSP</tt:Name>
            <tt:Enabled>false</tt:Enabled>
            <tt:Port>5555</tt:Port>
         </tds:NetworkProtocols>
         <tds:NetworkProtocols>
            <tt:Name>HTTP</tt:Name>
            <tt:Enabled>false</tt:Enabled>
            <tt:Port>8082</tt:Port>
         </tds:NetworkProtocols>
      </tds:GetNetworkProtocolsResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>


gsoap 함수 흐름

soap_serve

soap_begin_serve

FCGI_Accept()

soap_begin

soap_begin_recv

soap->fparse(soap); // http header 존재하면 파싱

soap_envelope_begin_in <soap:Envelope ...

soap_recv_header <soap:Header ...

soap_body_begin_in <soap:Body ...


soap_serve_request

soap_serve___tev2__CreatePullPointSubscription

soap_body_end_in </soap:Body>

soap_envelope_end_in </soap:Envelope>

soap_end_recv


soap_begin_send

soap_envelope_begin_out <SOAP-ENV:Envelope

soap_putheader <SOAP-ENV:Header/>

soap_body_begin_out <SOAP-ENV:Body...

soap_put__tev__CreatePullPointSubscriptionResponse

soap_body_end_out </SOAP-ENV:Body>

soap_envelope_end_out </SOAP-ENV:Envelope>

soap_end_send

soap_destroy

soap_end


댓글을 달아 주세요