Download the PHP package gts-meghni/laravel-captcha without Composer

On this page you can find all versions of the php package gts-meghni/laravel-captcha. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-captcha

Laravel Captcha

Packagist PHP from Packagist Laravel versions GitHub Workflow Status (main) Total Downloads

Classic image-based CAPTCHA generation and validation for Laravel applications.

Invisible proof of work, plus a distorted-text challenge rendered server-side and streamed from a URL rather than inlined as base64, so it survives a strict content security policy. No database table of its own.

Requirements

PHP 8.3+, Laravel 12 or 13, and either the gd or imagick extension.

driver names which of the two to use, and is only consulted when the application has not already bound its own Intervention ImageManager; if it has, that binding wins and this setting is ignored. wave is GD only.

Installation

Or individually. Config, translations, and the fonts and background textures, which are read from the package by default, so publish them only to add your own:

The browser half lives in its own package, GTS-MEGHNI/laravel-captcha-js:

It solves the proof of work, fetches image challenges and ships React and Next.js hooks, against the same endpoints this package serves. Nothing requires it, since the endpoints are plain JSON, but it saves reimplementing the hash loop.

What this actually protects against

Read this before choosing settings, because the honest answer shapes them.

No captcha stops a determined attacker. A multimodal model reads distorted text about as well as a person; a solving service costs a dollar per thousand. What is achievable is making automation expensive per attempt and capped per identity. That is what this package does, with two mechanisms that fail to different attackers:

Image challenge Proof of work
Asks the user for effort Yes, read and type No, invisible
Asks the machine for effort No Yes, every attempt
Beaten by a vision model Yes Irrelevant, nothing to look at
Beaten by native or GPU hashing Irrelevant Yes, at a low difficulty

What those two rows mean

"Beaten by a vision model." The image challenge asks a question whose answer is visible in the picture. Anything that can see can therefore answer it, and software can now see. Screenshot the image, hand it to a multimodal model, type back what it returns.

This is not a projection. A model read the default preset here at 40% first time: 4 of 10 challenges exactly right, 87% of individual characters, with no preprocessing and no training. Tesseract, the classic OCR the distortion was designed against, managed 0 of 100 on the same renderer. The defence works perfectly against the attacker of ten years ago and not at all against the one that exists now.

And 40% understates it, because a bot can retry for nothing. Unsure about a character? Discard the challenge, ask for another, try again. Three attempts at 40% succeed 78% of the time. That is why the proof of work now charges for requests as well as failures (pow.volume): retrying has to cost something, or partial accuracy is as good as perfect accuracy.

"Irrelevant, nothing to look at." The proof of work has no image, no question and no answer to recognise. The client is asked to spend CPU: find a number whose hash starts with a run of zeros. A model that reads images has nothing to read, and being cleverer does not help. There is no shortcut through a hash, only guessing. Vision is simply the wrong tool, the way a lockpick is the wrong tool against a wall.

Which is why the row below it flips. The proof of work is beaten, by an attacker who reimplements the hash outside a browser: one CPU core in plain JavaScript manages 443 solves a minute at difficulty 16, and native or GPU code far more. That is what escalating difficulty is for, and it is why running both mechanisms is worth more than either, since the attacker who defeats one is not the attacker who defeats the other.

Run proof of work everywhere; it costs users nothing. Add the image only where a successful attempt is expensive to you, or only once a caller starts failing. Neither replaces per-account lockout, and neither replaces asking for something scarce, such as a verified phone number or a national ID, where fake accounts are the concern.

Proof of work

The server issues a random salt. The client must find a number, a nonce, such that sha256(salt + nonce) begins with a run of zero bits. There is no shortcut, only guessing, so it costs CPU. The server confirms with a single hash.

Difficulty is in bits: 16 bits means the digest must open with 16 zero bits, or four hexadecimal zeros, which takes about 65,000 guesses on average. Each extra bit doubles the work.

The salt is server-chosen and single-use. That is what stops one solution being computed once and replayed forever, and it is why the client cannot supply its own: it would supply one it had already solved.

In the browser, through the companion package:

React and Next.js get hooks instead:

In a FormRequest:

Difficulty follows suspicion

A single difficulty cannot serve both a first-time visitor and a script on its five hundredth attempt. Two counters raise it, and both matter:

Failures Difficulty Cost to a person Attacker throughput, one core
0 16 ~45ms, unnoticed 443 solves/min
1 18 ~250ms 111 solves/min
2 20 ~1s 22 solves/min
4 24 ~17s 1.4 solves/min

The difficulty issued with a challenge is stored alongside it, so raising the floor mid-flight never invalidates work already done.

Image challenge

expires_in is seconds, following RFC 6749; expires_at is the same moment as an ISO-8601 stamp. Prefer expires_in for a countdown, because a device clock that disagrees with the server cannot throw it off.

The image streams from a URL rather than being inlined as a data: URI, so a front end under img-src 'self' can display it. It is drawn when requested, not when the token is issued, and redrawn deterministically from a stored seed. A refetch returns identical bytes, and a challenge never displayed costs no drawing work at all.

Or guard a route wholesale with the captcha middleware. Field names come from config, so an existing payload needs no front-end change:

One preset ships: default, six characters, case-insensitive. Register your own challenge type on the manager and point a preset at it:

Single use, and only plausible answers

A token is single-use. Any verification consumes it, wrong answers included, and consumption happens under a cache lock. Laravel's pull() is a get() followed by a forget(), so without the lock two simultaneous submissions could both spend one solved challenge.

Two further checks:

min_seconds costs nothing and removes scripts that answer in milliseconds. It does not stop one that waits on purpose.

bind_ip closes the solving-service route, where a page forwards the image out and the answer returns from another machine. It is off by default because a phone moving from wifi to mobile data mid-form changes address and would be refused through no fault of its own. Turn it on where clients are stable. It applies to the proof of work as well.

Behind a load balancer, configure TrustProxies first

Everything above that reads an address reads $request->ip(): bind_ip, the proof-of-work binding, the throttles on all three endpoints, and the volume counter when it is keyed on an address. Behind a proxy or load balancer with Laravel's TrustProxies unconfigured, every request reports the proxy's address instead of the client's.

That fails silently and in the wrong direction:

Configure trusted proxies before turning bind_ip on, and confirm with Request::ip() in a real request rather than assuming.

Resisting OCR

Thresholding an image to strip its background is one operation and works on any texture, so the package leans on distortion that cannot be separated from the glyphs: overlap (touching characters defeat the segmentation OCR runs before recognition), wave (sine shear across the finished image, GD only), speckle with tone: ink (drawn from the glyph palette, so a luminance threshold cannot drop it without dropping the glyphs), and per-glyph angle and jitter.

Prefer adding characters over adding distortion. More glyphs multiply the error rate without making any single character harder for a person.

Previewing a preset

Settings are easier to judge by eye than by reading. captcha:preview writes samples to disk without a browser or a running front end:

--path writes somewhere else. Each run redraws from fresh seeds, so a handful of samples shows the spread a preset produces rather than one lucky draw.

The answers are not printed, because only their digests are stored. Drawing goes through the same cache as a live challenge, so a preview that reports the challenge expired is telling you the store is misconfigured.

Escalating only when there is a reason

A captcha on every login taxes every legitimate user daily to inconvenience a bot a vision model already defeats. Ask after a couple of failures instead:

Return 'captcha_required' => Captcha::requiredFor($key) from a failed attempt so the front end knows to render the image next time rather than guessing.

Key on the identity under attack, not the address it arrives from: an office behind one NAT shares an IP, and a botnet does not. The same counter raises the proof-of-work difficulty, so an attacker cannot dodge one defence without the other.

Storage

A challenge lives in the cache for expire seconds and holds only the preset name, a render seed, a SHA-256 digest of the answer, the issue time and optionally the issuing address. That is a couple of hundred bytes, and no table of its own. cache.store defaults to the application's default store. Two requirements decide whether a store works:

Shared across workers Required. The token is issued by one request and the image drawn by another. An array store, or a file store local to one container, hands out URLs that 404 on the next request.
Expires on its own Preferred. Laravel removes an expired entry only when something reads it, and an abandoned challenge is never read again.

Redis evicts by TTL and leaves nothing behind. The file and database stores accumulate one entry per abandoned challenge, files against an inode quota and rows in the cache table, so schedule the sweep:

A no-op on Redis and memcached, so it is safe to schedule everywhere. On the database store it deletes only rows carrying the captcha prefix; on a file store it sweeps every expired entry in that directory, because a file store hashes its keys and entries cannot be attributed, which is the reason to give the captcha a store of its own.

Prefer a dedicated store or connection over sharing the application's. A php artisan cache:clear on a shared store deletes every in-flight challenge, and every user mid-form is told their answer is wrong.

Localisation

English, French and Arabic ship for the validation messages. The challenge itself never localises. Latin characters and Western digits in every locale, so one keyboard layout answers any challenge.

Measured results

Numbers, not intentions. Re-run these after any change to rendering or difficulty.

Against Tesseract 5.3.4, 100 challenges, attacked raw, then greyscaled and thresholded and upscaled 3×, then again with a character whitelist and single-line page mode:

Corpus Exact match Per-character
Control, every defence off, black on white 40/50 (80%) 88%
default 0/100 3.3%

The control proves the measurement: the same renderer with defences off is read at 80%, so the zeros describe the defences rather than a broken harness. Read them as "below roughly 3%", since zero of 100 puts the 95% upper bound near there.

Against a multimodal model, 10 challenges read blind, answers committed before scoring:

Corpus Exact match Per-character
default 4/10 (40%) 87%

This is the number that matters, and why proof of work leads: classic OCR is defeated by the image, a vision model is not.

Note too that a bot can discard a low-confidence read and ask for another challenge, so effective accuracy approaches 100% given free retries, which is what pow.volume is there to price.

Attacker throughput, one CPU core, plain JavaScript:

Difficulty Solves per minute
16 443
20 22
24 1.4

At a flat 16 bits the endpoint throttle (60/min) binds long before the work does, which is precisely why difficulty escalates.

Limits

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Thank you for considering contributing to Laravel Captcha! Please review our contributing guide to get started.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

Laravel Captcha is open-sourced software licensed under the MIT license.


All versions of laravel-captcha with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
illuminate/support Version ^12.0||^13.0
intervention/image Version ^4.2
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package gts-meghni/laravel-captcha contains the following files

Loading the files please wait ...