From 1c0b1101b037d4c91a63b95aa24717385a57a2e4 Mon Sep 17 00:00:00 2001 From: HF Date: Mon, 8 Mar 2021 23:47:44 +0100 Subject: [PATCH] fix table width more captcha changes --- deployment/example-ecosystem-captchas.yml | 1 + src/captchaserver.js | 9 ++- src/components/Captcha.jsx | 7 ++- src/core/config.js | 2 + src/routes/api/captcha.js | 53 +++++++++-------- src/routes/api/index.js | 10 ++-- src/ssr-components/Html.jsx | 2 - src/styles/default.css | 2 + src/ui/placePixel.js | 2 +- src/utils/captcha.js | 69 +++++++++++------------ 10 files changed, 90 insertions(+), 67 deletions(-) diff --git a/deployment/example-ecosystem-captchas.yml b/deployment/example-ecosystem-captchas.yml index f98120f..f89f7b9 100644 --- a/deployment/example-ecosystem-captchas.yml +++ b/deployment/example-ecosystem-captchas.yml @@ -6,3 +6,4 @@ apps: PORT: 8080 HOST: "localhost" REDIS_URL: 'redis://localhost:6379' + CAPTCHA_TIMEOUT: 120 diff --git a/src/captchaserver.js b/src/captchaserver.js index 1ea20a1..e13916e 100644 --- a/src/captchaserver.js +++ b/src/captchaserver.js @@ -8,6 +8,9 @@ import process from 'process'; import http from 'http'; import ppfunCaptcha from 'ppfun-captcha'; +import { getIPFromRequest } from './utils/ip'; +import { setCaptchaSolution } from './utils/captcha'; + const PORT = process.env.PORT || 8080; const HOST = process.env.HOST || 'localhost'; @@ -22,8 +25,12 @@ const server = http.createServer((req, res) => { nodeDeviation: 0.5, connectionPathDeviation: 0.3, }); - const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; + + const ip = getIPFromRequest(req); + + setCaptchaSolution(captcha.text, ip); console.log(`Serving ${captcha.text} to ${ip}`); + res.writeHead(200, { 'Content-Type': 'image/svg+xml', 'Cache-Control': 'no-cache', diff --git a/src/components/Captcha.jsx b/src/components/Captcha.jsx index 73c2a5c..f442b8b 100644 --- a/src/components/Captcha.jsx +++ b/src/components/Captcha.jsx @@ -1,4 +1,8 @@ /* + * Form to ask for captcha. + * If callback is provided, it sets the captcha text to it. + * If callback is not provided, it provides a button to send the + * captcha itself * @flow */ @@ -6,6 +10,7 @@ import React, { useState } from 'react'; import { t } from 'ttag'; import { IoReloadCircleSharp } from 'react-icons/io5'; +import { requestSolveCaptcha } from '../actions/fetch'; function getUrl() { return `${window.ssv.captchaurl}/captcha.svg?${new Date().getTime()}`; @@ -25,7 +30,7 @@ const Captcha = ({ callback, cancel }) => {

setError(true)} /> diff --git a/src/core/config.js b/src/core/config.js index 521ab2d..f79d6d2 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -90,5 +90,7 @@ export const auth = { // time on which to display captcha in minutes export const CAPTCHA_TIME = parseInt(process.env.CAPTCHA_TIME, 10) || 30; +// time during which the user can solve a captcha in seconds +export const CAPTCHA_TIMEOUT = parseInt(process.env.CAPTCHA_TIMEOUT, 10) || 120; export const SESSION_SECRET = process.env.SESSION_SECRET || 'dummy'; diff --git a/src/routes/api/captcha.js b/src/routes/api/captcha.js index 45ab8b3..dfecc54 100644 --- a/src/routes/api/captcha.js +++ b/src/routes/api/captcha.js @@ -8,42 +8,51 @@ import type { Request, Response } from 'express'; import logger from '../../core/logger'; -import { verifyCaptcha } from '../../utils/captcha'; +import { checkCaptchaSolution } from '../../utils/captcha'; import { getIPFromRequest } from '../../utils/ip'; export default async (req: Request, res: Response) => { const ip = getIPFromRequest(req); + const { t } = req.ttag; try { - const { token } = req.body; - if (!token) { + const { text } = req.body; + if (!text) { res.status(400) - .json({ errors: [{ msg: 'No token given' }] }); + .json({ errors: [t`No captcha text given`] }); return; } - if (!await verifyCaptcha(token, ip)) { - logger.info(`CAPTCHA ${ip} failed his captcha`); - res.status(422) - .json({ - errors: [{ - msg: - 'You failed your captcha', - }], - }); - return; - } + const ret = await checkCaptchaSolution(text, ip); - res.status(200) - .json({ success: true }); + switch (ret) { + case 0: + res.status(200) + .json({ success: true }); + break; + case 1: + res.status(422) + .json({ + errors: [t`You took too long, try again.`], + }); + break; + case 2: + res.status(422) + .json({ + errors: [t`You failed your captcha`], + }); + break; + default: + res.status(422) + .json({ + errors: [t`Unknown Captcha Error`], + }); + } } catch (error) { - logger.error('checkHuman', error); + logger.error('CAPTCHA', error); res.status(500) .json({ - errors: [{ - msg: - 'Server error occured', - }], + errors: [t`Server error occured`], }); } }; diff --git a/src/routes/api/index.js b/src/routes/api/index.js index a378bc0..fefaa8d 100644 --- a/src/routes/api/index.js +++ b/src/routes/api/index.js @@ -44,6 +44,11 @@ router.use((err, req, res, next) => { } }); +/* + * make localisations available + */ +router.use(expressTTag); + // captcah doesn't need a user router.post('/captcha', captcha); @@ -89,11 +94,6 @@ router.post('/block', block); router.post('/blockdm', blockdm); -/* - * make localisations available - */ -router.use(expressTTag); - router.get('/chathistory', chatHistory); router.get('/me', me); diff --git a/src/ssr-components/Html.jsx b/src/ssr-components/Html.jsx index 02e3b7e..4f7b142 100644 --- a/src/ssr-components/Html.jsx +++ b/src/ssr-components/Html.jsx @@ -24,8 +24,6 @@ const Html = ({ styles, // code as string code, - // if recaptcha should get loaded - useCaptcha, }) => ( diff --git a/src/styles/default.css b/src/styles/default.css index 97318f5..192cb2b 100644 --- a/src/styles/default.css +++ b/src/styles/default.css @@ -112,6 +112,8 @@ td, th { border: 1px solid #dddddd; text-align: left; padding: 8px; + max-width: 18em; + overflow-x: hidden; } tr:nth-child(even) { diff --git a/src/ui/placePixel.js b/src/ui/placePixel.js index 200d711..61d5943 100644 --- a/src/ui/placePixel.js +++ b/src/ui/placePixel.js @@ -241,7 +241,7 @@ export function receivePixelReturn( case 10: store.dispatch(sweetAlert( 'Captcha', - `Please prove that you are human..`, + t`Please prove that you are human`, 'captcha', t`OK`, )); diff --git a/src/utils/captcha.js b/src/utils/captcha.js index bc74fe3..3e62c14 100644 --- a/src/utils/captcha.js +++ b/src/utils/captcha.js @@ -5,58 +5,60 @@ import logger from '../core/logger'; import redis from '../data/redis'; - +import { getIPv6Subnet } from './ip'; import { CAPTCHA_URL, CAPTCHA_TIME, + CAPTCHA_TIMEOUT, } from '../core/config'; const TTL_CACHE = CAPTCHA_TIME * 60; // seconds /* - * https://docs.hcaptcha.com/ + * set captcha solution * - * @param token + * @param text Solution of captcha * @param ip - * @return boolean, true if successful, false on error or fail + * @param ttl time to be valid in seconds */ -async function verifyHCaptcha( - token: string, +export function setCaptchaSolution( + text: string, ip: string, -): Promise { - const success = true; - if (success) { - logger.info(`CAPTCHA ${ip} successfully solved captcha`); - return true; - } - logger.info(`CAPTCHA Token for ${ip} not ok`); - return false; +) { + const key = `capt:${getIPv6Subnet(ip)}`; + return redis.setAsync(key, text, 'EX', CAPTCHA_TIMEOUT); } /* - * verify captcha token from client + * check captcha solution * - * @param token token of solved captcha from client + * @param text Solution of captcha * @param ip - * @returns Boolean if successful + * @return 0 if solution right + * 1 if timed out + * 2 if wrong */ -export async function verifyCaptcha( - token: string, +export async function checkCaptchaSolution( + text: string, ip: string, -): Promise { - try { - const key = `human:${ip}`; - - if (!await verifyHCaptcha(token, ip)) { - return false; +) { + const ipn = getIPv6Subnet(ip); + const key = `capt:${ipn}`; + const solution = await redis.getAsync(key); + if (solution) { + if (solution.toString('utf8') === text) { + const solvkey = `human:${ipn}`; + await redis.setAsync(solvkey, '', 'EX', TTL_CACHE); + logger.info(`CAPTCHA ${ip} successfully solved captcha`); + return 0; } - - await redis.setAsync(key, '', 'EX', TTL_CACHE); - return true; - } catch (error) { - logger.error(error); + logger.info( + `CAPTCHA ${ip} got captcha wrong (${text} instead of ${solution})`, + ); + return 2; } - return false; + logger.info(`CAPTCHA ${ip} timed out`); + return 1; } /* @@ -70,7 +72,7 @@ export async function needCaptcha(ip: string) { return false; } - const key = `human:${ip}`; + const key = `human:${getIPv6Subnet(ip)}`; const ttl: number = await redis.ttlAsync(key); if (ttl > 0) { return false; @@ -78,6 +80,3 @@ export async function needCaptcha(ip: string) { logger.info(`CAPTCHA ${ip} got captcha`); return true; } - - -export default verifyCaptcha;