2008 06 Test CAPTCHA

background image

16

POCZĄTKI

HAKIN9 6/2008

P

odczas wypełniania różnorodnych formularzy
na stronach WWW bardzo często musimy
udowodnić, że nie jesteśmy botami. Zwykle

służą do tego specjalnie spreparowane obrazki, z
których przeciętny człowiek powinien bez problemu
odczytać nieco zniekształcony tekst (Rysunek 1).

Taki rodzaj zabezpieczeń przydaje się

szczególnie przy różnego rodzaju rejestracjach,
np. darmowych kont e-mail, lub wypowiedziach
na forach internetowych. Chroni on przed
automatycznym tworzeniem kont przez automaty
(np. roboty spamerskie). Technika ta nosi nazwę
CAPTCHA, co jest skrótem angielskiego określenia
Completely Automated Public Turing Test to Tell
Computers and Humans Apart
. Luis von Ahn,
Manuel Blum, Nicholas Hopper i John Langford z
Carnegie Mellon University stworzyli ją na potrzeby
Yahoo. Chcieli w ten sposób ukrócić automatyczne
tworzenie spamerskich kont e-mail na tym portalu.
Na początku CAPTCHA sprawdzała się wyśmienicie.
Jednak – jak to w przyrodzie bywa – każdej akcji
towarzyszy reakcja. Opracowanych zostało kilka
typów ataków, które w mniejszym bądź większym
stopniu umożliwiały obejście tego zabezpieczenia.
Najprostszy atak polega na zatrudnieniu
podstawionych osób, które zakładały konta e-mail
chronione przez system CAPTCHA. W końcu osoby te
były w stanie odczytać zniekształcony tekst. Problem
w tym, że nikt nie będzie chciał robić czegoś takiego
za darmo – należy za to zapłacić, co może okazać
się mało opłacalne dla spamera. Drugi problem to
niska efektywność rozwiązania. Inne, dosyć ciekawe

SŁAWOMIR ORŁOWSKI

Z ARTYKUŁU

DOWIESZ SIĘ

co to jest test CAPTCHA,

jakie są wady i zalety tego

rozwiązania,

jak z poziomu kodu C#

wygenerować obrazek

CAPTCHA,

o klasach platformy .NET

obsługujących grafikę 2D.

CO POWINIENEŚ

WIEDZIEĆ

znać podstawy języka C#

i technologii ASP.NET.

rozwiązanie polega na użyciu podstawionych stron
– najlepiej pornograficznych. Jeśli użytkownik takiego
serwisu WWW zechce obejrzeć jakąś jego część
(zdjęcie), musi wpisać kod z obrazka. Wystarczy, że
obrazek zabezpieczający do takiej strony zostanie
wklejony z formularza znajdującego się na stronie,
którą chcemy zaatakować. Nieświadomy użytkownik
sam rozwiąże za nas zagadkę. Jest to kolejny rodzaj
ataku, w którym wykorzystujemy człowieka. Sprytne,
bardziej efektywne, tańsze – ale czy wystarczająco
skuteczne? Ludzie odpowiedzialni za projekt
CAPTCHA twierdzą, że nie. Spamerzy twierdzą
oczywiście, że tak. Jednak proszę sobie wyobrazić
serwis WWW, w którym za każdym razem, kiedy
chcemy zobaczyć coś nowego, musimy wpisywać
jakiś kod. Wątpię, żeby taka strona przyciągała
wielu użytkowników. Lepszym rozwiązaniem są
programy OCR (ang. Optical Character Recognition),
które za pomocą pewnych algorytmów potrafią
odczytać tekst z obrazka. Oczywiście standardowe
aplikacje OCR nie poradzą sobie z przeciętnym
obrazkiem CAPTCHA. Potrzebne są narzędzia
bardziej wyspecjalizowane, stworzone specjalnie na
potrzeby łamania akurat tego typu zabezpieczeń.
Jednym z takich projektów jest program PWNtcha
(ang. Pretend We’re Not a Turing Computer but a
Human Antagonist
). Potrafi on prawdopodobnie
zdekodować tekst pochodzący ze stosunkowo
mało skomplikowanych obrazów. Nie należy jednak
oczekiwać, że poradzi sobie z każdym obrazkiem.
Aby w miarę dobrze działał, należy go dostroić do
konkretnego problemu. Napisałem prawdopodobnie,

Stopień trudności

Test CAPTCHA

Jak odróżnić człowieka od automatu w Internecie? Istnieje prosty

test, który może to sprawdzić. Wystarczy wygenerować obrazek

ze zniekształconym tekstem i kazać użytkownikowi go odczytać.

background image

17

TEST CAPTCHA

HAKIN9

6/2008

ponieważ autor PWNtcha nie udostępnia
tego programu do testów. Jest to dosyć
logiczne, ponieważ narzędzie mogłoby być
użyte przez spamerów. Innym projektem jest
aiCAPTCHA. Używa on algorytmów sztucznej
inteligencji. Nie sądzę jednak, że zostanie
stworzony program, który będzie sobie radził
ze wszystkimi rodzajami obrazków CAPTCHA.
Ostatnio na świecie pojawiło się coraz więcej
przeciwników tego zabezpieczenia. Osoby
niedowidzące mają poważne problemy przy
odczytaniu tekstu. Obrazki te są również
coraz trudniejsze do odczytania przez
dobrze widzące osoby. Taki system wymaga
również od użytkownika pewnej fatygi, a to
może skutecznie zniechęcić użytkowników.
Przecież korzystanie z Internetu powinno być
lekkie, łatwe i przyjemne. Istnieje też sporo
innych, darmowych i komercyjnych rozwiązań
przeciwko botom, jednak test CAPTCHA
nadal jest – i pewnie jeszcze długo będzie
– wykorzystywany.

W tym artykule chciałbym zaproponować

stworzenie własnej klasy, która będzie miała
możliwość generowania obrazków CAPTCHA.
Klasa będzie napisana w języku C#, w
związku z tym będzie ją można używać w
projektach ASP.NET. Użyjemy standardu C#
2.0 i .NET 2.0. Program napisany będzie przy
użyciu Visual C# 2008 Express Edition. Niech

klasa nosi nazwę Captcha i będzie częścią
przestrzeni nazw Hakin9. Na początku
zadeklarujemy pola i konstruktory klasy
(Listing 1.).

Pola

width

i

height

będą

przechowywały odpowiednio szerokość
i wysokość obrazka. Pole

fontSize

odpowiada za wielkość czcionki. Pole

text

zawierać będzie tekst, jaki ma

być umieszczony na obrazku. Pole

random

będzie pomocne przy generacji

liczb pseudolosowych, potrzebnych
do losowego dodawania pewnych
elementów utrudniających programom
OCR odczytanie tekstu. Pora teraz napisać
główną metodę klasy, która będzie
generowała obrazek (Listing 2.).

Metoda ta zwracać będzie obiekt

typu

Bitmap

, czyli mapę bitową. Ponieważ

w definicji klasy zadeklarowaliśmy trzy
konstruktory, na początku metody

Generate

musimy sprawdzić, czy wszystkie wymagane

pola są wypełnione danymi. Jeśli nie,
generowany jest wyjątek. Dalej deklarujemy
egzemplarz klasy

Bitmap

, który zawierać

będzie obrazek. Na jego podstawie budujemy
obiekt klasy

Graphics

, który umożliwi nam

rysowanie. Pole, które będziemy wypełniać
grafiką, definiujemy za pomocą klasy

Rectangle

.

Płótno, na którym będziemy malować,

jest już przygotowane – pora teraz na pędzel.
Użyjemy do tego klasy

HatchBrush

. W

konstruktorze tej klasy przekazujemy styl, kolor
rysowania i kolor podkładu. Zbiór styli zawiera
typ wyliczeniowy

HatchStyle

– mamy

do wyboru aż 56 rodzajów wypełnienia.
Teraz za pomocą referencji

g

wypełniamy

całość obrazka używając do tego metody

FillRectangle

. W tym przypadku wybrany

został styl

SmallConfetti

, ale nic nie stoi

na przeszkodzie, aby Czytelnik przeciążył
metodę

Generate

z argumentem wywołania,

który umożliwi własny wybór stylu. Jest to

Rysunek 2.

Przykładowe użycie klasy

Captcha

Listing 1.

Pola i konstruktory klasy Captcha

using

System

;

using

System

.

Text

;

using

System

.

Drawing

;

using

System

.

Drawing

.

Imaging

;

using

System

.

Drawing

.

Drawing2D

;

namespace

Rysunki

{

public

class

RPicture

{

private

int

width

;

private

int

height

;

private

int

fontSize

;

private

string

text

;

private

Random

random

;



public

RPicture

()

{

random

=

new

Random

();

}

public

RPicture

(

int

_width

,

int

_height

,

int

_fontSize

)

{

random

=

new

Random

();

width

=

_width

;

height

=

_height

;

fontSize

=

_fontSize

;

}

public

RPicture

(

int

_width

,

int

_height

,

int

_fontSize

,

string

_text

)

{

random

=

new

Random

();

width

=

_width

;

height

=

_height

;

fontSize

=

_fontSize

;

text

=

_text

;

}

}

Rysunek 1.

Przykładowe obrazki

zabezpieczające

background image

18

POCZATKI

HAKIN9 6/2008

TEST CAPTCHA

19

HAKIN9

6/2008

bardzo proste ćwiczenie, które może wykonać
Czytelnik samodzielnie.

W kolejnym kroku przygotowujemy napis,

który zostanie umieszczony na obrazku. Klasa

StringFormat

przechowuje informacje

dotyczące formatowania napisu. Z kolei
egzemplarz klasy

GraphicsPath

posłuży

nam do umieszczenia tekstu na obrazku.
Jest to bardzo ciekawa klasa, niezwykle
użyteczna przy tworzeniu fragmentów
aplikacji, które obsługują obiekty graficzne.
Za jej pomocą możemy rysować różnorodne
kształty, wypełniać wnętrza figur itd. Kształty,
w tym również tekst, to skończona sekwencja
połączonych ze sobą linii i łuków. Metodą

AddString

dodajemy napis do obiektu

path

. Jej trzeci argument odpowiada za

styl czcionki. Można wybierać pomiędzy
kursywą, pogrubieniem, przekreśleniem i
podkreśleniem. Jak widać na Listingu 2., style
można ze sobą łączyć. Myślę, że pozostałe
argumenty wywołania nie wymagają opisu.

Dla tekstu tworzymy jeszcze nowy

pędzel. Wybrany został na stałe styl

LargeConfetti

– tak, jak w poprzednim

przypadku można przeciążyć metodę

Generate

, aby przyjmowała jako argument

również ten styl. Tekst powinien być
nieznacznie zaburzony. Posłuży do tego
metoda

Warp

klasy

GraphicsPath

. Jedna

z jej wersji przyjmuje cztery argumenty.
Pierwszym z nich są punkty definiujące
czworościan, który będzie zawierał
zmieniony obrazek. Drugi argument
to obrazek źródłowy, trzeci to macierz
przekształcenia – użyta została macierz
zerowa. Ostatnim argumentem jest typ
wyliczeniowy zawierający dwa sposoby
przekształcania:

Bilinear

i

Perspective

.

Proponuję samemu poeksperymentować
z możliwymi transformacjami. Tutaj zostało
użyte dosyć proste przekształcenie z
elementami losowości.

Po tych czynnościach pozostaje nam

tylko wyświetlić przygotowany tekst za
pomocą metody

FillPath

. Do narysowania

tekstu celowo użyty został nieregularny styl
pędzla oraz zaburzenie tekstu. Utrudni to
programom próbującym zdekodować tekst
odnalezienie liter. W kolejnych dwóch pętlach
dodajemy do obrazka losowe zakłócenia. Jest
to konieczne, ponieważ obrazek generowany

zawsze w ten sam sposób byłby łatwy do
oczytania przez programy, które wystarczyłoby
odpowiednio dostosować. Pierwsza pętla
dodaje wypełnione elipsy umieszczone
w przypadkowych miejscach (metoda

FillEllipse

). Za sprawą odpowiednio

dobranych współczynników elipsy pojawią
się na obrazku jako kropki, które nie będą
utrudniały człowiekowi odczytania tekstu z

Listing 2.

Metoda generująca obrazek CAPTCHA

public

Bitmap

Generate

()

{

if

(

width

==

0

||

height

==

0

||

text

==

null

)

throw

new

NullReferenceException

(

"Please select width, height and text

for image"

);

Bitmap

picture

=

new

Bitmap

(

width

,

height

,

PixelFormat

.

Format32bppArgb

);

Graphics

g

=

Graphics

.

FromImage

(

picture

);

g

.

SmoothingMode

=

SmoothingMode

.

AntiAlias

;

Rectangle

r

=

new

Rectangle

(

0

,

0

,

width

,

height

);


HatchBrush

brush

=

new

HatchBrush

(

HatchStyle

.

SmallConfetti

,

Color

.

LightGray

,

Color

.

White

);

g

.

FillRectangle

(

brush

,

r

);

StringFormat

format

=

new

StringFormat

();

format

.

Alignment

=

StringAlignment

.

Center

;

format

.

LineAlignment

=

StringAlignment

.

Center

;

GraphicsPath

path

=

new

GraphicsPath

();

path

.

AddString

(

text

,

FontFamily

.

GenericSerif

,

(

int

)

FontStyle

.

Bold

+

(

int

)

FontStyle

.

Italic

,

fontSize

,

r

,

format

);

brush

=

new

HatchBrush

(

HatchStyle

.

LargeConfetti

,

Color

.

LightGray

,

Color

.

DarkGray

);


float

r1

=

random

.

Next

(

width

/

8

);

float

r2

=

random

.

Next

(

height

/

4

);

PointF

p1

=

new

PointF

(

0

,

0

);

PointF

p2

=

new

PointF

(

width

,

r2

);

PointF

p3

=

new

PointF

(

r1

,

height

);

PointF

p4

=

new

PointF

(

width

,

height

-

r2

);

PointF

[]

points

=

{

p1

,

p2

,

p3

,

p4

}

;

Matrix

m

=

new

Matrix

();

path

.

Warp

(

points

,

r

,

m

,

WarpMode

.

Perspective

);

g

.

FillPath

(

brush

,

path

);

for

(

int

i

=

0

;

i

<

width

/

3

;

i

++)

g

.

FillEllipse

(

brush

,

new

Rectangle

(

random

.

Next

(

0

,

width

)

,

random

.

Next

(

0

,

height

)

,

random

.

Next

(

1

,

width

/

20

)

,

random

.

Next

(

1

,

width

/

20

)));

for

(

int

i

=

0

;

i

<

width

/

22

;

i

++)

g

.

DrawEllipse

(

new

Pen

(

brush

)

,

(

float

)

random

.

Next

(

0

,

width

)

,

(

float

)

random

.

Next

(

0

,

height

)

,

(

float

)

random

.

Next

(

0

,

width

)

,

(

float

)

random

.

Next

(

0

,

height

));


return

picture

;

}

Listing 3.

Strona obrazek.aspx, generująca testowy obrazek

protected

void

Page_Load

(

object

sender

,

EventArgs

e

)

{

string

tekst

=

"Hakin9"

;

Captcha

image

=

new

Captcha

(

150

,

50

,

38

,

tekst

);

image

.

Generate

()

.

Save

(

Response

.

OutputStream

,

System

.

Drawing

.

Imaging

.

ImageForm

at

.

Jpeg

);

Session

.

Add

(

"tekst"

,

tekst

);

}

Rysunek 3

. Wynik skanowania obrazka

CAPTCHA przy pomocy FineReader

background image

18

POCZATKI

HAKIN9 6/2008

TEST CAPTCHA

19

HAKIN9

6/2008

obrazka. Dobrym pomysłem jest również
umieszczenie pewnych nieregularnych
linii, które nachodzą na napis. Jest to duże
utrudnienie dla botów, oczywiście pod
warunkiem, że linie są rysowane losowo. To
zabezpieczenie realizowane jest w drugiej
pętli, która rysuje losowe elipsy z odpowiednio
dobranymi współczynnikami. Na końcu
metody zwracany jest cały obrazek. I to
wszystko. Klasa do generowania obrazków
CAPTCHA jest już gotowa. Oczywiście można
do niej wprowadzić szereg modyfikacji.
Najlepiej umieścić ją w projekcie aplikacji
Windows, gdzie z łatwością możemy ją
przetestować.

Kolejne zadanie to stworzenie testowego

serwisu WWW, w którym klasa Captcha
zostanie użyta praktycznie. Umieszczamy ją w
projekcie strony ASP.NET. Przypomnę tylko, ze
pliki klas najlepiej umieszczać w podkatalogu
App_Code katalogu projektu. Oczywiście
na potrzeby artykułu serwis będzie bardzo
prosty. Będzie składał się z dwóch stron:
Default.aspx i obrazek.aspx. Pierwsza z nich
jest standardową stroną WWW, natomiast
za pomocą drugiej wygenerujemy testowy

obrazek. Obrazek zwracany będzie jako
odpowiedź. Innym rozwiązaniem może być
tymczasowy zapis obrazka na dysku twardym
serwera, jednak przy sporym ruchu mogłoby
się okazać, że na dysku brakuje miejsca do
zapisu. Strona obrazek.aspx musi zawierać
kod obrazka testowego. Odpowiedni kod
umieścimy w metodzie zdarzeniowej

Page _

Load

(Listing 3.). Jest to jedynie przykład i

pod zmienną

tekst

powinno się podstawić

automatycznie wygenerowany ciąg o danej
długości. W artykule Automatyczna generacja
ciągów (Hakin9 5/2008) przedstawiłem jeden
ze sposobów otrzymania takiego ciągu.
Dalej generowany jest obiekt klasy

Captcha

.

Wygenerowany obrazek od razu zamieniamy
na strumień wyjściowy i pakujemy do formatu
JPEG. Na koniec dodajemy jeszcze zmienną
sesji, która przechowywać będzie tekst
umieszczony na obrazku. Strona Default.aspx
zawierać będzie jedynie cztery kontrolki:

Image1

,

TextBox1

,

Button1

i

Label1

(Listing 4.). Pierwsza z nich wyświetlać będzie
obrazek CAPTCHA. Jej własność

ImageUrl

ustawiamy na obrazek.aspx. Po kliknięciu
przycisku Button1 nastąpi sprawdzenie, czy

tekst wprowadzony do kontrolki

TextBox1

jest

taki sam, jak ten przechowywany w zmiennej
sesji

tekst

(Listing 5.). Serwis WWW w akcji

przedstawia Rysunek 2. Przeprowadźmy
jeszcze prosty test wygenerowanego obrazka.
Użyjemy do tego aplikacji FineReader 9.0 w
wersji Professional. Jest to jeden z najbardziej
popularnych programów typu OCR. Jedna z
jego opcji umożliwia skanowanie obrazków
zapisanych w najpopularniejszych formatach.
Obrazek CAPTCHA wygenerowany za
pomocą stworzonej klasy został zapisany na
dysku jako mapa bitowa. Został następnie
wczytany za pomocą programu FineReader,
po czym dokonano próby odczytania tekstu.
Wynik przedstawia Rysunek 3.

Program nie jest w stanie odczytać

tekstu z obrazka. Oczywiście ten test
należy traktować ostrożnie, ponieważ
FineReader jest zoptymalizowany specjalnie
do pracy biurowej. Pokazuje on jednak,
że standardowe algorytmy OCR w tym
przypadku zawodzą.

Problem odpowiedniego zabezpieczenia

wszelakiego rodzaju formularzy
rejestracyjnych przed automatami używanymi
przez spamerów to poważna sprawa.

Podsumowanie

W zeszłym roku ponad 97% wszystkich
wysłanych maili to niechciana poczta.
Automatyczne wpisy reklamowe na
forach internetowych nie przysparzają
ich administratorom chwały. Sondaże
przeprowadzane w Internecie mogą paść
ofiarą botów. Naturalnie test CAPTCHA
nie rozwiąże tych wszystkich problemów,
może jednak zmniejszyć ich skalę.
Dodatkowo możemy używać odpowiednich
filtrów. Przyznam się, że czasem mam
problemy z prawidłowym odczytaniem
tekstu z obrazka. Potrafi to zirytować wielu
użytkowników. Jednak jeśli może to pomóc
w ograniczeniu działalności spamerów,
warto poświęcić temu kilka sekund i nieco
wysilić swoje zmysły.

Listing 4.

Strona Default.aspx

<%

@

Page

Language

=

"C#"

AutoEventWireup

=

"true"

CodeFile

=

"Default.aspx.cs"

Inherits

=

"_

Default"

%>

<!

DOCTYPE

html

PUBLIC

"

-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/

xhtml1/DTD/xhtml1-transitional.dtd">

<

html

xmlns

=

"http://www.w3.org/1999/xhtml"

>

<

head

runat

=

"server"

>

<

title

>

Untitled

Page

<

/

title

>

<

/

head

>

<

body

>

<

form

id

=

"form1"

runat

=

"server"

>

<

div

>

<

asp

:

Image

ID

=

"Image1"

runat

=

"server"

ImageUrl

=

"obrazek.aspx"

/

>&

nbsp

;<

br

/

>

<

asp

:

TextBox

ID

=

"TextBox1"

runat

=

"server"

><

/

asp

:

TextBox

>

<

asp

:

Button

ID

=

"Button1"

runat

=

"server"

OnClick

=

"Button1_Click"

Text

=

"Sprawdź!"

/

>

<

br

/

>

<

asp

:

Label

ID

=

"Label1"

runat

=

"server"

BackColor

=

"White"

Font

-

Bold

=

"True"

Font

-

Size

=

"Larger"

Text

=

"Label"

><

/

asp

:

Label

><

/

div

>

<

/

form

>

<

/

body

>

<

/

html

>

Listing 5.

Metoda zdarzeniowa Button1_Click

protected

void

Button1_Click

(

object

sender

,

EventArgs

e

)

{

string

pass

=

Session

[

"tekst"

]

.

ToString

();

if

(

TextBox1

.

Text

==

pass

)

Label1

.

Text

=

"Nie jesteś botem !"

;

else

Label1

.

Text

=

"Jesteś botem !!!"

;

}

Sławomir Orłowski

Z wykształcenia fizyk. Obecnie jest doktorantem na

Wydziale Fizyki, Astronomii i Informatyki Stosowanej

Uniwersytetu Mikołaja Kopernika w Toruniu. Zajmuje się

symulacjami komputerowymi układów biologicznych

(dynamika molekularna) oraz bioinformatyką.

Programowanie jest nieodzowną częścią jego

pracy naukowej i dydaktycznej. Ma doświadczenie w

programowaniu w językach C, C++, Delphi, Fortran, Java,

C# i Tcl. Współzałożyciel i koordynator grupy .NET WFAiIS.

Jest autorem artykułów i książek informatycznych.

Strona domowa: http://www.fizyka.umk.pl/~bigman/.

Kontakt z autorem: bigman@fizyka.umk.pl


Wyszukiwarka

Podobne podstrony:
2008 06 Virtual machines [Consumer test]
PiKI 2008 06
2008 06 pisemny klucz
2008 06 Java Microedition – metody integracji aplikacji [Inzynieria Oprogramowania]
2008 Odpowiedzi Test przed probna matura Arkusz PR Geografia
06 test prawo
2008 06 pisemny
mat fiz 2008 06 02
Fwd pediatria test 2008 i 2009, Test z pediatrii- 2.02.2009
Fwd pediatria test 2008 i 2009, Test z pediatrii- 2.02.2009
2008 06 05 WHR B DAinstrukcja
2008 06 Edytor grafiki wektorowej Inkscape [Grafika]
Elektronika Praktyczna 2008 06
2008.06.02 prawdopodobie stwo i statystyka
SIMR-AN2-EGZ-2008-06-27a-rozw
2008.06.02 matematyka finansowa

więcej podobnych podstron