├── PHP
├── purecaptcha_img.php
├── purecaptcha_usage.php
└── purecaptcha.php
└── README.md
/PHP/purecaptcha_img.php:
--------------------------------------------------------------------------------
1 | show();
--------------------------------------------------------------------------------
/PHP/purecaptcha_usage.php:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PureCaptcha
2 | ===========
3 |
4 | OWASP PureCaptcha project aims to help developers use easy-to-user independent CAPTCHAs in their project with zero hassle.
5 | Traditionally, using CAPTCHAs required making accounts in a third party provider and using rigorous APIs, or installing many libraries
6 | and then using text rendering and image modification libraries to render a CAPTCHA image.
7 |
8 | PureCaptcha on the other hand, aims to do all of the above in one step. It includes very simple code to render a few ASCII characters
9 | into bitmap images, and code to modify, scale, rotate and distort simple bitmap images. It then outputs the CAPTCHA as a simple
10 | monochrome BMP image (which has a very small size).
11 |
12 | It also provides example usages for CAPTCHAs. It is of utmost importance to properly use CAPTCHAs, otherwise side-channel and logical
13 | attacks would be viable on the system.
14 |
15 | Read more about OWASP PureCaptcha at https://www.owasp.org/index.php/OWASP_PureCaptcha
16 |
17 |
18 | ##Usage##
19 |
20 | **Basic usage**
21 |
22 | Use the following script as the source of the captcha image.
23 |
24 | require_once "purecaptcha.php";
25 |
26 | $pureCaptcha = new PureCaptcha();
27 |
28 | $captcha = $pureCaptcha->show();
29 |
30 | Store `$captcha` in the session and compare it user input later.
31 |
32 | **Complete API**
33 |
34 | **captcha** : The text to be displayed in captcha
35 | **length** : The length of the captcha text
36 |
37 | **spacing** : The spacing between two characters in the captcha
38 |
39 | **degree** : The degree of rotation of captcha text in clockwise direcion
40 |
41 | **scaleX** : Amount to be scaled in x direction
42 |
43 | **scaleY** : Amount to be scaled in y direction
44 |
45 | **noisePercent** : The noise percentage
46 |
47 | // Getters and setters are provided for each
48 | $pureCaptcha->setCaptcha("M2H7");
49 | $captha = $pureCaptcha->getCaptcha();
--------------------------------------------------------------------------------
/PHP/purecaptcha.php:
--------------------------------------------------------------------------------
1 | ascii=unserialize(gzuncompress(base64_decode(
25 | preg_replace('/\s+/', '', $this->ascii))));
26 | $this->text=$this->randomText();
27 |
28 | }
29 |
30 | /**
31 | * Contains the captcha text.
32 | * @var string
33 | */
34 | public $text;
35 |
36 | /**
37 | * Generates random text for use in captcha
38 | * @param integer $length
39 | * @return string
40 | */
41 | protected function randomText($length=4)
42 | {
43 | $res="";
44 | for ($i=0;$i<$length;++$i)
45 | $res.=$this->chars[mt_rand(0,strlen($this->chars)-1)];
46 | return $res;
47 | }
48 | /**
49 | * returns the index of a char in $chars array
50 | */
51 | protected function asciiEntry($char)
52 | {
53 | for ($i=0;$ichars);++$i)
54 | if ($this->chars[$i]==$char) return $i;
55 | return -1;
56 |
57 | }
58 | /**
59 | * converts a text to a bitmap
60 | * which is a 2D array of ones and zeroes denoting the text
61 | */
62 | protected function textBitmap($text,$spacing=2)
63 | {
64 | $width=$this->charWidth;
65 | $height=$this->charHeight;
66 | $result=array();
67 | $baseY=$baseX=0;
68 |
69 | for ($index=0;$indexascii[$this->asciiEntry($text[$index])][$j][$i];
76 | for ($i=0;$i<$spacing;++$i)
77 | $result[$baseY+$j][$baseX+$width+$i]=0;
78 | }
79 | $baseX+=$width+$spacing;
80 | }
81 | return $result;
82 | }
83 | /**
84 | * displays a bitmap string on the browser screen
85 | */
86 | protected function displayBitmap($bitmap)
87 | {
88 | header("Content-Type: image/bmp");
89 | echo $this->bitmap2bmp($bitmap);
90 | }
91 | /**
92 | * generates a monochrome BMP file
93 | * a bitmap needs to be sent to this function
94 | * i.e a 2D array with every element being either 1 or 0
95 | * @param integer $width
96 | * @param integer $height
97 | * @param array $bitmap
98 | * @return string
99 | */
100 | protected function bitmap2bmp($bitmap)
101 | {
102 | $width=count($bitmap[0]);
103 | $height=count($bitmap);
104 | $bytemap=$this->bitmap2bytemap($bitmap);
105 |
106 | $rowSize=floor(($width+31)/32)*4;
107 | $size=$rowSize*$height + 62; //62 metadata size
108 | #bitmap header
109 | $data= "BM"; //header
110 | $data.= (pack('V',$size)); //bitmap size ,4 bytes unsigned little endian
111 | $data.= "RRRR";
112 | $data.= (pack('V',14+40+8)); //bitmap data start offset ,
113 | //4 bytes unsigned little endian, 14 forced, 40 header, 8 colors
114 |
115 | #info header
116 | $data.= pack('V',40); //bitmap header size (min 40),
117 | //4 bytes unsigned little-endian
118 | $data.= (pack('V',$width)); //bitmap width , 4 bytes signed integer
119 | $data.= (pack('V',$height)); //bitmap height , 4 bytes signed integer
120 | $data.= (pack('v',1)); //number of colored plains , 2 bytes
121 | $data.= (pack('v',1)); //color depth , 2 bytes
122 | $data.= (pack('V',0)); //compression algorithm , 4 bytes (0=none, RGB)
123 | $data.= (pack('V',0)); //size of raw data, 0 is fine for no compression
124 | $data.= (pack('V',11808)); //horizontal resolution (dpi), 4 bytes
125 | $data.= (pack('V',11808)); //vertical resolution (dpi), 4 bytes
126 | $data.= (pack('V',0)); //number of colors in pallette (0 = all), 4 bytes
127 | $data.= (pack('V',0)); //number of important colors (0 = all), 4 bytes
128 |
129 | #color palette
130 | $data.= (pack('V',0x00FFFFFF)); //next color, white
131 | $data.= (pack('V',0)); //first color, black
132 |
133 | for ($j=$height-1;$j>=0;--$j)
134 | for ($i=0;$i<$rowSize/4;++$i)
135 | for ($k=0;$k<4;++$k)
136 | if (isset($bytemap[$j][$i*4+$k]))
137 | $data.= pack('C',$bytemap[$j][$i*4+$k]);
138 | else
139 | $data.= pack('C',0);
140 | return $data;
141 | }
142 | /**
143 | * Converts a bitmap to a bytemap, which is necessary for outputting it
144 | *
145 | */
146 | protected function bitmap2bytemap($bitmap)
147 | {
148 | $width=count($bitmap[0]);
149 | $height=count($bitmap);
150 | $bytemap=array();
151 | for ($j=0;$j<$height;++$j)
152 | {
153 | for ($i=0;$i<$width/8;++$i)
154 | {
155 | $bitstring="";
156 | for ($k=0;$k<8;++$k)
157 | if (isset($bitmap[$j][$i*8+$k]))
158 | $bitstring.=$bitmap[$j][$i*8+$k];
159 | else
160 | $bitstring.="0";
161 | $bytemap[$j][]=bindec($bitstring);
162 | }
163 | }
164 | return $bytemap;
165 | }
166 | /**
167 | * rotates a bitmap, returning new dimensions with the bitmap
168 | * return bitmap
169 | */
170 | protected function rotateBitmap($bitmap, $degree)
171 | {
172 | $c=cos(deg2rad($degree));
173 | $s=sin(deg2rad($degree));
174 |
175 | $width=count($bitmap[0]);
176 | $height=count($bitmap);
177 | $newHeight=round(abs($width*$s)+abs($height*$c));
178 | $newWidth=round(abs($width*$c) + abs($height*$s))+1;
179 | $x0 = $width/2 - $c*$newWidth/2 - $s*$newHeight/2;
180 | $y0 = $height/2 - $c*$newHeight/2 + $s*$newWidth/2;
181 | $result=array_fill(0, $newHeight, array_fill(0, $newWidth, 0));
182 | for ($j=0;$j<$newHeight;++$j)
183 | for ($i=1;$i<$newWidth;++$i)
184 | {
185 | $y=(int)(-$s*$i+$c*$j+$y0);
186 | $x=(int)($c*$i+$s*$j+$x0);
187 | if (isset($bitmap[$y][$x]))
188 | $result[$j][$i]=$bitmap[$y][$x];
189 | }
190 | return $result;
191 | }
192 | /**
193 | * scales a bitmap to be bigger
194 | */
195 | protected function scaleBitmap($bitmap,$scaleX,$scaleY)
196 | {
197 | $width=count($bitmap[0]);
198 | $height=count($bitmap);
199 | $newHeight=$height*$scaleY;
200 | $newWidth=$width*$scaleX;
201 | $result=array_fill(0, $newHeight, array_fill(0, $newWidth, 0));
202 | for ($j=0;$j<$newHeight;++$j)
203 | for ($i=0;$i<$newWidth;++$i)
204 | $result[$j][$i]=$bitmap[(int)($j/$scaleY)]
205 | [(int)($i/$scaleX)];
206 | return $result;
207 | }
208 | /**
209 | * adds random noise to the captcha
210 | */
211 | protected function distort($bitmap,$noisePercent=5)
212 | {
213 | for ($j=0;$jtextBitmap($this->text);
226 | $degree=mt_rand(2,4);
227 | if (mt_rand()%100<50)
228 | $degree=-$degree;
229 | $bitmap=$this->rotateBitmap($bitmap,$degree);
230 | $bitmap=$this->scaleBitmap($bitmap,$scale,$scale);
231 | if ($distort) $bitmap=$this->distort($bitmap);
232 | $this->displayBitmap($bitmap);
233 | return $this->text;
234 | }
235 |
236 | }
237 |
--------------------------------------------------------------------------------