This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Vicens\Captcha; |
||
4 | |||
5 | use Illuminate\Support\Facades\Session; |
||
6 | |||
7 | class Captcha |
||
8 | { |
||
9 | /** |
||
10 | * 存储在session中的key |
||
11 | */ |
||
12 | const SESSION_NAME = '_captcha'; |
||
13 | |||
14 | /** |
||
15 | * 验证码配置 |
||
16 | * @var array |
||
17 | */ |
||
18 | protected $config = [ |
||
19 | /** |
||
20 | * 调试模型 |
||
21 | */ |
||
22 | 'debug' => false, |
||
23 | /** |
||
24 | * 默认验证码长度 |
||
25 | * @var int |
||
26 | */ |
||
27 | 'length' => 4, |
||
28 | /** |
||
29 | * 验证码字符集 |
||
30 | * @var string |
||
31 | */ |
||
32 | 'charset' => 'abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ123456789', |
||
33 | /** |
||
34 | * 是否开启严格模式(区分大小写) |
||
35 | * @var bool |
||
36 | */ |
||
37 | 'strict' => false, |
||
38 | /** |
||
39 | * 默认验证码宽度 |
||
40 | * @var int |
||
41 | */ |
||
42 | 'width' => 150, |
||
43 | /** |
||
44 | * 默认验证码高度 |
||
45 | * @var int |
||
46 | */ |
||
47 | 'height' => 40, |
||
48 | /** |
||
49 | * 指定文字颜色 |
||
50 | * @var string |
||
51 | */ |
||
52 | 'textColor' => null, |
||
53 | /** |
||
54 | * 文字字体文件 |
||
55 | * @var string |
||
56 | */ |
||
57 | 'textFont' => null, |
||
58 | /** |
||
59 | * 指定图片背景色 |
||
60 | * @var string |
||
61 | */ |
||
62 | 'backgroundColor' => null, |
||
63 | /** |
||
64 | * 开启失真模式 |
||
65 | * @var bool |
||
66 | */ |
||
67 | 'distortion' => true, |
||
68 | /** |
||
69 | * 最大前景线条数 |
||
70 | * @var int |
||
71 | */ |
||
72 | 'maxFrontLines' => null, |
||
73 | /** |
||
74 | * 最大背景线条数 |
||
75 | * @val int |
||
76 | */ |
||
77 | 'maxBehindLines' => null, |
||
78 | /** |
||
79 | * 文字最大角度 |
||
80 | * @var int |
||
81 | */ |
||
82 | 'maxAngle' => 8, |
||
83 | /** |
||
84 | * 文字最大偏移量 |
||
85 | * @var int |
||
86 | */ |
||
87 | 'maxOffset' => 5 |
||
88 | ]; |
||
89 | |||
90 | /** |
||
91 | * Captcha constructor. |
||
92 | * @param array $config |
||
93 | */ |
||
94 | public function __construct(array $config = []) |
||
95 | { |
||
96 | $this->setConfig($config); |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * 设置验证码配置 |
||
101 | * |
||
102 | * @param array|string $config 配置数组或配置项key |
||
103 | * @param mixed $value 配置项值 |
||
104 | * @return $this |
||
105 | */ |
||
106 | public function setConfig($config, $value = null) |
||
107 | { |
||
108 | |||
109 | if (!is_array($config)) { |
||
110 | $config = [$config => $value]; |
||
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||
111 | } |
||
112 | |||
113 | foreach ($config as $key => $value) { |
||
114 | if (array_key_exists($key, $this->config)) { |
||
115 | $this->config[$key] = $value; |
||
116 | } |
||
117 | } |
||
118 | |||
119 | return $this; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * 获取配置 |
||
124 | * |
||
125 | * @param string|null $key 配置项key |
||
126 | * @return string|number|array |
||
127 | */ |
||
128 | public function getConfig($key = null) |
||
129 | { |
||
130 | if ($key !== null) { |
||
131 | return $this->config[$key]; |
||
132 | } |
||
133 | |||
134 | return $this->config; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * 生成验证码 |
||
139 | * |
||
140 | * @return Image |
||
141 | */ |
||
142 | public function make() |
||
143 | { |
||
144 | $code = $this->generate(); |
||
145 | |||
146 | $hash = password_hash($code, PASSWORD_BCRYPT, array('cost' => 10)); |
||
147 | |||
148 | if ($hash === false) { |
||
149 | throw new \RuntimeException('Bcrypt hashing not supported.'); |
||
150 | } |
||
151 | |||
152 | Session::put(self::SESSION_NAME, $hash); |
||
153 | |||
154 | return new Image($this->build($code)); |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * 仅测试正确性, 不删除验证码 |
||
159 | * |
||
160 | * @param string $input |
||
161 | * @return bool |
||
162 | */ |
||
163 | public function test($input) |
||
164 | { |
||
165 | if ($this->config['debug']) { |
||
166 | return true; |
||
167 | } elseif (!(Session::has(self::SESSION_NAME) && $input)) { |
||
168 | return false; |
||
169 | } |
||
170 | |||
171 | $code = Session::get(self::SESSION_NAME); |
||
172 | |||
173 | if ($this->config['strict']) { |
||
174 | // 开启严格模式 |
||
175 | password_verify($input, $code); |
||
176 | } |
||
177 | |||
178 | //返回验证结果 |
||
179 | return password_verify(strtoupper($input), $code); |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * 检测正确性,并删除验证码 |
||
184 | * |
||
185 | * @param string $input |
||
186 | * @return bool |
||
187 | */ |
||
188 | public function check($input) |
||
189 | { |
||
190 | $result = $this->test($input); |
||
191 | Session::forget(self::SESSION_NAME); |
||
192 | |||
193 | return $result; |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * 生成验证码 |
||
198 | * |
||
199 | * @return string |
||
200 | */ |
||
201 | protected function generate() |
||
202 | { |
||
203 | $characters = str_split($this->getConfig('charset')); |
||
204 | $length = $this->getConfig('length'); |
||
205 | |||
206 | $code = ''; |
||
207 | for ($i = 0; $i < $length; $i++) { |
||
208 | $code .= $characters[rand(0, count($characters) - 1)]; |
||
209 | } |
||
210 | |||
211 | if ($this->config['strict']) { |
||
212 | return $code; |
||
213 | } |
||
214 | |||
215 | return strtoupper($code); |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * 创建验证码图片 |
||
220 | * |
||
221 | * @param string $code |
||
222 | * @return resource |
||
223 | */ |
||
224 | protected function build($code) |
||
225 | { |
||
226 | |||
227 | // 图片宽 |
||
228 | $width = $this->getConfig('width'); |
||
229 | // 图片高 |
||
230 | $height = $this->getConfig('height'); |
||
231 | // 背景颜色 |
||
232 | $backgroundColor = $this->getConfig('backgroundColor'); |
||
233 | |||
234 | // 随机取一个字体 |
||
235 | $font = $this->getTextFont(); |
||
236 | |||
237 | // 根据宽高创建一个背景画布 |
||
238 | $image = imagecreatetruecolor($width, $height); |
||
239 | |||
240 | if ($backgroundColor === null) { |
||
241 | $backgroundColor = imagecolorallocate($image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(200, 255)); |
||
242 | } else { |
||
243 | $color = $backgroundColor; |
||
244 | $backgroundColor = imagecolorallocate($image, $color[0], $color[1], $color[2]); |
||
245 | } |
||
246 | // 填充背景色 |
||
247 | imagefill($image, 0, 0, $backgroundColor); |
||
248 | |||
249 | // 绘制背景干扰线 |
||
250 | $this->drawLines($image, $this->getConfig('maxBehindLines')); |
||
251 | |||
252 | // 写入验证码文字 |
||
253 | $color = $this->renderText($image, $code, $font); |
||
254 | |||
255 | // 绘制前景干扰线 |
||
256 | $this->drawLines($image, $this->getConfig('maxFrontLines'), $color); |
||
257 | |||
258 | if ($this->getConfig('distortion')) { |
||
259 | // 创建失真 |
||
260 | $image = $this->createDistortion($image, $width, $height, $backgroundColor); |
||
261 | } |
||
262 | |||
263 | //如果不指定字体颜色和背景颜色,则使用图像过滤器修饰 |
||
264 | if (function_exists('imagefilter') && is_null($backgroundColor) && is_null($this->getConfig('textColor'))) { |
||
265 | // 颜色翻转 - 1/2几率 |
||
266 | if (mt_rand(0, 1) == 0) { |
||
267 | imagefilter($image, IMG_FILTER_NEGATE); |
||
268 | } |
||
269 | // 用边缘检测来突出图像的边缘 - 1/11几率 |
||
270 | if (mt_rand(0, 10) == 0) { |
||
271 | imagefilter($image, IMG_FILTER_EDGEDETECT); |
||
272 | } |
||
273 | // 改变图像的对比度 |
||
274 | imagefilter($image, IMG_FILTER_CONTRAST, mt_rand(-50, 10)); |
||
275 | |||
276 | if (mt_rand(0, 5) == 0) { |
||
277 | // 用高斯算法和指定颜色模糊图像 |
||
278 | imagefilter($image, IMG_FILTER_COLORIZE, mt_rand(-80, 50), mt_rand(-80, 50), mt_rand(-80, 50)); |
||
279 | } |
||
280 | } |
||
281 | return $image; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * 创建失真 |
||
286 | * |
||
287 | * @param resource $image |
||
288 | * @param int $width |
||
289 | * @param int $height |
||
290 | * @param int $backgroundColor |
||
291 | * @return resource |
||
292 | */ |
||
293 | protected function createDistortion($image, $width, $height, $backgroundColor) |
||
294 | { |
||
295 | //创建失真 |
||
296 | $contents = imagecreatetruecolor($width, $height); |
||
297 | $rWidth = mt_rand(0, $width); |
||
298 | $rHeight = mt_rand(0, $height); |
||
299 | $phase = mt_rand(0, 10); |
||
300 | $scale = 1.1 + mt_rand(0, 10000) / 30000; |
||
301 | |||
302 | for ($x = 0; $x < $width; $x++) { |
||
303 | for ($y = 0; $y < $height; $y++) { |
||
304 | $vX = $x - $rWidth; |
||
305 | $vY = $y - $rHeight; |
||
306 | $vN = sqrt($vX * $vX + $vY * $vY); |
||
307 | |||
308 | if ($vN != 0) { |
||
309 | $vN2 = $vN + 4 * sin($vN / 30); |
||
310 | $nX = $rWidth + ($vX * $vN2 / $vN); |
||
311 | $nY = $rHeight + ($vY * $vN2 / $vN); |
||
312 | } else { |
||
313 | $nX = $rWidth; |
||
314 | $nY = $rHeight; |
||
315 | } |
||
316 | $nY = $nY + $scale * sin($phase + $nX * 0.2); |
||
317 | |||
318 | $pixel = $this->getColor($image, round($nX), round($nY), $backgroundColor); |
||
319 | |||
320 | if ($pixel == 0) { |
||
321 | $pixel = $backgroundColor; |
||
322 | } |
||
323 | |||
324 | imagesetpixel($contents, $x, $y, $pixel); |
||
325 | } |
||
326 | } |
||
327 | |||
328 | return $contents; |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * 获取一个字体 |
||
333 | * |
||
334 | * @return string |
||
335 | */ |
||
336 | protected function getTextFont() |
||
337 | { |
||
338 | // 指定字体 |
||
339 | if ($this->getConfig('textFont') && file_exists($this->getConfig('textFont'))) { |
||
340 | return $this->getConfig('textFont'); |
||
341 | } |
||
342 | // 随机字体 |
||
343 | return __DIR__ . '/../fonts/' . mt_rand(0, 5) . '.ttf'; |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * 写入验证码到图片中 |
||
348 | * |
||
349 | * @param resource $image |
||
350 | * @param string $phrase |
||
351 | * @param string $font |
||
352 | * @return int |
||
353 | */ |
||
354 | protected function renderText($image, $phrase, $font) |
||
355 | { |
||
356 | $length = strlen($phrase); |
||
357 | if ($length === 0) { |
||
358 | return imagecolorallocate($image, 0, 0, 0); |
||
359 | } |
||
360 | |||
361 | // 计算文字尺寸 |
||
362 | $size = $this->getConfig('width') / $length - mt_rand(0, 3) - 1; |
||
363 | $box = imagettfbbox($size, 0, $font, $phrase); |
||
364 | $textWidth = $box[2] - $box[0]; |
||
365 | $textHeight = $box[1] - $box[7]; |
||
366 | $x = ($this->getConfig('width') - $textWidth) / 2; |
||
367 | $y = ($this->getConfig('height') - $textHeight) / 2 + $size; |
||
368 | |||
369 | if (!$this->getConfig('textColor')) { |
||
370 | $textColor = array(mt_rand(0, 150), mt_rand(0, 150), mt_rand(0, 150)); |
||
371 | } else { |
||
372 | $textColor = $this->getConfig('textColor'); |
||
373 | } |
||
374 | $color = imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]); |
||
375 | |||
376 | // 循环写入字符,随机角度 |
||
377 | for ($i = 0; $i < $length; $i++) { |
||
378 | $box = imagettfbbox($size, 0, $font, $phrase[$i]); |
||
379 | $w = $box[2] - $box[0]; |
||
380 | $angle = mt_rand(-$this->getConfig('maxAngle'), $this->getConfig('maxAngle')); |
||
381 | $offset = mt_rand(-$this->getConfig('maxOffset'), $this->getConfig('maxOffset')); |
||
382 | imagettftext($image, $size, $angle, $x, $y + $offset, $color, $font, $phrase[$i]); |
||
383 | $x += $w; |
||
384 | } |
||
385 | |||
386 | return $color; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * 画线 |
||
391 | * |
||
392 | * @param resource $image |
||
393 | * @param int $width |
||
394 | * @param int $height |
||
395 | * @param int|null $color |
||
396 | */ |
||
397 | protected function renderLine($image, $width, $height, $color = null) |
||
398 | { |
||
399 | $color = $color ?: imagecolorallocate($image, mt_rand(100, 255), mt_rand(100, 255), mt_rand(100, 255)); |
||
0 ignored issues
–
show
|
|||
400 | |||
401 | if (mt_rand(0, 1)) { |
||
402 | // 横向 |
||
403 | $xA = mt_rand(0, $width / 2); |
||
404 | $yA = mt_rand(0, $height); |
||
405 | $xB = mt_rand($width / 2, $width); |
||
406 | $yB = mt_rand(0, $height); |
||
407 | } else { |
||
408 | // 纵向 |
||
409 | $xA = mt_rand(0, $width); |
||
410 | $yA = mt_rand(0, $height / 2); |
||
411 | $xB = mt_rand(0, $width); |
||
412 | $yB = mt_rand($height / 2, $height); |
||
413 | } |
||
414 | imagesetthickness($image, mt_rand(1, 3)); |
||
415 | imageline($image, $xA, $yA, $xB, $yB, $color); |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * 画线 |
||
420 | * |
||
421 | * @param resource $image |
||
422 | * @param int $max |
||
423 | * @param int|null $color |
||
424 | */ |
||
425 | protected function drawLines($image, $max, $color = null) |
||
426 | { |
||
427 | $square = $this->getConfig('width') * $this->getConfig('height'); |
||
428 | $effects = mt_rand($square / 3000, $square / 2000); |
||
429 | |||
430 | // 计算线条数 |
||
431 | if ($max != null && $max > 0) { |
||
432 | $effects = min($max, $effects); |
||
433 | } |
||
434 | |||
435 | if ($max !== 0) { |
||
436 | for ($e = 0; $e < $effects; $e++) { |
||
437 | |||
438 | if ($color !== null) { |
||
439 | $this->renderLine($image, $this->getConfig('width'), $this->getConfig('height'), $color); |
||
440 | } else { |
||
441 | $this->renderLine($image, $this->getConfig('width'), $this->getConfig('height')); |
||
442 | } |
||
443 | |||
444 | } |
||
445 | } |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * 获取颜色 |
||
450 | * |
||
451 | * @param resource $image |
||
452 | * @param int $width |
||
453 | * @param int $height |
||
454 | * @param int $background |
||
455 | * @return int |
||
456 | */ |
||
457 | protected function getColor($image, $width, $height, $background) |
||
458 | { |
||
459 | $sWidth = imagesx($image); |
||
460 | $sHeight = imagesy($image); |
||
461 | if ($width < 0 || $width >= $sWidth || $height < 0 || $height >= $sHeight) { |
||
462 | return $background; |
||
463 | } |
||
464 | |||
465 | return imagecolorat($image, $width, $height); |
||
466 | } |
||
467 | |||
468 | /** |
||
469 | * @param string $name |
||
470 | * @param array $arguments |
||
471 | * @return $this |
||
472 | */ |
||
473 | public function __call($name, $arguments) |
||
474 | { |
||
475 | if (array_key_exists($name, $this->config)) { |
||
476 | $this->config[$name] = $arguments[0]; |
||
477 | } |
||
478 | |||
479 | return $this; |
||
480 | } |
||
481 | } |
||
482 |