diff --git a/application/config/autoload.php b/application/config/autoload.php index fa6ace1e8..e9566e74e 100644 --- a/application/config/autoload.php +++ b/application/config/autoload.php @@ -66,7 +66,7 @@ | $autoload['helper'] = array('url', 'file'); */ -$autoload['helper'] = ['url', 'audit', 'date', 'dd', 'validation', 'general']; +$autoload['helper'] = ['url', 'audit', 'date', 'dd', 'validation', 'general', 'captcha']; /* diff --git a/application/controllers/Mine.php b/application/controllers/Mine.php index a2a60e676..6da29a345 100644 --- a/application/controllers/Mine.php +++ b/application/controllers/Mine.php @@ -805,6 +805,8 @@ public function cadastrar() if ($this->form_validation->run('clientes') == false) { $this->data['custom_error'] = (validation_errors() ? '
' . validation_errors() . '
' : false); + } elseif (strtolower($this->input->post('captcha')) != strtolower($this->session->userdata('captchaWord'))) { + $this->session->set_flashdata('error', 'Os caracteres da imagem não foram preenchidos corretamente ou o tempo máximo para preenchimento foi ultrapassado!'); } else { $data = [ 'nomeCliente' => set_value('nomeCliente'), @@ -834,7 +836,36 @@ public function cadastrar() $this->session->set_flashdata('error', 'Falha ao realizar cadastro!'); } } - $data = ''; + + $arrFont = ['ZXX_Noise.otf', 'karabine.ttf', 'capture.ttf', 'captcha.ttf']; + shuffle($arrFont); + + $vals = [ + 'img_path' => './assets/captcha-images/', + 'img_url' => base_url().'assets/captcha-images/', + 'font_path' => './assets/font/'.$arrFont[0], + 'img_width' => 150, + 'img_height' => 45, + 'expiration' => 7200, + 'word_length' => 6, + 'font_size' => 20, + 'img_id' => 'Imageid', + 'pool' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'colors' => [ + 'background' => [255, 255, 255], + 'border' => [255, 255, 255], + 'text' => [0, 0, 0], + 'grid' => [255, 40, 40], + ] + ]; + + $cap = create_captcha($vals); + $this->session->set_userdata('captchaWord', $cap['word']); + + $data = [ + 'captchaImage' => $cap['image'] + ]; + $this->load->view('conecte/cadastrar', $data); } diff --git a/application/helpers/captcha_helper.php b/application/helpers/captcha_helper.php new file mode 100644 index 000000000..9fcbd1b2d --- /dev/null +++ b/application/helpers/captcha_helper.php @@ -0,0 +1,353 @@ + '', + 'img_path' => '', + 'img_url' => '', + 'img_width' => '150', + 'img_height' => '30', + 'font_path' => '', + 'expiration' => 7200, + 'word_length' => 8, + 'font_size' => 16, + 'img_id' => '', + 'pool' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'colors' => array( + 'background' => array(255,255,255), + 'border' => array(153,102,102), + 'text' => array(204,153,153), + 'grid' => array(255,182,182) + ) + ); + + foreach ($defaults as $key => $val) + { + if ( ! is_array($data) && empty($$key)) + { + $$key = $val; + } + else + { + $$key = isset($data[$key]) ? $data[$key] : $val; + } + } + + if ( ! extension_loaded('gd')) + { + log_message('error', 'create_captcha(): GD extension is not loaded.'); + return FALSE; + } + + if ($img_path === '' OR $img_url === '') + { + log_message('error', 'create_captcha(): $img_path and $img_url are required.'); + return FALSE; + } + + if ( ! is_dir($img_path) OR ! is_really_writable($img_path)) + { + log_message('error', "create_captcha(): '{$img_path}' is not a dir, nor is it writable."); + return FALSE; + } + + // ----------------------------------- + // Remove old images + // ----------------------------------- + + $now = microtime(TRUE); + + $current_dir = @opendir($img_path); + while ($filename = @readdir($current_dir)) + { + if (in_array(substr($filename, -4), array('.jpg', '.png')) + && (str_replace(array('.jpg', '.png'), '', $filename) + $expiration) < $now) + { + @unlink($img_path.$filename); + } + } + + @closedir($current_dir); + + // ----------------------------------- + // Do we have a "word" yet? + // ----------------------------------- + + if (empty($word)) + { + $word = ''; + $pool_length = strlen($pool); + $rand_max = $pool_length - 1; + + // PHP7 or a suitable polyfill + if (function_exists('random_int')) + { + try + { + for ($i = 0; $i < $word_length; $i++) + { + $word .= $pool[random_int(0, $rand_max)]; + } + } + catch (Exception $e) + { + // This means fallback to the next possible + // alternative to random_int() + $word = ''; + } + } + } + + if (empty($word)) + { + // Nobody will have a larger character pool than + // 256 characters, but let's handle it just in case ... + // + // No, I do not care that the fallback to mt_rand() can + // handle it; if you trigger this, you're very obviously + // trying to break it. -- Narf + if ($pool_length > 256) + { + return FALSE; + } + + // We'll try using the operating system's PRNG first, + // which we can access through CI_Security::get_random_bytes() + $security = get_instance()->security; + + // To avoid numerous get_random_bytes() calls, we'll + // just try fetching as much bytes as we need at once. + if (($bytes = $security->get_random_bytes($pool_length)) !== FALSE) + { + $byte_index = $word_index = 0; + while ($word_index < $word_length) + { + // Do we have more random data to use? + // It could be exhausted by previous iterations + // ignoring bytes higher than $rand_max. + if ($byte_index === $pool_length) + { + // No failures should be possible if the + // first get_random_bytes() call didn't + // return FALSE, but still ... + for ($i = 0; $i < 5; $i++) + { + if (($bytes = $security->get_random_bytes($pool_length)) === FALSE) + { + continue; + } + + $byte_index = 0; + break; + } + + if ($bytes === FALSE) + { + // Sadly, this means fallback to mt_rand() + $word = ''; + break; + } + } + + list(, $rand_index) = unpack('C', $bytes[$byte_index++]); + if ($rand_index > $rand_max) + { + continue; + } + + $word .= $pool[$rand_index]; + $word_index++; + } + } + } + + if (empty($word)) + { + for ($i = 0; $i < $word_length; $i++) + { + $word .= $pool[mt_rand(0, $rand_max)]; + } + } + elseif ( ! is_string($word)) + { + $word = (string) $word; + } + + // ----------------------------------- + // Determine angle and position + // ----------------------------------- + $length = strlen($word); + $angle = ($length >= 6) ? mt_rand(-($length-6), ($length-6)) : 0; + $x_axis = mt_rand(6, (360/$length)-16); + $y_axis = ($angle >= 0) ? mt_rand($img_height, $img_width) : mt_rand(6, $img_height); + + // Create image + // PHP.net recommends imagecreatetruecolor(), but it isn't always available + $im = function_exists('imagecreatetruecolor') + ? imagecreatetruecolor($img_width, $img_height) + : imagecreate($img_width, $img_height); + + // ----------------------------------- + // Assign colors + // ---------------------------------- + + is_array($colors) OR $colors = $defaults['colors']; + + foreach (array_keys($defaults['colors']) as $key) + { + // Check for a possible missing value + is_array($colors[$key]) OR $colors[$key] = $defaults['colors'][$key]; + $colors[$key] = imagecolorallocate($im, $colors[$key][0], $colors[$key][1], $colors[$key][2]); + } + + // Create the rectangle + ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $colors['background']); + + // ----------------------------------- + // Create the spiral pattern + // ----------------------------------- + $theta = 1; + $thetac = 7; + $radius = 16; + $circles = 20; + $points = 32; + + for ($i = 0, $cp = ($circles * $points) - 1; $i < $cp; $i++) + { + $theta += $thetac; + $rad = $radius * ($i / $points); + $x = ($rad * cos($theta)) + $x_axis; + $y = ($rad * sin($theta)) + $y_axis; + $theta += $thetac; + $rad1 = $radius * (($i + 1) / $points); + $x1 = ($rad1 * cos($theta)) + $x_axis; + $y1 = ($rad1 * sin($theta)) + $y_axis; + imageline($im, $x, $y, $x1, $y1, $colors['grid']); + $theta -= $thetac; + } + + // ----------------------------------- + // Write the text + // ----------------------------------- + + $use_font = ($font_path !== '' && file_exists($font_path) && function_exists('imagettftext')); + if ($use_font === FALSE) + { + ($font_size > 5) && $font_size = 5; + $x = mt_rand(0, $img_width / ($length / 3)); + $y = 0; + } + else + { + ($font_size > 30) && $font_size = 30; + $x = mt_rand(0, $img_width / ($length / 1.5)); + $y = $font_size + 2; + } + + for ($i = 0; $i < $length; $i++) + { + if ($use_font === FALSE) + { + $y = mt_rand(0 , $img_height / 2); + imagestring($im, $font_size, $x, $y, $word[$i], $colors['text']); + $x += ($font_size * 2); + } + else + { + $y = mt_rand($img_height / 2, $img_height - 3); + imagettftext($im, $font_size, $angle, $x, $y, $colors['text'], $font_path, $word[$i]); + $x += $font_size; + } + } + + // Create the border + imagerectangle($im, 0, 0, $img_width - 1, $img_height - 1, $colors['border']); + + // ----------------------------------- + // Generate the image + // ----------------------------------- + $img_url = rtrim($img_url, '/').'/'; + + if (function_exists('imagejpeg')) + { + $img_filename = $now.'.jpg'; + imagejpeg($im, $img_path.$img_filename); + } + elseif (function_exists('imagepng')) + { + $img_filename = $now.'.png'; + imagepng($im, $img_path.$img_filename); + } + else + { + return FALSE; + } + + $img = ' '; + ImageDestroy($im); + + return array('word' => $word, 'time' => $now, 'image' => $img, 'filename' => $img_filename); + } +} diff --git a/application/views/conecte/cadastrar.php b/application/views/conecte/cadastrar.php index ce92d772a..8b8613f4c 100644 --- a/application/views/conecte/cadastrar.php +++ b/application/views/conecte/cadastrar.php @@ -78,10 +78,9 @@
Cadastre-se no Sistema
-
- -
- + +
+
@@ -135,7 +134,6 @@
-
@@ -177,15 +175,25 @@
-
-
-
-
- - Acessar +
+ +
+
+ +
+ +
+
+
+ +
+
+
+ + Acessar +
-
@@ -253,6 +261,9 @@ }, cep: { required: true + }, + captcha: { + required: true } }, messages: { @@ -289,8 +300,10 @@ }, cep: { required: 'Campo Requerido.' + }, + captcha: { + required: 'Campo Requerido.' } - }, errorClass: "help-inline", diff --git a/assets/captcha-images/index.html b/assets/captcha-images/index.html new file mode 100644 index 000000000..7f684dff4 --- /dev/null +++ b/assets/captcha-images/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + diff --git a/assets/font/ZXX_Noise.otf b/assets/font/ZXX_Noise.otf new file mode 100644 index 000000000..c48b02a90 Binary files /dev/null and b/assets/font/ZXX_Noise.otf differ diff --git a/assets/font/captcha.ttf b/assets/font/captcha.ttf new file mode 100644 index 000000000..8cfa4bc9d Binary files /dev/null and b/assets/font/captcha.ttf differ diff --git a/assets/font/capture.ttf b/assets/font/capture.ttf new file mode 100644 index 000000000..041b3e388 Binary files /dev/null and b/assets/font/capture.ttf differ diff --git a/assets/font/karabine.ttf b/assets/font/karabine.ttf new file mode 100644 index 000000000..5c3d35c8c Binary files /dev/null and b/assets/font/karabine.ttf differ