Monthly Archives: August 2007

CAPTCHA와 reCAPTCHA

웹페이지 돌아다니다가 글 쓰려고 하면 스팸 방지 등을 위해서 그림으로 문자들을 보여주고 문자를 사용자가 입력하여 확인하는 경우가 있습니다.

최근에 제가 rapidshare.com와 badongo.com에서 자료(?)를 받는데 일반 사용자는 다운로드 받을때 그림을 보고 입력하는 부분이 있습니다. 프로그램으로 기계적으로 다운로드해서 네트워크 부하를 많이 일으키지 못하도록 하는거죠. 이미지는 아래와 같이 생겼고, rapidshare에서 사용하는 이미지는 글자의 왜곡이 거의 없네요.


프로그램적으로 저런 이미지를 읽기가 얼마나 어려운지 모르겠지만, 간단히 이미지에서 글을 읽기 힘들기 때문에 조금더 어렵게 하는 의미는 있겠죠.

위와 같은 기술을 CAPTCHA라고 하는데, 위키피디아에서 많은 정보를 얻을수 있습니다. CAPTCHA는 Completely Automated Public Turing Test to tell Computers and Humans Apart의 약자라고 하는데, 컴퓨터와 사람을 구별하는 완전히 자동화된 테스트 정도 되겠네요. 약자를 그럴듯하게 만들기 위해서 약간 부가적인 단어가 들어간듯 싶습니다. CMU에서 만든 용어고 CMU에서 많이 연구를 했나봅니다.

이미지를 복잡하게 만들면 문자를 읽어내는 프로그램을 만들기가 더 힘들지만, 사람 역시 문자를 판독하기가 어려워진다고 합니다. 일반적인 홈페이지에서는 CAPTCHA를 도입하는 것만으로도 악의적인 사용자의 공격을 어렵게 만들수 있지만, 사이트를 공격할만한 가치가 크다면 문자를 판독하는 알고리즘을 개발할수도 있고, relay 공격으로 CAPTCHA 문제들을 자동화해서 풀수도 있습니다. relay 방법은 알고리즘을 통해서 문자를 읽는것이 아니라, 사용자가 어느정도 있는 사이트를 악의적인 사용자가 운영하고 있다면, 사용자들이 로그인하거나 가입할때 목표로 하는 CAPTCHA 문제를 받아와서 사용자가 풀게하여 목표 사이트에 가입이나 스팸성 글을 올리는 방법입니다. 아무리 CAPTCHA를 어렵게 만들어도 relay 공격 방법은 막을수 없죠.

어떻게 계산했는지 모르겠지만, 전세계적으로 매일 대략 150,000 시간이 CAPTCHA를 푸는데 소비(낭비)된다고 합니다! 이렇게 허비되는 시간을 좀더 유용하게 사용하기 위한 프로젝트가 reCAPTCHA입니다. 역시 CMU 프로젝트이고, CAPTCHA 문제를 만들때 임의로 생성하는 것이 아니라, 오래된 문서에서 OCR로 판독이 안되는 부분을 문제로 내는겁니다. 하나의 문제만 내면 사용자가 정답을 입력했는지 알 방법이 없기 때문에, 두개의 CAPTCHA 문제를 내고, 사용자들의 입력 결과를 통계를 내서 오랜된 문서의 디지털화에 도움을 줍니다. 정말 반짝이는 아이디어네요. 아래는 reCAPTCHA의 예제입니다.


시간이 있으면 Tattertools 플러그인을 제작하면 좋을것 같네요. 찾아보니 없는거 같네요. 그리고 트랙백 받을때도 어떻게 활용할 방법이 없을까하는 생각도 드네요. 가끔 트래백으로 스팸성 댓글이 많이 달리거든요.

VMware 상장, Xen 피인수

굴직한 Virtualization 회사들의 소식입니다.

VMware가 2007년 8월 14일 상장했습니다. 상장한지 하루만에 78% 올라서 시가 총액이 $19 billion (약 19조원) 되었다고 하네요.

http://biz.yahoo.com/ap/070814/vmware_ipo.html?.v=22

창업한지 3년된 Xen-Source를 Citrix 에서 $500 million(약 500억원)에 인수했다고 합니다.

http://venturebeat.com/2007/08/15/citrix-acquires-xensource-for-500m-in-virtualization-frenzy/

VMware가 Virtualization 분야에선 아직 절대강자이긴 하지만, 금액은 많이 비교가 되네요…

XPath & libxml2

XML은 꽤 오래전부터 사용했지만, 여러가지 복잡한 용어들이 나오면서 좀 멀어졌던 느낌이었는데, XPath는 정말 프로그래머에게 유용한 툴인것 같네요.

XPath는 XML 문서에서 쉽게 element를 찾는 API로 쿼리를 문자열로 넘기면 조건에 맞는 element나 element 리스트를 반환하게 됩니다. 1.0 버전이 있고 2.0 버전이 최근에 나왔습니다. 아직까지는 라이브러리들이 1.0 기반이 대부분입니다.

쿼리는 예제로 살펴보는것이 빠른듯하네요.

“A/B/C” : A element 밑에 B element 밑에 C element들은 찾을때
“/A/B/C” : 위와 같지만 A가 최상위 element.
“/A/B/C[1]” : C element중 첫번째
“/A/B/C[2]” : C element중 두번째
“//C” : 모든 C element
“B//C” : B 하위에 있는 C element
“A/B/*” : A element 밑에 B element 바로 밑의 모든 element
“A/B//*” : A element 밑에 B element 밑의 모든 element (하위 element 포함)
“//*” : 문서의 모든 element

libxml2라는 C 라이브러리가 있지만 python wrapper를 이용하여 python에서 위의 쿼리들을 돌려봤습니다. 테스트해본 결과 /로 시작하지 않는 쿼리들은 제대로 동작하지 않더군요. (검색되는 결과가 없음) 이런 쿼리들은 앞에 //를 붙여주면 제대로 동작합니다.

아래는 python 소스입니다. 중간에 예제 XML을 보기 좋게(?) 들여쓰기했놨지만 출력할때 한줄로 볼수 있도록 xml에서 공백과 newline을 제거합니다.

[CODE type=python]
import libxml2

def xpathElements(ctxt, query):
   if query[0] == ‘/’:
       print “\”%s\”” % query
   else:
       print “\”%s\” -> \”//%s\”” % (query, query)
       query = “//” + query
   res = ctxt.xpathEval(query)
   for e in res:
       print ”    %s (%s)” % (e.name, e)

xml = “””
<A>
   <B>
       <C id=’c1’/>
       <C id=’c2′>
           <D/>
       </C>
       <E>
           <F/>
           <A>
               <B>
                   <C/>
               </B>
           </A>
       </E>
   </B>
</A>”””

xml = ”.join([l.strip() for l in xml.splitlines()])

doc = libxml2.parseDoc(xml)

ctxt = doc.xpathNewContext()

xpathElements(ctxt, “A/B/C”)
xpathElements(ctxt, “/A/B/C”)
xpathElements(ctxt, “/A/B/C[1]”)
xpathElements(ctxt, “/A/B/C[2]”)
xpathElements(ctxt, “/A/B/C[3]”)
xpathElements(ctxt, “//C”)
xpathElements(ctxt, “//B//C”)
xpathElements(ctxt, “A/B/*”)
xpathElements(ctxt, “A/B//*”)
xpathElements(ctxt, “//*”)
[/HTML][/CODE]

다음은 실행결과입니다.

“A/B/C” -> “//A/B/C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“/A/B/C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
“/A/B/C[1]”
   C (<C id=”c1″/>)
“/A/B/C[2]”
   C (<C id=”c2″><D/></C>)
“/A/B/C[3]”
“//C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“//B//C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“A/B/*” -> “//A/B/*”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   E (<E><F/><A><B><C/></B></A></E>)
   C (<C/>)
“A/B//*” -> “//A/B//*”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   D (<D/>)
   E (<E><F/><A><B><C/></B></A></E>)
   F (<F/>)
   A (<A><B><C/></B></A>)
   B (<B><C/></B>)
   C (<C/>)
“//*”
   A (<A><B><C id=”c1″/><C id=”c2″><D/></C><E><F/><A><B><C/></B></A></E></B></A>)
   B (<B><C id=”c1″/><C id=”c2″><D/></C><E><F/><A><B><C/></B></A></E></B>)
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   D (<D/>)
   E (<E><F/><A><B><C/></B></A></E>)
   F (<F/>)
   A (<A><B><C/></B></A>)
   B (<B><C/></B>)
   C (<C/>)