Add in hcaptcha support.

merge-requests/208/head
dolphin 4 years ago committed by Thomas Lynch
parent c2e7699b15
commit d51aa143a8
  1. 1
      .gitignore
  2. 6
      configs/main.js.example
  3. 16
      gulp/res/js/forms.js
  4. 2
      helpers/captcha/verify.js
  5. 18
      helpers/checks/captcha.js
  6. 3
      helpers/render.js
  7. 2
      models/pages/captcha.js
  8. 3
      server.js
  9. 2
      views/includes/captcha.pug
  10. 2
      views/includes/head.pug

1
.gitignore vendored

@ -12,3 +12,4 @@ gulp/res/js/locals.js
gulp/res/js/post.js
gulp/res/js/modal.js
tmp/
.idea/

@ -41,12 +41,16 @@ module.exports = {
//settings for captchas
captchaOptions: {
type: 'grid', //"text", "grid" or "google". If using google, make sure your CSP header in nginx config allows the google domain.
type: 'grid', //"text", "grid", "hcaptcha" or "google". If using google/hcaptcha, make sure your CSP header in nginx config allows the google/hcaptcha domain.
generateLimit: 1000, //max number of captchas to have generated at any time, prevent mass unsolved captcha spam, especially on TOR.
google: { //options for google captcha, when captcha type is google
siteKey: 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',
secretKey: 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
},
hcaptcha: {
siteKey: "10000000-ffff-ffff-ffff-000000000001",
secretKey: "0x0000000000000000000000000000000000000000"
},
grid: {
size: 4,
imageSize: 120,

@ -67,14 +67,13 @@ let recaptchaResponse = null;
function recaptchaCallback(response) {
recaptchaResponse = response;
}
class formHandler {
constructor(form) {
this.form = form;
this.enctype = this.form.getAttribute('enctype');
this.messageBox = form.querySelector('#message');
this.captchaField = form.querySelector('.captchafield') || form.querySelector('.g-recaptcha');
this.captchaField = form.querySelector('.captchafield') || form.querySelector('.g-recaptcha') || form.querySelector('.h-captcha');
this.submit = form.querySelector('input[type="submit"]');
if (this.submit) {
@ -126,11 +125,12 @@ class formHandler {
formSubmit(e) {
const xhr = new XMLHttpRequest();
let postData;
const captchaResponse = recaptchaResponse || hcaptchaResponse;
if (this.enctype === 'multipart/form-data') {
this.fileInput && (this.fileInput.disabled = true);
postData = new FormData(this.form);
if (recaptchaResponse) {
postData.append('captcha', recaptchaResponse);
if (captchaResponse) {
postData.append('captcha', captchaResponse);
}
this.fileInput && (this.fileInput.disabled = false);
if (this.files && this.files.length > 0) {
@ -141,8 +141,8 @@ class formHandler {
}
} else {
postData = new URLSearchParams([...(new FormData(this.form))]);
if (recaptchaResponse) {
postData.set('captcha', recaptchaResponse);
if (captchaResponse) {
postData.set('captcha', captchaResponse);
}
}
if (this.banned
@ -169,8 +169,10 @@ class formHandler {
}
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (recaptchaResponse && grecaptcha) {
if (captchaResponse && grecaptcha) {
grecaptcha.reset();
} else if(captchaResponse && hcaptcha) {
hcaptcha.reset();
}
this.submit.disabled = false;
this.submit.value = this.originalSubmitText;

@ -25,7 +25,7 @@ module.exports = async (req, res, next) => {
}
}
const captchaInput = req.body.captcha || req.body['g-recaptcha-response'];
const captchaInput = req.body.captcha || req.body['g-recaptcha-response'] || req.body["h-captcha-response"];
const captchaId = req.cookies.captchaid;
try {
await checkCaptcha(captchaInput, captchaId);

@ -69,6 +69,24 @@ module.exports = async (captchaInput, captchaId) => {
throw 'Incorrect captcha answer';
}
break;
case 'hcaptcha':
let hcaptchaResponse;
try {
const form = new FormData();
form.append('secret', captchaOptions.hcaptcha.secretKey);
form.append('sitekey', captchaOptions.hcaptcha.siteKey);
form.append('response', captchaInput[0]);
hcaptchaResponse = await fetch('https://hcaptcha.com/siteverify', {
method: 'POST',
body: form,
}).then(res => res.json());
} catch (e) {
throw 'Captcha error occurred';
}
if (!hcaptchaResponse || !hcaptchaResponse.success) {
throw 'Incorrect captcha answer';
}
break;
default:
throw 'Captcha config error';
break;

@ -29,6 +29,9 @@ switch (captchaOptions.type) {
case 'google':
renderLocals.googleRecaptchaSiteKey = captchaOptions.google.siteKey;
break;
case 'hcaptcha':
renderLocals.hcaptchaSitekey = captchaOptions.hcaptcha.siteKey;
break;
case 'grid':
renderLocals.captchaGridSize = captchaOptions.grid.size;
break;

@ -2,7 +2,7 @@
const { Captchas, Ratelimits } = require(__dirname+'/../../db/')
, { secureCookies, rateLimitCost, captchaOptions } = require(__dirname+'/../../configs/main.js')
, generateCaptcha = captchaOptions.type !== 'google' ? require(__dirname+`/../../helpers/captcha/generators/${captchaOptions.type}.js`) : null
, generateCaptcha = captchaOptions.type !== 'google' && captchaOptions !== "hcaptcha" ? require(__dirname+`/../../helpers/captcha/generators/${captchaOptions.type}.js`) : null
, production = process.env.NODE_ENV === 'production';
module.exports = async (req, res, next) => {

@ -81,6 +81,9 @@ const express = require('express')
case 'google':
app.locals.googleRecaptchaSiteKey = captchaOptions.google.siteKey;
break;
case 'hcaptcha':
app.locals.hcaptchaSiteKey = captchaOptions.hcaptcha.siteKey;
break;
case 'grid':
app.locals.captchaGridSize = captchaOptions.grid.size;
break;

@ -2,6 +2,8 @@ case captchaType
when 'google'
div(class="g-recaptcha" data-sitekey=`${googleRecaptchaSiteKey}` data-theme="dark" data-size="compact" data-callback="recaptchaCallback")
noscript Please enable JavaScript to solve the captcha.
when 'hcaptcha'
div(class='h-captcha' data-sitekey=`${hcaptchaSiteKey}` data-theme='dark' data-size='compact' data-callback='recaptchaCallback')
when 'text'
noscript.no-m-p
iframe.captcha(src='/captcha.html' 'width=210' height='80' scrolling='no' loading='lazy')

@ -20,3 +20,5 @@ script(src=`/js/all.js?v=${commit}`)
if captchaType === 'google'
script(src="https://www.google.com/recaptcha/api.js" async defer)
if captchaType === 'hcaptcha'
script(src="https://hcaptcha.com/1/api.js" async defer)
Loading…
Cancel
Save