├── 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 |
23 | 24 | 25 | 27 |
28 | 29 | 30 |
-------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------