Add recaptcha

This commit is contained in:
Sebastien Hattry 2023-02-23 11:26:03 +00:00
parent 451a69151f
commit 718bfc11eb
9 changed files with 204 additions and 24 deletions

View file

@ -70,6 +70,11 @@ MESBR_COUPON_3_CHARCUT=""
ANALYTICS_GOOGLE_ID=
GOOGLE_MAP_API_KEY=
# Google Recaptcha
GOOGLE_RECAPTCHA_PUBLIC=
GOOGLE_RECAPTCHA_PRIVATE=
GOOGLE_RECAPTCHA_VERSION=
## MERCANET
MERCANET_SECRET=
MERCANET_MERCHAND_ID=

View file

@ -3,19 +3,38 @@
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
class SecureHeaders
{
// Enumerate headers which you do not want in your application's responses.
// Great starting point would be to go check out @Scott_Helme's:
// https://securityheaders.com/
/**
* Enumerate headers which you do not want in your application's responses.
* Great starting point would be to go check out @Scott_Helme's:
* https://securityheaders.com/
*
* @var array
*/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
public function handle($request, Closure $next)
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
View::share('nonce', $nonce = \uniqid());
// * For VueJS dev tool to execute
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-$nonce'";
$reportUri = Route::has('cspReportUri') ?
\sprintf('report-uri %s', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
$this->removeUnwantedHeaders($this->unwantedHeaderList);
$response = $next($request);
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
@ -24,19 +43,40 @@ class SecureHeaders
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
$cspPolicies = [
'frame-src ' . implode(" ", [
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/'
]) . ';',
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com;",
// phpcs:ignore Generic.Files.LineLength.TooLong
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com https://www.googletagmanager.com https://www.google-analytics.com https://maps.googleapis.com https://fonts.googleapis.com;"
'script-src ' . implode(" ", [
'\'self\'',
'\'unsafe-inline\'',
'\'unsafe-eval\'',
'https://cdnjs.cloudflare.com',
'https://*.googletagmanager.com',
'https://www.google-analytics.com',
'https://maps.googleapis.com',
'https://fonts.googleapis.com',
'https://www.google.com/recaptcha/',
'https://www.gstatic.com/recaptcha/',
]) . ' ' . $jsDev . ';'
];
$response->headers->set(
'Content-Security-Policy',
implode(' ', $cspPolicies)
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
return $response;
}
private function removeUnwantedHeaders($headerList)
/**
* Remove unwanted headers
*
* @param array $headerList
* @return void
*/
private function removeUnwantedHeaders(array $headerList)
{
foreach ($headerList as $header) {
header_remove($header);

View file

@ -4,6 +4,7 @@ namespace App\Http\Requests;
use App\Enums\ContactFormCivility;
use App\Enums\ContactFormType;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
@ -26,6 +27,8 @@ class ContactFormRequest extends FormRequest
*/
public function rules()
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
if ($this->type == ContactFormType::onepage()->value) {
app()->setLocale('nl');
}

View file

@ -2,6 +2,7 @@
namespace App\Http\Requests;
use App\Lib\Helpers\ToolboxHelper;
use Carbon\Carbon;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
@ -36,6 +37,8 @@ class CustomerRegisterRequest extends FormRequest
*/
public function rules()
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
return [
'email' => 'required|email:rfc,dns,spoof,filter|max:255|unique:customers,email',
'username' => 'required|string|min:1|max:255|unique:customers,username',
@ -78,6 +81,10 @@ class CustomerRegisterRequest extends FormRequest
];
}
/**
*
* @return array
*/
public function attributes()
{
return [
@ -85,6 +92,10 @@ class CustomerRegisterRequest extends FormRequest
];
}
/**
*
* @return array
*/
public function messages()
{
return [

View file

@ -0,0 +1,65 @@
<?php
namespace App\Lib\Helpers;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Validator;
class ToolboxHelper
{
/**
* Validate recaptcha
*
* @param Request $request
* @param boolean $isV3
* @return void
*/
public static function validateRecaptcha(Request $request, bool $isV3 = true)
{
Validator::validate($request->all(), [
'g-recaptcha-response' => [
'required', 'string',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) use ($isV3) {
try {
$response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
'secret' => config('services.google.recaptcha.private'),
'response' => \strval($value),
'remoteip' => app('clientIp')
]);
if ($e = $response->toException()) {
throw $e;
}
if ($isV3) {
if (
!$response->json('success') or
!$response->json('challenge_ts') or
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST) or
!$response->json('score')
) {
throw new \RuntimeException();
}
if (\floatval($response->json('score')) < 0.8) {
$fail("Vous n'avez pas obtenu un score assez élevé lors de la validation du recaptcha");
}
return;
}
if (
!$response->json('success') or
!$response->json('challenge_ts') or
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST)
) {
throw new \RuntimeException();
}
} catch (\Exception $e) {
$fail('Erreur lors de la validation du formulaire veuillez réssayer');
} //end try
}
]
], [], ['g-recaptcha-response' => 'reCaptcha']);
}
}

View file

@ -30,4 +30,12 @@ return [
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
'google' => [
'recaptcha' => [
'public' => env('GOOGLE_RECAPTCHA_PUBLIC'),
'private' => env('GOOGLE_RECAPTCHA_PRIVATE'),
'version' => env('GOOGLE_RECAPTCHA_VERSION', "3") === "3" ? true : false
]
],
];

View file

@ -8,7 +8,7 @@
@section('content')
<section class="content-register">
<div class="content">
<form action="{{ route('inscription') }}" method="POST" class="form-register" name="form-register">
<form action="{{ route('inscription') }}" method="POST" id="register-form-id" class="form-register" name="form-register">
@csrf
<div class="bloc-left">
@if ($errors->any())
@ -79,20 +79,38 @@
<div>
<label>
<input type="checkbox" name="opt_in" @if(old('opt_in') !== null)checked @endif>
{{ __('J’accepte de recevoir les actualités et bons plans Le Gaulois') }}
{{ __('J\'accepte de recevoir les actualités et bons plans Le Gaulois') }}
</label>
</div>
<br>
<div>
<label>
<input class="changeLength" type="checkbox" name="agree" required>
{{ __('J’accepte les conditions générales d’utilisation du site et autorise LDC Sablé à traiter mes données personnelles conformément à la Charte de confidentialité.') }}
{{ __('J\'accepte les conditions générales d\'utilisation du site et autorise LDC Sablé à traiter mes données personnelles conformément à la Charte de confidentialité.') }}
</label>
</div>
<button type="submit" class="btn-red" value="Enregistrer">{{ __('Enregistrer') }}</button>
</div>
<div class="completion g-recaptcha" data-sitekey="{{ config('services.google.recaptcha.public') }}"></div>
</div>
</form>
</div>
</section>
@endsection
@push('scripts')
<script nonce="{{ $nonce }}">
window.__LDC_CN_Recaptcha = function() {
(function(d, t) {
let g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g.src = 'https://www.google.com/recaptcha/api.js';
s.parentNode.insertBefore(g, s)
}(document, 'script'));
};
function onSubmit(token) {
document.getElementById("register-form-id").submit();
}
</script>
@endpush

View file

@ -1,4 +1,4 @@
<form action="{{ $type->equals(ContactFormType::complain()) ? route('product-request.submit') : route('contact-us.submit') }}" class="form-contact" method="post" enctype="multipart/form-data">
<form action="{{ $type->equals(ContactFormType::complain()) ? route('product-request.submit') : route('contact-us.submit') }}" id="contact-form-id" class="form-contact" method="post" enctype="multipart/form-data">
@csrf
<p id="contactFormError" @if(count($errors))class="display" @endif>
@ -137,10 +137,11 @@
@if($errors->default->has('message'))<small class="text-danger">{{ implode('<br>', $errors->default->get('message')) }}</small>@endif
</p>
<p class="envoyer">
<div class="envoyer g-recaptcha" data-sitekey="{{ config('services.google.recaptcha.public') }}"></div>
<div class="envoyer mt-1">
<span><sup>*</sup>{{ __('mandatory_fields') }}</span>
<button title="Envoyer" class="btn btn-red btn-arronded-20 text-uppercase">{{ __('send') }}</button>
</p>
<button class="btn btn-red btn-arronded-20 text-uppercase" title="Envoyer" type="submit">{{ __('send') }}</button>
</div>
</form>
@push('scripts')
@ -169,3 +170,20 @@
</script>
@endpush
@push('scripts')
<script nonce="{{ $nonce }}">
window.__LDC_CN_Recaptcha = function() {
(function(d, t) {
let g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g.src = 'https://www.google.com/recaptcha/api.js';
s.parentNode.insertBefore(g, s)
}(document, 'script'));
};
function onSubmit(token) {
document.getElementById("contact-form-id").submit();
}
</script>
@endpush

View file

@ -21,18 +21,18 @@
<div class="ldcweb-banner-title">
<p aria-level="h1">Le site internet Le Gaulois utilise des cookies !</p>
</div>
<p class="ldcweb-banner-para-p" data-open="Ce site web stocke les cookies sur votre ordinateur. Le Gaulois utilise ces informations
afin d’améliorer et de personnaliser votre expérience de navigation, ainsi que pour l'analyse et les indicateurs concernant nos
visiteurs à la fois sur ce site web et sur d'autres supports. Vous pouvez cliquer sur « Accepter » si vous consentez à l’utilisation
de ces cookies." data-close="Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
cookies.">Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
<p class="ldcweb-banner-para-p" data-open="Ce site web stocke les cookies sur votre ordinateur. Le Gaulois utilise ces informations
afin d’améliorer et de personnaliser votre expérience de navigation, ainsi que pour l'analyse et les indicateurs concernant nos
visiteurs à la fois sur ce site web et sur d'autres supports. Vous pouvez cliquer sur « Accepter » si vous consentez à l’utilisation
de ces cookies." data-close="Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
cookies.">Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
cookies.</p>
</div>
</div>
<div class="ldcweb-banner-details">
<p>Si vous refusez l'utilisation des cookies, vos informations ne seront pas suivies lors de votre visite sur ce site. Un cookie sera utilisé
<p>Si vous refusez l'utilisation des cookies, vos informations ne seront pas suivies lors de votre visite sur ce site. Un cookie sera utilisé
dans votre navigateur afin de se souvenir de ne pas suivre vos préférences. Certaines fonctionnalités ne seront pas accessibles.
Vous pouvez changer d’avis et modifier votre consentement à tout moment en revenant sur ce site.
Pour en savoir plus sur les cookies que nous utilisons, consultez notre <a href="{{ route('chart-conf') }}">charte de confidentialité</a>.</p>
@ -47,8 +47,8 @@
<p class="subTitle">{{ __('Le respect de votre vie privée est notre priorité') }}</p>
<p class="description">Les fonctionnalités de ce site s’appuient sur des services proposés par des tiers.
Si vous acceptez, ces tiers déposeront des cookies qui vous permettront de visualiser sur le site du contenu hébergé par ces tiers.
Via ces cookies, ces tiers collecteront et utiliseront vos données de navigation pour des finalités qui leur sont propres, conformément
à leur politique de confidentialité (lien ci-dessous). Cette page vous permet de donner ou de retirer votre consentement, soit globalement
Via ces cookies, ces tiers collecteront et utiliseront vos données de navigation pour des finalités qui leur sont propres, conformément
à leur politique de confidentialité (lien ci-dessous). Cette page vous permet de donner ou de retirer votre consentement, soit globalement
soit service par service.</p>
</div>
<div class="modal-details">
@ -95,6 +95,18 @@
@push('scripts')
<script>
window.LDCWebCN = new LdcwebCn({
'recaptcha': {
'name': "API - reCAPTACHA",
'description': 'Les cookies qui sont déposés via le recaptcha sur les formulaires ont pour finalité d\'empêcher que ceux-ci ne soient soumis par des robots.',
'link': 'https://policies.google.com/privacy',
'labelLink': 'En savoir plus',
'cookies': ['NID', '__Secure-3PAPISID', '__Secure-3PSID', '__Secure-3PSIDCC'],
'script': function() {
if (window.__LDC_CN_Recaptcha) {
window.__LDC_CN_Recaptcha();
}
}
},
'analytics': {
'name': "Google Analytics",
'description': 'Service d\'analyse d\'audience',