Apache Http 서버의 전체 connection pool 개수가 Tomcat Instance connector thread 의 개수를 초과해서는 안된다. 초과시, 일부 요청은 새로 ajp connection을 연결을 시도하기 위해서 hang 상태에 걸릴 수 있다.

전체 Tomcat Instance의 connection thread 개수는 다음과 같아야 한다.

• Apache Http 서버가 prefork 방식일 때의 connection pool은 MaxClients을 의미한다.

• Apache Http 서버가 worker 방식일 때의 connection pool은 (MaxClients / ThreadsPerChild) * connection _pool_size 을 의미한다.

Tomcat Instance 의 Connecotr가 AJP 이기 때문에 connection의 개수는 thread의 개수로 이어지기 때문에 maxThreads 값과 연관되어 진다. 성능 테스트를 통해서 적절한 TPS 값을 찾고 이에 맞게 maxThreads 숫자를 찾아야 한다. 또한 load balance 되는 worker들의 maxThreads 값의 합을 통해서 Apache Http 서버의 MaxClients 값을 산출해야 한다.

prefork방식일 때는 아래와 같이 대략 맞추는 것이 좋다.

Tomcat maxThreads 값 * load balanced 개수 (연동되는 Tomcat instance 개수) = Apache Http MaxClients * 1.1 (또는 1.2)

1.1 또는 1.2 의 수를 넣어주는 것은 mod_jk의 load balance에 대한 조정(variation) 값이다.

Posted by '김용환'
,

 

<timeout 설정>

하나의 tomcat worker에 전달되어 서비스가 되도록 한다.

 

worker.list=tomcat

worker.tomcat.type=ajp13

worker.tomcat.port={portnumber}

#worker.tomcat.connect_timeout=1000

#worker.tomcat.prepost _timeout=1000

#worker.tomcat.socket_timeout=3

#worker.tomcat.reply_timeout=1000

 

Tomcat Instance의 GC time, 통신 속도, 트래픽을 고려해서 개발자가 mod_jk의 timeout설정을 해야 한다.

아래 Timeout 설정에 하나라도 만족이 되면, mod_jk는 해당 Tomcat Instance에 대해서 에러로 처리되고, 특정기간(recover_time : 60초)동안 소켓을 전달하지 않는다.

실제 정확한 Timeout은 연속 시도 값(reties 속성값) 과 연속 시도 대기 값(retry_interval 속성값)에 따라서 영향을 받는다.

현재 reties default 값은 2, retry_interval 값은 100ms이므로, 실제 Timeout 값은 아래와 같다.

실제 Timeout은 (2 * xxx_timeout ) + 100ms이다.

위 설정의 의미를 설명한다.

* worker.list=tomcat : worker는 tomcat worker 하나만 지정한다.

* worker.tomcat.type=ajp13 : 로 Apache Http 서버와 Tomcat 서버가 통신하는 protocol은 ajp 13이다.

* worker.tomcat.connect_timeout=1000 : jk와 Tomcat Instance 간 연결이 완료된 후, arj13 protocol에서의 cping request에 대한 cpong respone timeout 을 1000ms로 지정한다.

* worker.tomcat.prepost _timeout=1000 : jk와 Tomcat Instance 간의 요청이 전달이 되기전 arj13 protocol에서의 cping request에 대한 cpong respone timeout 을 1000ms로 지정한다.

* worker.tomcat.socket_timeout=3 : jk와 Tomcat Instance간의 응답 대기 시간이 3초 되면, timeout이 된다. 이 응답대기 시간은 TCP socket 내부적인 상태에 대한 timeout을 의미한다.

* worker.tomcat.reply_timeout= 1000 : jk와 Tomcat Instance 간의 의미있는 요청과 응답간의 시간에 대해서 1000ms timeout으로 지정된다.

내부 IDC에서는 방화벽을 쓰지 않기 때문에, socket_keepalive(디폴트 false)설정은 따로 지정하지 않는다.

connection_pool_timeout을 지정할 경우, Tomcat Instance에서의 connectionTimeout 설정과 같이 설정해야 한다.

 

<status 보기>

Apache Http 서버에서 status 정보를 볼 수 있듯이, mod_jk에서도 status 정보를 보여줄 수 있다. 이 설정은 서비스에는 영향을 주지 않는다.

workers.properties 파일 안에 Status worker를 추가한다.

worker.list=jkstatus

worker.jkstatus.type=status

conf 설정 파일에 jk mount 디렉토리를 지정하고, 외부로 노출되지 않도록 IP관리가 되어야 한다.

JkMount /jkmanager/* jkstatus

<Location /jkmanager/>

JkMount jkstatus

Order deny,allow

Deny from all

Allow from 127.0.0.1

Allow from <내부 IP>

</Location>

http://127.0.0.1/jkmanager/ 로 접속하면 관련정보를 확인할 수 있다.

 

<fail on status>

 

worker.{application1}.fail_on_status=400

Tomcat Instance에서 보낸 Http response status code에 대해서 fail로 간주할 수 있도록 한다. Tomcat Instance에서 400 error발생시 fail이 동작될 수 있다.

 

<redirect>

 

worker.{application1}.redirect ={component6}

worker.{application6}.port=8050

worker.{application6}.host=2.2.2.2

worker.{application6}.type=ajp13

worker.{application6}.activation=disable

특정 상황에서 application1 worker가 application6 worker로 redirect가 가능하다.
activation 설정은 1.2.19부터 가능하다.

worker.properties 파일에 connection_pool_timeout 설정을 추가하여, fail 이후의 상황에 대해서 필요 없는 connection을 오랫동안 쓰지 않도록 할 수 있다. Apache Tomcat 서버의 server.xml의 connectionTimeout값과 동일하게 잡아야 한다.

worker.tomcat1.port=8050

worker.tomcat1.connection_pool_timeout=5

Posted by '김용환'
,

mod_jk 샘플/설명

Web service 2012. 1. 17. 18:33


<간단 설명> 

Apache Http의 설정 파일이다.

LoadModule jk_module modules/mod_jk.so

<IfModule mod_jk.c>

JkWorkersFile "/home/www/httpd/conf/workers.properties"

JkLogFile "| /home/www/httpd/bin/rotatelogs –l /home/www/httpd/logs/mod_jk.log. %y%m%d 86400 "

JkLogLevel error

JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

#JkRequestLogFormat "%w %V %T"

</IfModule>

mod_jk 로그도 full 되어서 write를 못하면 apache httpd 서버가 동작이 안될 수 있으니 rotatelogs를 쓰는 것이 좋다.

 

필요시에는 JkLogLevel 을 error에서 debug 혹은 info 레벨로 낮추어서 테스트할 수 있다. 디폴트 값은 error이다.

하루 단위로 mod_jk 로그가 생성되도록 하였다. 옵션에 대한 상세한 mod_jk.log 설정은 다음과 같다.

JkLogFile에 적힌 옵션들과 숫자들의 의미이다.

• %y : 연도 (year)

• %m : 월 (month)

• %d : 일 (day)

• 86400 : 하루를 초로 나타난 시간 (60 * 24 * 24)

JkLogLevel 의 경우는 default로 error로 초기화 되나, 필요에 따라서 info나 debug로 설정한다.

JkLogStampFormat 설명은 다음과 같다.

• %a : 요일

• %b : 월 (month)

• %d : 일 (day)

• %H:%M:%S %Y] : 시간정보 와 연도 (year)

JkRequestLogFormat 설명은 다음과 같다.

• %w : Tomcat worker의 이름

• %V : 요청한 Domain 이름

• %T : 처리 시간 (소수점 초 단위)

JkRequestLogFormat을 정의하면, Apache Http 서버에서 Apache Tomcat 서버로 Http요청으로 넘어갈 때, 로그가 저장이 된다.

[Fri Dec 03 14:47:19 2010] solution_search search.nhncorp.com 0.017900
[Fri Dec 03 14:47:20 2010] solution_search search.nhncorp.com 0.020706

따라서, Error 가 발생할 때만, 로그가 저장되길 원한다면, JkRequestLogFormat을 사용하지 말아야 한다. 그 이유는 내부적으로 log level을 JK_LOG_REQUEST_LEVEL로 지정하고 있는데, JkLogLevel과는 상관없이 출력하도록 코딩이 되어 있다.

WAS에서 Flush 되지 않거나 하는 문제가 발생시 다음의 옵션을 사용하여 Flush 되게 할 수 있다.

JkOptions +FlushPackets +FlushHeader

예를 들어, chunked encoding 으로 gzip을 전송시 바로 flush가 안될 수 있는데, 위의 옵션을 추가하면 바로 flush 되는 것을 확인할 수 있다. (Http server 2.2.8부터 가능)

만약 동작이 되지 않는다면, JkLogLevel 을 trace로 내리면 구체적인 로그를 확인할 수 있다. 로그 레벨의 디폴트 값은 info이며, 레빌의 수준은 trace, debug, info, warn, error 단계로 나눌 수 있다.


<상세 로그 보기>



Apache Http서버는 mod_jk 관련 로그를 상세하게 출력이 가능하다. Failover시에 대한 정보를 아래와 같은 포맷으로 출력할 수 있다.

LogFormat "%h %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D %{pid}P %{JK_WORKER_NAME}n %{JK_LB_FIRST_NAME}n %{JK_LB_FIRST_STATE}n %{JK_LB_LAST_NAME}n %{JK_LB_LAST_STATE}n %{JK_REQUEST_DURATION}n" mod_jk_log


CustomLog "| /home/www/httpd/bin/rotatelogs -l /home/www/httpd/modjklog.%Y%m%d 86400" mod_jk_log env=!nolog-request
추가된 설정의 의미를 설명한다.
 %{pid}P : prefork 기준 Apache Http process 아이디
 %{JK_WORKER_NAME}n : load balancer worker 이름
 %{JK_LB_FIRST_NAME}n : 처음 시도한 load balancer worker의 이름
 %{JK_LB_FIRST_STATE}n : 처음 시도한 load balancer worker 의 상태
 %{JK_LB_LAST_NAME}n : 마지막으로 Http 요청을 처리한 load balancer worker의 이름
 %{JK_LB_LAST_STATE}n : 마지막으로 Http 요청을 처리한 load balancer worker의 상태
 %{JK_REQUEST_DURATION}n : 처리시간


Posted by '김용환'
,

 

VirtualHost 설정은 순서가 민감하다. 먼저 선언된 것일수록 우선순위가 높다.

아래와 같이 www1.google.com과 www2.google.com이 있다고 가정한다.

<VirtualHost *>

ServerName www1.google.com

DocumentRoot /home/www/deploy/google-www1

</VirtualHost>

<VirtualHost *>

ServerName www2.google.com

DocumentRoot /home/www/deploy/google-www2

</VirtualHost>

웹브라우저에서 www1.google.com 요청이 들어오면, 첫번째 VirtualHost에서 처리하게 된다.

www2.google.com 요청이 들어면, 두번째 VirtualHost에서 처리하게 된다.

www3.google.com 을 요청하면, 어떤 ServerName 지시자에도 해당되지 않은 요청이기 때문에 첫번째 VirtualHost에서 처리하게 되어 www1.google.com에서 처리가 된다.

 

특이하게 쓰는 경우에는 어떻게 될까?

아래 예제와 같이 첫번째 VirtualHost와  두번째 VirtualHost의 ServerName으로 선언된 www2.google.com을 ServerAlias로 지정한다.

<VirtualHost *>

ServerName www1.google.com

ServerAlias www2.google.com

DocumentRoot /home/www/deploy/google-www1

</VirtualHost>

<VirtualHost *>

ServerName www2.google.com

DocumentRoot /home/www/deploy/google-www2

</VirtualHost>

www2.google.com을 요청하면, 우선순위가 높은 첫번째 VirtualHost에서 처리하게 되어 www1.google.com에서 처리가 된다.

Posted by '김용환'
,

 

ListenBackLog 지시자는 성능을 조금 더 높이기 위한 설정으로서, Http 서버가 요청을 받기 위한 Listen 의 큐의 길이를 길게 해준다. 511이 디폴트로 되어 있는데, 웹 서비스의 성격에 따라서 효과를 발휘할 수도 있고, 없을 수도 있다. 또한 TCP Syn Flooding 공격에 유리하다고 알려져 있다.

기본적인 파일을 전달하는 Http 서버를 테스트해본 결과 8192 의 값이 좋게 나왔다. (항상 좋은 결과치를 나오지 않으니 성능 테스트를 통해서 결과치를 확인해야 한다.)

ListenBackLog 8192

이 값과 함께 수정해야 하는 값은 리눅스 커널 파라미터이다.

먼저 다음의 명령어를 사용하여 먼저 파라미터를 수정한다.

#] echo 8192 > /proc/sys/net/core/somaxconn

그리고, 영구적으로 사용하기 위해서 /etc/sysctl.conf 파일을 열어, 아래 필드를 추가 또는 수정한다.

net.core.somaxconn = 8192

다음의 명령어를 실행하여 리눅스 OS에 반영한다.

#] sysctl –p

Posted by '김용환'
,

 

access.log(error.log) 파일을 통해서 간단한 Http 요청이 들어왔는지 확인할 수 있다. 보다 깊숙하게 어떤 정보가 들어왔는지 확인하기 위해서는 mod_dumpio 모듈을 이용해서 확인할 수 있다.

Modules 디렉토리안에 mod_dumpio.so 파일이 존재하지 않으면, 소스디렉토리의 modules/debug/mod_dumpio.c 파일을 설치하면 된다.

${APACHE_HOME}/bin/apxs –aic mod_dumpio.c

Modules디렉토리의 mod_dumpio.so 파일이 생성되고, conf/httpd.conf 파일에 다음 설정이 추가된다.

LoadModule dumpio_module modules/mod_dumpio.so

Conf/httpd.conf 파일에 다음 설정을 추가하고 서버를 재시작한다.

LogLevel debug

DumpIOInput On

Java 의 HttpURLConnection을 이용해서 http://dev-service.google.com/index?type=service&keyword=aaa 을 요청하도록 하였다. Access.log 파일에는 기존과 동일하게 요청결과를 확인할 수 있다.

10.64.83.203 - - [13/Apr/2011:19:57:53 +0900] "POST /index HTTP/1.1" 200 64061

Error.log 파일에는 Http 요청에 대한 정보가 상세하게 기록이 된다.

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 26 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): POST /index HTTP/1.1\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 39 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): Content-type: text/xml; charset=UTF-8\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 25 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): Cache-Control: no-cache\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 18 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): Pragma: no-cache\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 27 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): User-Agent: Java/1.6.0_24\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 30 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): Host: dev-service.google.com\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 62 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 24 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): Connection: keep-alive\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 20 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): Content-Length: 18\r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 2 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): \r\n

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [readbytes-blocking] 18 readbytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_in (data-HEAP): 24 bytes

[Wed Apr 13 19:57:53 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_in (data-HEAP): type=service&keyword=aaa

DumpIOOutput 지시자를 추가하면, response 정보도 확인할 수 있다.

LoadModule dumpio_module modules/mod_dumpio.so

LogLevel debug

DumpIOInput On

DumpIOOutput On

Error.log 파일에 기존 http request 정보와 함께 다음과 같이 response 출력도 확인할 수 있다.

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(142): mod_dumpio: dumpio_out

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_out (data-HEAP): 330 bytes

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_out (data-HEAP): HTTP/1.1 200 OK\r\nDate: Wed, 13 Apr 2011 11:23:38 GMT\r\nServer: Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.7a mod_jk/1.2.31\r\nLast-Modified: Wed, 13 Apr 2011 11:23:31 GMT\r\nAccept-Ranges: bytes\r\nContent-Length: 6\r\nCache-Control: max-age=7192\r\nExpires: Wed, 13 Apr 2011 13:23:31 GMT\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(142): mod_dumpio: dumpio_out

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_out (data-FILE): 6 bytes

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(74): mod_dumpio: dumpio_out (data-MMAP): hello\n

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_out (metadata-EOS): 0 bytes

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(142): mod_dumpio: dumpio_out

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_out (metadata-FLUSH): 0 bytes

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(142): mod_dumpio: dumpio_out

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_out (metadata-FLUSH): 0 bytes

[Wed Apr 13 20:23:38 2011] [debug] mod_dumpio.c(55): mod_dumpio: dumpio_out (metadata-EOC): 0 bytes

Posted by '김용환'
,

 

예전에 만든 자료인데, 기억 못할까봐 정리해둔다.

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

part 1. 10년짜리 사실 인증서 만들기

개발 서버에서는 공인인증서를 꼭 써야 할 필요는 없다. 만약 공인인증서를 쓰지 않았을 때를 위해서 만들어 놓은 환경을 설명한다. 테스트 환경에서만 작동하는 보안 서버를 구축하면 된다.

CA (Certificate Authority, 인증기관)을 구성하고, 인증기관 자체의 private key와 public key를 생성하고, 결과물로 나온 인증서를 보안서버에 올리고, 보안 서버와 통신하는 모든 개발 PC및 개발 장비에서 사용할 수 있는 방법을 설명한다.

Http 서버 설정을 제외하고는 모두 Root 계정을 이용해야 한다.

자료는 아래 링크를 참조하면 된다.

http://www.ipsec-howto.org/x595.html

http://tldp.org/HOWTO/SSL-Certificates-HOWTO/x195.html

 

<파일 수정>

첫번째, CentOS의 openssl.conf 파일을 수정한다.

Redhat 계통(CentOS포함)에서는 /usr/share/ssl/openssl.cnf 설정파일을 참조해서 조금 수정하면 된다. dev개발환경이라는 가정을 하고, 다음 dev-openssl.conf 파일을 생성한다.

dev-ssl.google.com 이라는 도메인에 대한 인증서를 작성한다. 추가적으로 commonName에 alpha-ssl.google.com, beta-ssl.google.com, dev-id.google.com, dev-member.google.com 와 같이 여러 개의 도메인을 한 번에 등록할 수 있다.

 

#

# OpenSSL example configuration file.

# This is mostly being used for generation of certificate requests.

#

# This definition stops the following lines choking if HOME isn't

# defined.

HOME = .

RANDFILE = $ENV::HOME/.rnd

# Extra OBJECT IDENTIFIER info:

#oid_file = $ENV::HOME/.oid

oid_section = new_oids

# To use this configuration file with the "-extfile" option of the

# "openssl x509" utility, name here the section containing the

# X.509v3 extensions to use:

# extensions =

# (Alternatively, use a configuration file that has only

# X.509v3 extensions in its main [= default] section.)

[ new_oids ]

# We can add new OIDs in here for use by 'ca' and 'req'.

# Add a simple OID like this:

# testoid1=1.2.3.4

# Or use config file substitution like this:

# testoid2=${testoid1}.5.6

####################################################################

[ ca ]

default_ca = CA_default # The default ca section

####################################################################

[ CA_default ]

dir = ./demoCA # Where everything is kept

certs = $dir/certs # Where the issued certs are kept

crl_dir = $dir/crl # Where the issued crl are kept

database = $dir/index.txt # database index file.

new_certs_dir = $dir/newcerts # default place for new certs.

certificate = $dir/cacert.pem # The CA certificate

serial = $dir/serial # The current serial number

crl = $dir/crl.pem # The current CRL

private_key = $dir/private/cakey.pem# The private key

RANDFILE = $dir/private/.rand # private random number file

x509_extensions = usr_cert # The extentions to add to the cert

# Comment out the following two lines for the "traditional"

# (and highly broken) format.

name_opt = ca_default # Subject Name options

cert_opt = ca_default # Certificate field options

# Extension copying option: use with caution.

# copy_extensions = copy

# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs

# so this is commented out by default to leave a V1 CRL.

# crl_extensions = crl_ext

default_days = 1825 # how long to certify for

default_crl_days= 30 # how long before next CRL

default_md = md5 # which md to use.

preserve = no # keep passed DN ordering

# A few difference way of specifying how similar the request should look

# For type CA, the listed attributes must be the same, and the optional

# and supplied fields are just that :-)

policy = policy_match

# For the CA policy

[ policy_match ]

countryName = match

stateOrProvinceName = match

organizationName = match

organizationalUnitName = optional

commonName = supplied

emailAddress = optional

# For the 'anything' policy

# At this point in time, you must list all acceptable 'object'

# types.

[ policy_anything ]

countryName = optional

stateOrProvinceName = optional

localityName = optional

organizationName = optional

organizationalUnitName = optional

commonName = supplied

emailAddress = optional

####################################################################

[ req ]

default_bits = 1024

default_keyfile = privkey.pem

distinguished_name = req_distinguished_name

attributes = req_attributes

x509_extensions = v3_ca # The extentions to add to the self signed cert

# Passwords for private keys if not present they will be prompted for

# input_password = secret

# output_password = secret

# This sets a mask for permitted string types. There are several options.

# default: PrintableString, T61String, BMPString.

# pkix : PrintableString, BMPString.

# utf8only: only UTF8Strings.

# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).

# MASK:XXXX a literal mask value.

# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings

# so use this option with caution!

string_mask = nombstr

# req_extensions = v3_req # The extensions to add to a certificate request

[ req_distinguished_name ]

countryName = Country Name (2 letter code)

countryName_default =

countryName_min = 2

countryName_max = 2

stateOrProvinceName = State or Province Name (full name)

stateOrProvinceName_default =

localityName = Locality Name (eg, city)

localityName_default =

0.organizationName = Organization Name (eg, company)

0.organizationName_default =

# we can do this but it is not needed normally :-)

#1.organizationName = Second Organization Name (eg, company)

#1.organizationName_default = World Wide Web Pty Ltd

organizationalUnitName = Organizational Unit Name (eg, section)

organizationalUnitName_default = team

commonName = Common Name (eg, your name or your server\'s hostname)

commonName_default = dev-ssl.google.com

commonName_max = 64

# 만약 하나의 인증서로 다른 서버도 같이 쓸 수 있음. 아래의 예제와 같이 진행하면 됨

#0.commonName = Common Name (eg, your name or your server\'s hostname)

#0.commonName_default = dev-ssl.google.com

#0.commonName_max = 64

#1.commonName = Common Name (eg, your name or your server\'s hostname)

#1.commonName_default = alpha-ssl.google.com

#1.commonName_max = 64

#2.commonName = Common Name (eg, your name or your server\'s hostname)

#2.commonName_default = beta-ssl.google.com

#2.commonName_max = 64

#3.commonName = Common Name (eg, your name or your server\'s hostname)

#3.commonName_default = dev-id.google.com

#3.commonName_max = 64

#4.commonName = Common Name (eg, your name or your server\'s hostname)

#4.commonName_default = dev-member.google.com

#4.commonName_max = 64

emailAddress = Email Address

emailAddress.default = (메일)

emailAddress_max = 64

# SET-ex3 = SET extension number 3

[ req_attributes ]

challengePassword = A challenge password

challengePassword_min = 4

challengePassword_max = 20

unstructuredName = An optional company name

[ usr_cert ]

# These extensions are added when 'ca' signs a request.

# This goes against PKIX guidelines but some CAs do it and some software

# requires this to avoid interpreting an end user certificate as a CA.

basicConstraints=CA:FALSE

# Here are some examples of the usage of nsCertType. If it is omitted

# the certificate can be used for anything *except* object signing.

# This is OK for an SSL server.

# nsCertType = server

# For an object signing certificate this would be used.

# nsCertType = objsign

# For normal client use this is typical

# nsCertType = client, email

# and for everything including object signing:

# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.

# keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# This will be displayed in Netscape's comment listbox.

nsComment = "OpenSSL Generated Certificate"

# PKIX recommendations harmless if included in all certificates.

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid,issuer:always

# This stuff is for subjectAltName and issuerAltname.

# Import the email address.

# subjectAltName=email:copy

# An alternative to produce certificates that aren't

# deprecated according to PKIX.

# subjectAltName=email:move

# Copy subject details

# issuerAltName=issuer:copy

#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem

#nsBaseUrl

#nsRevocationUrl

#nsRenewalUrl

#nsCaPolicyUrl

#nsSslServerName

[ v3_req ]

# Extensions to add to a certificate request

basicConstraints = CA:FALSE

keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]

# Extensions for a typical CA

# PKIX recommendation.

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid:always,issuer:always

# This is what PKIX recommends but some broken software chokes on critical

# extensions.

#basicConstraints = critical,CA:true

# So we do this instead.

basicConstraints = CA:true

# Key usage: this is typical for a CA certificate. However since it will

# prevent it being used as an test self-signed certificate it is best

# left out by default.

# keyUsage = cRLSign, keyCertSign

# Some might want this also

# nsCertType = sslCA, emailCA

# Include email address in subject alt name: another PKIX recommendation

# subjectAltName=email:copy

# Copy issuer details

# issuerAltName=issuer:copy

# DER hex encoding of an extension: beware experts only!

# obj=DER:02:03

# Where 'obj' is a standard or added object

# You can even override a supported extension:

# basicConstraints= critical, DER:30:03:01:01:FF

[ crl_ext ]

# CRL extensions.

# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.

# issuerAltName=issuer:copy

authorityKeyIdentifier=keyid:always,issuer:always

 

두번째 수정할 파일은 /usr/share/ssl/misc/CA 파일이다. CA 파일은 Shell script로 구성되어 있으며, 인증기관 역할을 해준다. 보안(Http) 서버를 리얼 장비에 사용할 때 패스워드에 등록하지만, 개발 장비에 사용될 경우에는 굳이 패스워드를 등록하지 않아도 된다.

이를 위해서 /usr/share/ssl/misc/CA 파일의 일부분을 수정하면 된다. REQ의 내용에 ‘-nodes’를 추가하면 된다.

 

REQ="openssl req $SSLEAY_CONFIG"

->

REQ="openssl req $SSLEAY_CONFIG -nodes"

 

<CA 구성>

CA(Certificate Authority, 인증기관)을 구성한다. 인증기관 자체의 private key와 public key 를 생성한다.

]# mkdir certs

]# cd certs

]# /usr/share/ssl/misc/CA –newca

CA certificate filename (or enter to create) (엔터)

Making CA certificate ...

Generating a 1024 bit RSA private key

.....................++++++

.........................++++++

writing new private key to './demoCA/private/./cakey.pem'

Enter PEM pass phrase:

Verifying - Enter PEM pass phrase:

-----

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [GB]:(입력)

State or Province Name (full name) [Berkshire]:(입력)

Locality Name (eg, city) [Newbury]:(입력)

Organization Name (eg, company) [My Company Ltd]:(입력)

Organizational Unit Name (eg, section) []:(입력)

Common Name (eg, your name or your server's hostname) [dev-ssl.google.com]: (입력)

Email Address []:(입력)

 

demoCA 디렉토리가 생기고, cacert.pem가 생성된다. 이는 CA 인증서 생성됨을 의미한다.

demoCA/private/cakey.pem 파일은 1024bite private key 이다.

 

]# cd demoCA/

]# openssl x509 -in cacert.pem -days 3650 -out cacert.pem -signkey ./private/cakey.pem

Getting Private key

Enter pass phrase for ./private/cakey.pem:

]# /usr/share/ssl/misc/CA -newreq

Generating a 1024 bit RSA private key

......++++++

..........................++++++

writing new private key to 'newreq.pem'

Enter PEM pass phrase:

Verifying - Enter PEM pass phrase:

-----

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [GB]:(엔터)

State or Province Name (full name) [Berkshire]:(엔터)

Locality Name (eg, city) [Newbury]:(엔터)

Organization Name (eg, company) [My Company Ltd]:(엔터)

Organizational Unit Name (eg, section) []:(엔터)

Common Name (eg, your name or your server's hostname) [dev-ssl.google.com]:(dev-ssl.google.com 엔터)

Email Address []:(입력)

Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []: (엔터)

An optional company name []: (엔터)

Request (and private key) is in newreq.pem

 

만들어진 newreq.pem 파일을 원래 생성했던 위치에 두어 다음 작업을 쉽게 하도록 한다.

]# cp newreq.pem ..

]# cd ..

]# ls -al

drwxr-xr-x 3 root root 4096 11 15:09 .

drwxr-xr-x 8 root root 4096 11 15:06 ..

drwxr-xr-x 6 root root 4096 11 15:07 demoCA

-rw-r--r-- 1 root root 1667 11 15:09 newreq.pem

 

그 후, 만들어진 private key 파일을 가지고 CA로부터 인증을 한다. 이 결과로 newcert.pem 이라는 공개키가 생성된다. CA –sign은 인증서 승인 후, 인증서의 정보를 보여준다.

]# /usr/share/ssl/misc/CA -sign

Using configuration from /usr/share/ssl/openssl.cnf

Enter pass phrase for ./demoCA/private/cakey.pem:

Check that the request matches the signature

Signature ok

Certificate Details:

Serial Number: 1 (0x1)

Validity

Not Before: Mar 11 06:10:07 2011 GMT

Not After : Mar 10 06:10:07 2012 GMT

Subject:

countryName =

stateOrProvinceName =

localityName =

organizationName =

organizationalUnitName =

commonName = dev-ssl.google.com

emailAddress = 메일

X509v3 extensions:

X509v3 Basic Constraints:

CA:FALSE

Netscape Comment:

OpenSSL Generated Certificate

X509v3 Subject Key Identifier:

77:BC:33:80:3E:07:F3:10:40:71:D0:EE:49:F6:53:93:66:DB:6A:20

X509v3 Authority Key Identifier:

keyid:B4:8A:40:59:12:D6:3A:44:FC:27:31:A9:3E:D6:D8:B7:CD:40:08:EB

DirName:/C=/ST=/L=/O=/OU=test/CN=dev-ssl.google.com/emailAddress=

serial:00

Certificate is to be certified until Mar 10 06:10:07 2012 GMT (365 days)

Sign the certificate? [y/n]:(y엔터)

1 out of 1 certificate requests certified, commit? [y/n](y엔터)

Write out database with 1 new entries

Data Base Updated

Certificate:

Data:

Version: 3 (0x2)

Serial Number: 1 (0x1)

Signature Algorithm: md5WithRSAEncryption

Issuer: C=, ST=, L=, O=, OU=, CN=dev-ssl.google.com/emailAddress=메일

Validity

Not Before: Mar 11 06:10:07 2011 GMT

Not After : Mar 10 06:10:07 2012 GMT

Subject: C=, ST=, L=, O=, OU=, CN=dev-ssl.google.com/emailAddress=메일

Subject Public Key Info:

Public Key Algorithm: rsaEncryption

RSA Public Key: (1024 bit)

Modulus (1024 bit):

00:ce:0d:60:77:08:7b:8b:35:6e:97:dd:58:d3:0e:

81:ed:1b:d7:55:f6:c1:49:dc:e2:07:93:08:ca:1d:

27:8e:cd:19:30:fc:98:b7:c3:89:0a:98:43:aa:cf:

7e:8c:24:53:1a:87:1c:c1:13:10:0e:db:a5:09:e4:

c3:79:0f:9b:1a:48:4b:51:50:63:bb:9a:23:c9:8f:

e4:b0:a3:5b:74:3c:a7:fe:98:06:3c:fc:47:0e:ed:

d8:e5:b1:3e:e1:8c:c1:e9:11:6e:ec:d4:84:0a:51:

67:7c:2b:86:ac:7d:16:b6:65:90:77:95:88:d2:29:

e4:60:4d:73:59:2e:82:70:c5

Exponent: 65537 (0x10001)

X509v3 extensions:

X509v3 Basic Constraints:

CA:FALSE

Netscape Comment:

OpenSSL Generated Certificate

X509v3 Subject Key Identifier:

77:BC:33:80:3E:07:F3:10:40:71:D0:EE:49:F6:53:93:66:DB:6A:20

X509v3 Authority Key Identifier:

keyid:B4:8A:40:59:12:D6:3A:44:FC:27:31:A9:3E:D6:D8:B7:CD:40:08:EB

DirName:/C=/ST=/L=/O=/OU=/CN=dev-ssl.google.com/emailAddress=메일

serial:00

Signature Algorithm: md5WithRSAEncryption

3c:d4:9b:79:9c:e3:eb:45:52:26:6d:12:61:27:a0:e6:4e:dc:

c3:3a:59:6d:6a:d0:a5:62:e3:88:f5:c9:26:e2:35:15:01:8d:

65:2f:1d:b6:2e:be:62:83:db:cf:80:0b:c6:cc:3c:00:b3:7f:

9a:ce:b0:35:fd:a5:c1:b7:aa:fb:5d:40:2c:4f:39:64:ba:f1:

c3:f8:10:aa:26:d0:5c:30:a1:3b:81:00:a6:77:6e:c5:33:ef:

25:c8:be:c1:c5:2b:40:e2:83:32:a1:a4:e2:99:91:95:d2:da:

83:3e:64:66:45:d3:86:25:db:aa:67:98:d4:fb:2f:cd:31:24:

0a:d7

-----BEGIN CERTIFICATE-----

MIIDqzCCAxSgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCS1Ix

EDAOBgNVBAgTB0t5dW5nZ2kxEDAOBgNVBAcTB0J1bmRhbmcxDDAKBgNVBAoTA05I

TjENMAsGA1UECxMEdGVzdDEcMBoGA1UEAxMTZGV2LXNzbC5uaG5jb3JwLmNvbTEd

MBsGCSqGSIb3DQEJARYOa25pZ2h0QG5obi5jb20wHhcNMTEwMzExMDYxMDA3WhcN

MTIwMzEwMDYxMDA3WjCBjzELMAkGA1UEBhMCS1IxEDAOBgNVBAgTB0t5dW5nZ2kx

EDAOBgNVBAcTB0J1bmRhbmcxDDAKBgNVBAoTA05ITjENMAsGA1UECxMEdGVhbTEc

MBoGA1UEAxMTZGV2LXNzbC5uaG5jb3JwLmNvbTEhMB8GCSqGSIb3DQEJARYSa25p

Z2h0QG5obmNvcnAuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDODWB3

CHuLNW6X3VjTDoHtG9dV9sFJ3OIHkwjKHSeOzRkw/Ji3w4kKmEOqz36MJFMahxzB

ExAO26UJ5MN5D5saSEtRUGO7miPJj+Swo1t0PKf+mAY8/EcO7djlsT7hjMHpEW7s

1IQKUWd8K4asfRa2ZZB3lYjSKeRgTXNZLoJwxQIDAQABo4IBFzCCARMwCQYDVR0T

BAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNh

dGUwHQYDVR0OBBYEFHe8M4A+B/MQQHHQ7kn2U5Nm22ogMIG4BgNVHSMEgbAwga2A

FLSKQFkS1jpE/CcxqT7W2LfNQAjroYGRpIGOMIGLMQswCQYDVQQGEwJLUjEQMA4G

A1UECBMHS3l1bmdnaTEQMA4GA1UEBxMHQnVuZGFuZzEMMAoGA1UEChMDTkhOMQ0w

CwYDVQQLEwR0ZXN0MRwwGgYDVQQDExNkZXYtc3NsLm5obmNvcnAuY29tMR0wGwYJ

KoZIhvcNAQkBFg5rbmlnaHRAbmhuLmNvbYIBADANBgkqhkiG9w0BAQQFAAOBgQA8

1Jt5nOPrRVImbRJhJ6DmTtzDOlltatClYuOI9ckm4jUVAY1lLx22Lr5ig9vPgAvG

zDwAs3+azrA1/aXBt6r7XUAsTzlkuvHD+BCqJtBcMKE7gQCmd27FM+8lyL7BxStA

4oMyoaTimZGV0tqDPmRmRdOGJduqZ5jU+y/NMSQK1w==

-----END CERTIFICATE-----

Signed certificate is in newcert.pem

]# ls -al

drwxr-xr-x 3 root root 4096 11 15:09 .

drwxr-xr-x 8 root root 4096 11 15:06 ..

drwxr-xr-x 6 root root 4096 11 15:10 demoCA

-rw-r--r-- 1 root root 3616 11 15:10 newcert.pem

-rw-r--r-- 1 root root 1667 11 15:09 newreq.pem

 

이렇게 만든 newreq.pem는 키이고, newcert.pem은 인증서이므로, 의미 있는 파일이름으로 각각 변경한다.

]# mv newreq.pem dev-ssl.node.key

]# mv newcert.pem dev-ssl.node.crt

 

<10년짜리 인증서 만들기>

 

CA 기본 shell script에서 디폴트로 1년로 만들기 때문에 매년 연장해야 하는 불편함이 존재할 수 있다. 이를 운영에서 편리하게 하기 위해서 ‘/usr/share/ssl/misc/CA –sign’의 결과의 내용을 살펴보면, 아래와 같이 1년으로 되어 있는 것으로 확인할 수 있다.

Validity

Not Before: Mar 11 06:10:07 2011 GMT

Not After : Mar 10 06:10:07 2012 GMT

Sign할 때의 인증기간을 수정하면 된다. /usr/share/ssl/misc/CA 파일을 수정한다.

첫번째 수정사항은 기간을 수정한다.

DAYS=”-days 365”

->

DAYS="-days 1280"

두번째 수정사항은 Sign 파라미터에 들어가는 스크립트를 수정한다.

$CA -policy policy_anything -out newcert.pem -infiles newreq.pem

->

$CA -policy policy_anything -out newcert.pem -in newreq.pem $DAYS

그리고, 인증서를 만드는 작업을 다시 진행한다.

Sign 작업후에 콘솔로 나오는 화면에 10년 짜리 인증서가 만들어진 것을 확인할 수 있다.

Validity

Not Before: Mar 11 10:46:21 2011 GMT

Not After : Mar 8 10:46:21 2021 GMT

 

 

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

part 2. 사설 인증서를 로딩한 웹 서버 사용하기

 

http서버에 10년 짜리 인증서를 설치한다는 것을 가정한다.

새로 만든 인증서와 키파일을 모두 /home/www/httpd/conf 파일에 위치한다. 암호를 설정하지 않았기 때문에 SSLPassPhraseDialog지시자에 builtin을 지정한다. 만약 암호를 설정하였을 때는 SSLPassPhraseDialog 지시자에 암호를 출력하는 스크립트를 넣을 수 있다.

<IfModule mod_ssl.c>

SSLRandomSeed startup builtin

SSLRandomSeed connect builtin

Listen 443

AddType application/x-x509-ca-cert .crt

AddType application/x-pkcs7-crl .crl

SSLPassPhraseDialog builtin

SSLSessionCache dbm:/home/www/httpd/logs/ssl_scache

SSLSessionCacheTimeout 300

SSLMutex file: /home/www/httpd/logs/ssl_mutex

</IfModule>

..

<VirtualHost *:443>

DocumentRoot /home/www/deploy

ServerName dev-ssl.google.com

<Directory "/home/www/deploy/service">

Options -Indexes FollowSymLinks MultiViews

AllowOverride None

Order allow,deny

Allow from all

FileETag None

</Directory>

<IfModule mod_ssl.c>

SSLEngine on

SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:!SSLv2:+EXP:+eNULL

SSLCertificateFile /home/www/httpd/conf/dev-ssl.node.crt

SSLCertificateKeyFile /home/www/httpd/conf/dev-ssl.node.key

SetEnvIf User-Agent ".*MSIE.*" \

nokeepalive ssl-unclean-shutdown \

downgrade-1.0 force-response-1.0

</IfModule>

위와 같이 설정하고, http 서버를 재시작한다.

 

<로컬 pc 에서 인증서 설치>

로컬 PC에서 웹 브라우져를 이용해서 dev-ssl.google.com으로 접속하면 다음과 같이 보안 인증서에 대한 경고 화면이 뜬다. (Window 7, IE8 기준) 바로 신뢰할 수 있는 루트 인증기관이 아닌 서버에서 인증한 보안 인증서이기 때문이다.

웹 브라우져에서 이런 화면이 나오지 않게 하기 위해서는 해당 서버의 인증서를 신뢰할 수 있는 서버로 등록하면 된다. 인증서를 다운받는 법은 2가지이다. 첫번째는 서버에서 인증서를 파일로 다운받는다. 두번째는 웹브라우져에서 ‘인증서 오류’ 라 나오는 버튼을 클릭하고 ‘인증서 보기’로 해서 창을 띄우는 방법을 사용한다.

인증서를 띄우면 아래와 같이 확인할 수 있다. 10년짜리 인증서이지만, 확인할 수 없는 인증서라고 문구가 나온다. 인증서 설치를 선택한다.

‘인증서 설치’ 버튼을 선택하면 인증서 가져오기 마법사 시작 팝업창이 뜬다. 다음을 선택한다.

인증서 가져오기 마법사에서 ‘모든 인증서를 다음 저장소에 저장’을 선택한다.

인증 저장서를 선택하는 팝업창에서 ‘신뢰할 수 있는 루트 인증 기관’을 선택하고 확인 버튼을 선택한다.

‘인증서 가져오기 마법사’에 대한 완료 팝업창이 뜬다. 마침 버튼을 선택한다.

인증서 설치에 대한 화면이 발생하고 인증서를 설치할 것인지 묻는 팝업창이 발생한다. ‘예’ 버튼을 선택한다.

인증서 설치가 완료되었다는 팝업창이 열린다.

웹 브라우져를 이용해서 dev-ssl.google.com을 접속하면 아래와 같이 ‘보안 인증서에 문제가 있습니다’라는 내용이 뜨지 않는다.

 

<리늑서 서버 설치>

리눅스 서버에서 wget으로 접근시에는 인증시에 대한 검증에서 실패가 되어 에러를 발생시킨다.

]# wget https://dev-ssl.google.com/

[root@e61127 certs2]# wget https://dev-ssl.google.com/

--14:51:58-- https://dev-ssl.google.com/

=> `index.html'

Resolving dev-ssl.google.com... 1.1.1.1

Connecting to dev-ssl.google.com|1.1.1.1|:443... connected.

ERROR: Certificate verification error for dev-ssl.google.com: unable to get local issuer certificate

To connect to dev-ssl.google.com insecurely, use `--no-check-certificate'.

Unable to establish SSL connection.

이 문제를 해결하기 위해서는 간단하게 ‘--no-check-certificate’ 파라미터를 추가하여 접근한다.

]# wget --no-check-certificate https://dev-ssl.google.com/

--14:55:21-- https://dev-ssl.google.com/

=> `index.html.1'

Resolving dev-ssl.google.com... 10.25.149.50

Connecting to dev-ssl.google.com|10.25.149.50|:443... connected.

WARNING: Certificate verification error for dev-ssl.google.com: unable to get local issuer certificate

HTTP request sent, awaiting response... 200 OK

Length: 11 [text/html]

100%[===============================>] 11 --.--K/s

14:55:21 (565.38 KB/s) - `index.html' saved [11/11]

 

<Java에 인증서 설치>

Java를 이용하여 개발 보안 서버에 접근시 에러가 발생한다. Validation Check에서 실패하기 때문이다.

Java의 KeyStore에 해당 보안 개발 서버에 대한 접근이 되도록 할 수 있다. 보안 개발 서버를 사용하는 Linux 또는 PC 서버의 java에 인증서를 설치한다.

참고 웹은 다음 싸이트이다.

http://blogs.sun.com/andreas/entry/no_more_unable_to_find

예제는 다음과 같다. Apache Common의 httpclient 3.0.1를 가지고 dev-ssl.google.com 접속하는 코드이다.

import org.apache.commons.httpclient.HttpClient;

import org.apache.commons.httpclient.HttpStatus;

import org.apache.commons.httpclient.methods.GetMethod;

public class Test {

public static void main(String[] args) throws Exception {

byte[] responseBody = null;

HttpClient client = new HttpClient();

GetMethod method = new GetMethod("https://dev-ssl.google.com");

int responseCode = client.executeMethod(method);

if (responseCode != HttpStatus.SC_OK) {

System.out.println("Method failed: " + method.getStatusLine());

} else {

responseBody = method.getResponseBody();

System.out.println(new String(responseBody));

}

}

}

테스트 코드를 실행하면 SSL handshake를 하면서 인증서에 대한 valid 체크에서 발생했다는 Exception이 발생한다.

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1649)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)

at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)

at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:893)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:632)

at com.sun.net.ssl.internal.ssl.AppOutputStream.write(AppOutputStream.java:59)

at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)

at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)

at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:827)

at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:1975)

at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:993)

at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:397)

at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:170)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324)

at org.Test.main(Test.java:13)

Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:323)

at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:217)

at sun.security.validator.Validator.validate(Validator.java:218)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)

... 17 more

Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)

at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)

at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:318)

... 23 more

Java 에서 신뢰할만한 웹 사이트를 등록하여 인증서가 발생되지 않게 할 수 있다.

Andreas Sterbenz’s Blogs(http://blogs.sun.com/andreas/resource/InstallCert.java)에서 소스를 다운받고 컴파일한다.

]# javac InstallCert.java

보안 개발 서버인 dev-ssl.google.com:443 을 파라미터로 넣고 java를 실행한다. 중간에 “

Enter certificate to add to trusted keystore”를 묻는 질문에 ‘1’을 입력한다.

]# java InstallCert dev-ssl.google.com:443

Loading KeyStore C:\Program Files\Java\jre6\lib\security\cacerts...

Opening connection to dev-ssl.google.com:443...

Starting SSL handshake...

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderExce

ption: unable to find valid certification path to requested target

at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unkno

wn Source)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown

Source)

at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)

at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source

)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Un

known Source)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Sou

rce)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Sou

rce)

at org.InstallCert.main(InstallCert.java:88)

Caused by: sun.security.validator.ValidatorException: PKIX path building failed:

sun.security.provider.certpath.SunCertPathBuilderException: unable to find vali

d certification path to requested target

at sun.security.validator.PKIXValidator.doBuild(Unknown Source)

at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)

at sun.security.validator.Validator.validate(Unknown Source)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown So

urce)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(

Unknown Source)

at org.InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.jav

a:183)

... 9 more

Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to

find valid certification path to requested target

at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown

Source)

at java.security.cert.CertPathBuilder.build(Unknown Source)

... 15 more

Server sent 1 certificate(s):

1 Subject CN=dev-ssl.google.com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=

GB

Issuer CN=dev-ssl.google.com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=

GB

sha1 e6 69 34 03 ec f2 b8 2e eb 57 ad 96 9f 25 a9 69 16 87 fc 90

md5 f0 a8 d1 63 65 8f 5c c6 a0 9b ea 4f 48 58 85 3d

Enter certificate to add to trusted keystore or 'q' to quit: [1]

1

[

[

Version: V3

Subject: CN=dev-ssl.google.com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=

GB

Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4

Key: Sun RSA public key, 1024 bits

modulus: 164347762420350693911866588325014644788520374516295193084948890924394

29081792855076590616988805837259808518029679131404902167803255295786539153611119

63396590676780565399712003063159189376410163690837891366179294614852352512484317

28761333574986219672551265326558528460114938587517180908714459949305597793224899

public exponent: 65537

Validity: [From: Sat Mar 12 14:05:07 KST 2011,

To: Tue Mar 09 14:05:07 KST 2021]

Issuer: CN=dev-ssl.google.com, O=, L=, ST=, C=

SerialNumber: [ 01]

Certificate Extensions: 4

[1]: ObjectId: 2.16.840.1.113730.1.13 Criticality=false

Extension unknown: DER encoded OCTET string =

0000: 04 1F 16 1D 4F 70 65 6E 53 53 4C 20 47 65 6E 65 ....OpenSSL Gene

0010: 72 61 74 65 64 20 43 65 72 74 69 66 69 63 61 74 rated Certificat

0020: 65 e

[2]: ObjectId: 2.5.29.14 Criticality=false

SubjectKeyIdentifier [

KeyIdentifier [

0000: 9E 6F 1F 00 01 A4 3F 8E 8D 03 B8 6C 0B B5 57 27 .o....?....l..W'

0010: 3A 11 0C 70 :..p

]

]

[3]: ObjectId: 2.5.29.35 Criticality=false

AuthorityKeyIdentifier [

KeyIdentifier [

0000: F4 8D D1 BC BA 8F B4 17 3E 98 93 E9 04 59 5A 32 ........>....YZ2

0010: 41 3D DF 36 A=.6

]

[CN=dev-ssl.google.com, O=, L=, ST=, C=]

SerialNumber: [ 00]

]

[4]: ObjectId: 2.5.29.19 Criticality=false

BasicConstraints:[

CA:false

PathLen: undefined

]

]

Algorithm: [MD5withRSA]

Signature:

0000: 2A E4 E5 14 91 58 17 84 39 DE BA 8F 41 61 92 F3 *....X..9...Aa..

0010: FA 9C 01 70 D9 F9 44 DD B1 02 E4 D9 1D 7C 58 2A ...p..D.......X*

0020: 5A D9 39 E2 24 F7 DD 09 DC 3C 62 C7 09 3D 87 43 Z.9.$....<b..=.C

0030: 44 DC 68 FE 12 89 8E 54 62 94 3D B7 F5 2E 4E 30 D.h....Tb.=...N0

0040: 99 B8 CB 8A 6D 8D 3A 47 0A E3 5C 81 5F AE 4B CF ....m.:G..\._.K.

0050: 8E 3D 70 64 EA E2 72 C5 E7 21 27 74 19 80 91 95 .=pd..r..!'t....

0060: E8 D7 07 E1 9E 53 06 66 D0 4C 79 92 DC D7 4E 30 .....S.f.Ly...N0

0070: 8F 21 37 15 38 01 9F F1 E9 C1 E9 E5 99 03 FB F8 .!7.8...........

]

Added certificate to keystore 'jssecacerts' using alias 'dev-ssl.google.com-1'

실행 이후에 자바 인증서인 jssecacerts 파일이 생성된다.

이 파일을 $JAVA_HOME/jre/lib/security 에 복사하고 Http 연결 테스트 코드를 실행하면 기대하는 결과값이 출력되는 것을 확인할 수 있다. 이렇게 보안 개발 서버와 통신하는 서버들은 이렇게 사용하여 공인 인증서 없이 테스트 환경을 구축할 수 있다.

 

<Keytool 이용>

자바에서는 인증서 파일에 대한 정보를 keystore라고 불리며, cacerts, jssecacerts 두 개의 파일을 사용한다. 읽는 순서는 첫번째가 jssecacerts 파일이고, 그 다음은 cacerts파일이다. 관리가 잘 되어, overwrite되지 않도록 잘 관리되어야 한다.

추가적인 보안 개발 서버를 추가할 때마다 keytool 명령어를 이용해서 관리할 수 있다.

keytool에 대한 자세한 내용은 문서를 참조한다.

http://download.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html

http://download.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html

리눅스를 이용하는 경우에 jssecacerts keystore 파일에 보안 개발 서버에 대한 인증서가 있는지 확인할 수 있다. 아래 싦행 명령어의 ‘실제 위치’는 ‘$JAVA_HOME/jre/lib/security’를 의미한다.

]# keytool -list –keystore 실제위치/jssecacerts -storepass changeit | grep dev-ssl

dev-ssl.google.com-1, 2011. 3. 14, trustedCertEntry,

더 이상 사용하지 않는다면 keystore 파일에서 보안 개발 서버를 삭제할 수 있다.

]# keytool -delete -alias dev-ssl.google.com-1 -keystore 실제위치/jssecacerts -storepass changeit

]# keytool -list -keystore 실제위치/jssecacerts -storepass changeit | grep dev

(결과 없음)

자바에서 http 서버로 보안 개발 서버로 접근하면, Exception이 발생되는 것을 확인할 수 있다.

윈도우를 사용하는 경우, cmd창을 실행하고, 창안에서 다음과 같이 확인 및 삭제가 가능하다.

]# keytool.exe -list -keystore 실제위치/jssecacerts -storepass changeit | findstr dev-ssl

dev-ssl.google.com-1, 2011. 3. 14, trustedCertEntry,

]# keytool -delete -alias dev-ssl.google.com-1 -keystore 실제위치/jssecacerts -storepass changeit

]# keytool.exe -list -keystore 실제위치/jssecacerts -storepass changeit | findstr dev-ssl

(결과 없음)

Posted by '김용환'
,

 

<시작하며..>

(2011-2012) 작년 연말/올해 연초 부터 hash dos 공격에 대한 보안 취약성에 대한 얘기가 있었다. 관련 내용을 정리해서 올려본다.

 

<시작>

12월 29일 한 통의 메일을 톰캣 리더 개발자인 Mark Thomas로부터 메일을 받았다. dos 공격을 당할 수 있는 내용에 대해서 oracle은 패치를 하지 않을 것이고, tomcat은 maxParameterCount의 값을 디폴트로 10000개로 지정하는 패치를 내어놓겠다고 했다.  톰캣 5 는 더이상 패치하겠다고 발표한 생태였지만, 패치를 하겠다고 얘기가 있었다.

 

[SECURITY] Apache Tomcat and the hashtable collision DoS vulnerability

11-12-29 (목) 07:28

추가정보 보기 보낸사람
: "Mark Thomas"<markt@apache.org> 주소록에 추가
받는사람
: "Tomcat Users List"<users@tomcat.apache.org>
참조    
: <announce@tomcat.apache.org>, <announce@apache.org>, "Tomcat Developers List"<dev@tomcat.apache.org>

You may have read about a recently announced vulnerability rooted in the
Java hashtable implementation [1]. Since Apache Tomcat uses a hashtable
for storing HTTP request parameters, it is affected by this issue.
As per [1], it appears that Oracle will not be providing a fix for this
vulnerability with in the JRE.
Tomcat has implemented a work-around for this issue by providing a new
option (maxParameterCount) to limit the number of parameters processed
for a single request. This default limit is 10000: high enough to be
unlikely to affect any application; low enough to mitigate the effects
of the DoS.
The work-around is available in:
trunk
7.0.23 onwards
6.0.35 onwards
The work-around will also be available in 5.5.35 once released.
If using an earlier version of Apache Tomcat that does not have the
maxParameterCount attribute available, limiting the maxPostSize to a few
10's of kB should also mitigate the issue although it may cause issues
for some applications.
While this is not viewed as a vulnerability in Apache Tomcat, the Apache
Tomcat security team is making this announcement due to the high
likelihood that applications will be affected by this issue and to make
users aware of the available work-arounds.
The Apache Tomcat security team
[1] http://www.nruns.com/_downloads/advisory28122011.pdf

자세한 정보는 메일의 아래 문서에서 확인할 수 있다. http://www.nruns.com/_downloads/advisory28122011.pdf

<간략하게 소개하는 취약점과 테스트 자료>

대부분의 언어에서는 Hash table을 많이 사용하고 있다. 특히 웹 서비스의 경우는 hash table로 데이터를 매핑해서 쓴다. 특히 post 방식일 때는 hash table을 파라미터 값으로 자동으로 매핑한다. get 방식일 때는 파라미터의 길이에 대해서 이미 브라우져단/서버단에서 기본적으로 제한을 가져서 문제가 없다. 그러나 post 방식일 때는 파라미터 개수 제한이 없다. (물론 서버 설정에서 이를 설정상으로 막을 수 있다.)

이를 악용하여 공격자가 조작된 값(colliding keys)을 파라미터로 전달하여 cpu 를 소진시킬 수 있다. hash contension이 일어나도록 동일한 string key값을 보내서 서버에 부담을 준다. (multi-collisions)
또는 randomized hash function이 없는 경우에도 활용될 수 있다.
hash table의 약점을 노린 것으로 hash table에 파라미터 입력시 O(n**2)으로 complexity가 높아지면서 cpu를 소진시켜 서버가 서비스를 못하게 한다. 하나의 요청만으로 웹 서버의 서비스를 못하게 할 수 있다.

hashtable에 존재하는 값과 입력된 값이 다를 경우 충돌이 일어나고, 그 충돌되는 값이 많아지면서 해당 값들을 계속 비교하는 경우를 multi-collision이라고 한다.

You tube에는 이런 취약점을 이용해서 간단하게 테스트한 자료가 있다.

<

 

<자바 패치 >

2003년부터 이 부분이 알려져 있다. Rice 대학의 Crosby, Wallach 가 발표한 논문에 비슷한 논문이 있다.
http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003.pdf

Perl와 CRuby는 고쳐졌지만 php 4/5, V8, 자바, asp.net, Python, Ruby에 보안 취약이 있었다.

자바의 경우는 String.hasCode()  메서드를 이용한 HashMap, HashTable 클래스를 사용하고 있다. DJBX33A 알고리즘을 사용하는데. 이슈가 있다. 2011년 11월 1일 레드햇 버그질라에 이 문제에 대한 이슈가 올라왔었지만, 오라클은 이 부분에 대해서 자바 hashmap 구현을 바꾸지 않겠다고 했다. 즉 자바 언어의 hash 구현의 이슈가 아니라고 답변하였고 웹 서버(tomcat, jbossweb, flassfish)에서 처리되는 것으로 되었다. (개인적인 의견으로는 참 애매한 이슈인 것 같다. 원래 그렇게 최악의 상황을 감안하고 구현되었을 수도 있고, 이거 고쳐서 backward compatibility를 잃어버리는 것보다는 나을 수 있다는 생각도 든다. 굳이 고쳐서 탈나는 것보다는 이게 나을 수도 ^^;;;)

https://bugzilla.redhat.com/show_bug.cgi?id=750533

그래서 12월 29일 close가 되었고, tomcat 리더 개발자인 mark tomas는 메일을 보낸 것이었다.

 

<톰캣 패치>

6.0.35, 7.0.23이 바로 패치되었고, 5.5.35는 늦게 패치가 되었다.

위에서 얘기한 Connector의 필드값으로 maxParameterCount 개수를 지정하면 문제를 해결하면 된다.

http://tomcat.apache.org/tomcat-6.0-doc/config/ajp.html

 

패치 할 수 없는 상황에서는 maxPostSize 를 이용할 수도 있다.

 

핵심 코드가 바뀌었는지 간단히 살펴본다.

http://www.mail-archive.com/dev@tomcat.apache.org/msg57020.html

Connector.java

maxParameterCount 파라미터를 읽는다.

tomcat/trunk/java/org/apache/catalina/connector/Connector.java

     /**
+     * The maximum number of parameters (GET plus POST) which will be
+     * automatically parsed by the container. 10000 by default. A value of less
+     * than 0 means no limit.
+     */
+    protected int maxParameterCount = 10000;


+    /**
+     * Return the maximum number of parameters (GET plus POST) that will be
+     * automatically parsed by the container. A value of less than 0 means no
+     * limit.
+     */
+    public int getMaxParameterCount() {
+        return maxParameterCount;
+    }
+
+
+    /**
+     * Set the maximum number of parameters (GET plus POST) that will be
+     * automatically parsed by the container. A value of less than 0 means no
+     * limit.
+     *
+     * @param maxParameterCount The new setting
+     */
+    public void setMaxParameterCount(int maxParameterCount) {
+        this.maxParameterCount = maxParameterCount;
+    }

 

tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java

기존에는 간단하게 Hashtable<String,String[]>의 값을 저장했지만, 새로 바뀐 버전에는 Hashtable<String,ArrayList<String>> 타입으로 바꾸었다.

-    private final Hashtable<String,String[]> paramHashStringArray =
-        new Hashtable<String,String[]>();

==>

+    private final Hashtable<String,ArrayList<String>> paramHashValues =
+        new Hashtable<String,ArrayList<String>>();

 

구버전까지는 key값에 대한 value값을 array list로 해서 계속 추가되는 형태이다. 반면 신버전에서는 Capacity의 크기를 체크한 후 값을 저장하는 코드로 바뀌었다. 이 값은

-        String values[];
-        if (paramHashStringArray.containsKey(key)) {
-            String oldValues[] = paramHashStringArray.get(key);
-            values = new String[oldValues.length + newValues.length];
-            for (int i = 0; i < oldValues.length; i++) {
-                values[i] = oldValues[i];
-            }
-            for (int i = 0; i < newValues.length; i++) {
-                values[i+ oldValues.length] = newValues[i];
-            }
-         } else {
-            values = newValues;
          }
-        paramHashStringArray.put(key, values);
     }

==>

+        ArrayList<String> values;
+        if (paramHashValues.containsKey(key)) {
+             values = paramHashValues.get(key);
         } else {
+            values = new ArrayList<String>(1);
+            paramHashValues.put(key, values);
+        }
+        values.ensureCapacity(values.size() + newValues.length);
+        for (String newValue : newValues) {
+            values.add(newValue);
         }


<아파치 해결>

LimitRequestBody 값으로 개수를 지정한다. 디폴트값은 1000000(백만)이라서 조절이 필요할 수 있다.
http://httpd.apache.org/docs/2.0/mod/core.html

<Directory "/home/www/web">
    LimitRequestBody 102400
</Directory>

 

* 참고 사항

이 메일의 배경은 12월 28일 독일에서 열린 Chaos Communication Congress의  Julian zeri Walde가 발표로 인해서 알려졌다. 자세한 내용은 아래와 같다. 원리에 대한 설명이 있다.

http://events.ccc.de/congress/2011/Fahrplan/attachments/2007_28C3_Effective_DoS_on_web_application_platforms.pdf

간단하게 이 자료를 소개하면서 자바의 소스를 분석해본다.

일반적으로 hash table은 O(n) 방식으로 내용을 검색하거나 추가할 수 있다.

 

그러나 하나의 키에 계속 값이 붙는 screwed 상황이 일어날 수 있다.  이 때는 O(n**2)가 되어서 속도가 저하되는 이슈가 있다.

 

 

만약 10bytes 짜리 20만개를 최악의 케이스로 넣는 다면, 40,000,000,000 번의 string 비교가 일어나고 40초가 걸릴 수 있다. (참고로 String intern, String compare는 비싼 자원이다..)

 

자바의 내부는 String hash값은 어떻게 되어 있을까?

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{

/** Cache the hash code for the string */
private int hash; // Default to 0

/**
     * Returns a hash code for this string. The hash code for a
     * <code>String</code> object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using <code>int</code> arithmetic, where <code>s[i]</code> is the
     * <i>i</i>th character of the string, <code>n</code> is the length of
     * the string, and <code>^</code> indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
    int h = hash;
        int len = count;
    if (h == 0 && len > 0) {
        int off = offset;
        char val[] = value;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }

}

 

결국 hash code 공식은 다음 공식을 따른다.

hash code = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

위키에 좀더 멋진 공식 표현이 있다. (http://en.wikipedia.org/wiki/Java_hashCode())

h(s)=\sum_{i=0}^{n-1}s[i] \cdot 31^{n-1-i}

 

멋진 공식이라 하더라도. hash value는 int 라는 제한적인 값(min<int<max) 범위에 있기 때문에 중복될 수 있다. 또한 아래와 같이 동일한 hashcode를 생성할 수 있다. 이런 것을 악용한다면, 웹 서버들을 장애를 일으킬 수 있을 것이다.

public class a1 {
    public static void main(String[] args) {
        System.out.println(new String("FB").hashCode());
        System.out.println(new String("Ea").hashCode());
    }
}

#결과
2236
2236

 

생각해보면, hashcode는 unique한 값이 아닌 적당하게 써서 관리하기 위한 목표를 가지고 있다. 취약점을 어쩔 수 없으니. 적당하게 크기 제한하는 것이 좋은 사례인 것 같다..


PS
2012년 1월 17일 Mark Tomas는 정식으로 메일을 보냈다.
http://mail-archives.apache.org/mod_mbox/tomcat-announce/201201.mbox/%3C4F155CE2.3060301%40apache.org%3E

CVE-2012-0022 Apache Tomcat Denial of Service

Severity: Important

Vendor: The Apache Software Foundation

Versions Affected:
- Tomcat 7.0.0 to 7.0.22
- Tomcat 6.0.0 to 6.0.33
- Tomcat 5.5.0 to 5.5.34
- Earlier, unsupported versions may also be affected

Description:
Analysis of the recent hash collision vulnerability identified unrelated
inefficiencies with Apache Tomcat's handling of large numbers of
parameters and parameter values. These inefficiencies could allow an
attacker, via a specially crafted request, to cause large amounts of CPU
to be used which in turn could create a denial of service.
The issue was addressed by modifying the Tomcat parameter handling code
to efficiently process large numbers of parameters and parameter values.

Mitigation:
Users of affected versions should apply one of the following mitigations:
- Tomcat 7.0.x users should upgrade to 7.0.23 or later
- Tomcat 6.0.x users should upgrade to 6.0.35 or later
- Tomcat 5.5.x users should upgrade to 5.5.35 or later

Credit:
The inefficiencies in handling large numbers of parameters were
identified by the Apache Tomcat security team.

References:
http://tomcat.apache.org/security.html
http://tomcat.apache.org/security-7.html
http://tomcat.apache.org/security-6.html
http://tomcat.apache.org/security-5.html




 

Posted by '김용환'
,