add captcha alert, remove some old captcha stuff

This commit is contained in:
HF 2021-02-24 03:41:39 +01:00
parent 74b4a44224
commit 7dd44811a6
13 changed files with 127 additions and 154 deletions

View File

@ -79,24 +79,22 @@ Configuration takes place in the environment variables that are defined in ecosy
#### Optional Configuration
| Variable | Description | Example |
|-------------------|:--------------------------------------|--------------------|
| ASSET_SERVER | URL for assets | "http://localhost" |
| USE_PROXYCHECK | Check users for Proxies | 0 |
| APISOCKET_KEY | Key for API Socket for SpecialAccess™ | "SDfasife3" |
| ADMIN_IDS | Ids of users with Admin rights | "1,12,3" |
| CAPTCHA_METHOD | 0: none, 1: reCaptcha, 2: hCaptcha | 2 |
| CAPTCHA_SECRET | re/hCaptcha secret key | "asdieewff" |
| CAPTCHA_SITEKEY | re/hCaptcha site key | "23ksdfssd" |
| CAPTCHA_TIME | time in minutes between captchas | 30 |
| SESSION_SECRET | random sting for express sessions | "ayylmao" |
| LOG_MYSQL | if sql queries should get logged | 0 |
| USE_XREALIP | see ngins / CDN section | 1 |
| BACKUP_URL | url of backup server (see Backup) | "http://localhost" |
| BACKUP_DIR | mounted directory of backup server | "/mnt/backup/" |
| GMAIL_USER | gmail username if used for mails | "ppfun@gmail.com" |
| GMAIL_PW | gmail password if used for mails | "lolrofls" |
| HOURLY_EVENT | run hourly void event on main canvas | 1 |
| Variable | Description | Example |
|-------------------|:--------------------------------------|-------------------------|
| ASSET_SERVER | URL for assets | "http://localhost" |
| USE_PROXYCHECK | Check users for Proxies | 0 |
| APISOCKET_KEY | Key for API Socket for SpecialAccess™ | "SDfasife3" |
| ADMIN_IDS | Ids of users with Admin rights | "1,12,3" |
| CAPTCHA_URL | URL where captcha is served | "http://localhost:8080" |
| CAPTCHA_TIME | time in minutes between captchas | 30 |
| SESSION_SECRET | random sting for express sessions | "ayylmao" |
| LOG_MYSQL | if sql queries should get logged | 0 |
| USE_XREALIP | see ngins / CDN section | 1 |
| BACKUP_URL | url of backup server (see Backup) | "http://localhost" |
| BACKUP_DIR | mounted directory of backup server | "/mnt/backup/" |
| GMAIL_USER | gmail username if used for mails | "ppfun@gmail.com" |
| GMAIL_PW | gmail password if used for mails | "lolrofls" |
| HOURLY_EVENT | run hourly void event on main canvas | 1 |
Notes:

View File

@ -3,6 +3,6 @@ apps:
name : 'captchas'
node_args: --nouse-idle-notification --expose-gc
env:
PORT: 80
PORT: 8080
HOST: "localhost"
REDIS_URL: 'redis://localhost:6379'

View File

@ -8,7 +8,7 @@ import process from 'process';
import http from 'http';
import ppfunCaptcha from 'ppfun-captcha';
const PORT = process.env.PORT || 80;
const PORT = process.env.PORT || 8080;
const HOST = process.env.HOST || 'localhost';
const server = http.createServer((req, res) => {
@ -25,7 +25,7 @@ const server = http.createServer((req, res) => {
const ip = req.headers['x-real-ip'] || req.connection.remoteAddress;
console.log(`Serving ${captcha.text} to ${ip}`);
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Type': 'image/svg+xml',
'Cache-Control': 'no-cache',
});
res.write(captcha.data);

View File

@ -3,9 +3,10 @@
* @flow
*/
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Captcha from './Captcha';
import { closeAlert } from '../actions';
const Alert = () => {
@ -20,9 +21,9 @@ const Alert = () => {
} = useSelector((state) => state.alert);
const dispatch = useDispatch();
const close = () => {
const close = useCallback(() => {
dispatch(closeAlert());
};
}, [dispatch]);
const onTransitionEnd = () => {
if (!alertOpen) setRender(false);
@ -54,12 +55,16 @@ const Alert = () => {
{alertMessage}
</p>
<p>
<button
type="button"
onClick={close}
>
{alertBtn}
</button>
{(alertType === 'captcha')
? <Captcha cancel={close} />
: (
<button
type="button"
onClick={close}
>
{alertBtn}
</button>
)}
</p>
</div>
</div>

View File

@ -2,10 +2,74 @@
* @flow
*/
import React from 'react';
import React, { useState } from 'react';
import { t } from 'ttag';
const Captcha = ({
}) => (
div />
);
import { IoReloadCircleSharp } from 'react-icons/io5';
function getUrl() {
return `${window.ssv.captchaurl}/captcha.svg?${new Date().getTime()}`;
}
const Captcha = ({ callback, cancel }) => {
const [captchaUrl, setCaptchaUrl] = useState(getUrl());
const [text, setText] = useState('');
const [error, setError] = useState(false);
return (
<div>
<p className="modaltext">
{t`Type the characters from the following image:`}
<span style={{ fontSize: 11 }}>
({t`Tip: Not case-sensitive; I and l are the same`})
</span>
</p>
<img
style={{width: '75%'}}
src={captchaUrl}
onError={() => setError(true)}
/>
<p className="modaltext">
{t`Can't read? Reload:`}&nbsp;
<span
role="button"
tabIndex={-1}
title={t`Reload`}
onClick={() => setCaptchaUrl(getUrl())}
>
<IoReloadCircleSharp />
</span>
</p>
<input
placeholder={t`I am human`}
type="text"
value={text}
autoFocus
autoComplete="off"
style={{ width: '5em' }}
onChange={(evt) => {
const txt = evt.target.value;
setText(txt);
if (callback) callback(txt);
}}
/>
{(!callback) && (
<div>
<button
type="button"
onClick={cancel}
>
{t`Cancel`}
</button>
<button
type="button"
>
{t`Send`}
</button>
</div>
)}
</div>
);
};
export default React.memo(Captcha);

View File

@ -25,7 +25,7 @@ const ChatBox = () => {
}, 10);
}, [chatOpen]);
const onTransitionEnd =() => {
const onTransitionEnd = () => {
if (!chatOpen) setRender(false);
};
@ -44,6 +44,6 @@ const ChatBox = () => {
</div>
)
);
}
};
export default React.memo(ChatBox);

View File

@ -90,20 +90,6 @@ can be downloaded from mega.nz here: `}<a href="https://mega.nz/#!JpkBwAbJ!EnSLl
{jt`Click ${mouseSymbol} middle mouse button or ${touchSymbol} long-tap to select current hovering color`}<br />
</div>
<p>{t`Partners:`} <a href="https://www.crazygames.com/c/io" target="_blank" rel="noopener noreferrer">crazygames.com</a></p>
{ (typeof window.hcaptcha === 'undefined')
? (
<p className="modaltext">
<small>
{jt`This site is protected by reCAPTCHA and the Google ${reCaptchaPP} and ${reCaptchaTOS} apply.`}
</small>
</p>
) : (
<p className="modaltext">
<small>
{jt`This site is protected by hCAPTCHA and its ${hCaptchaPP} and ${hCaptchaTOS} apply.`}
</small>
</p>
)}
</p>
);
};

View File

@ -121,6 +121,7 @@ class SignUpForm extends React.Component {
<input
style={inputStyles}
value={name}
autoComplete="username"
onChange={(evt) => this.setState({ name: evt.target.value })}
type="text"
placeholder={t`Name`}
@ -128,6 +129,7 @@ class SignUpForm extends React.Component {
<input
style={inputStyles}
value={email}
autoComplete="email"
onChange={(evt) => this.setState({ email: evt.target.value })}
type="text"
placeholder={t`Email`}
@ -135,6 +137,7 @@ class SignUpForm extends React.Component {
<input
style={inputStyles}
value={password}
autoComplete="new-password"
onChange={(evt) => this.setState({ password: evt.target.value })}
type="password"
placeholder={t`Password`}
@ -142,6 +145,7 @@ class SignUpForm extends React.Component {
<input
style={inputStyles}
value={confirmPassword}
autoComplete="new-password"
onChange={(evt) => this.setState({
confirmPassword: evt.target.value,
})}

View File

@ -20,6 +20,8 @@ export const TILE_FOLDER = path.join(__dirname, `./${TILE_FOLDER_REL}`);
export const ASSET_SERVER = process.env.ASSET_SERVER || '.';
export const CAPTCHA_URL = process.env.CAPTCHA_URL || 'http://localhost:8080';
export const USE_XREALIP = process.env.USE_XREALIP || false;
export const BACKUP_URL = process.env.BACKUP_URL || null;
@ -86,12 +88,6 @@ export const auth = {
},
};
// o: none
// 1: reCaptcha
// 2: hCaptcha
export const CAPTCHA_METHOD = Number(process.env.CAPTCHA_METHOD || 0);
export const CAPTCHA_SECRET = process.env.CAPTCHA_SECRET || false;
export const CAPTCHA_SITEKEY = process.env.CAPTCHA_SITEKEY || false;
// time on which to display captcha in minutes
export const CAPTCHA_TIME = parseInt(process.env.CAPTCHA_TIME, 10) || 30;

View File

@ -11,7 +11,6 @@
/* eslint-disable max-len */
import React from 'react';
import { CAPTCHA_METHOD, CAPTCHA_SITEKEY } from '../core/config';
const Html = ({
title,
@ -48,10 +47,6 @@ const Html = ({
dangerouslySetInnerHTML={{ __html: style.cssText }}
/>
))}
{(CAPTCHA_METHOD === 1) && CAPTCHA_SITEKEY && useCaptcha
&& <script src="https://www.google.com/recaptcha/api.js" async defer />}
{(CAPTCHA_METHOD === 2) && CAPTCHA_SITEKEY && useCaptcha
&& <script src="https://hcaptcha.com/1/api.js" async defer />}
{code && (
<script
// eslint-disable-next-line react/no-danger
@ -67,24 +62,6 @@ const Html = ({
{body}
</div>
{scripts && scripts.map((script) => <script key={script} src={script} />)}
{(CAPTCHA_METHOD === 2) && CAPTCHA_SITEKEY && useCaptcha
&& (
<div
className="h-captcha"
data-sitekey={CAPTCHA_SITEKEY}
data-callback="onCaptcha"
data-size="invisible"
/>
)}
{(CAPTCHA_METHOD === 1) && CAPTCHA_SITEKEY && useCaptcha
&& (
<div
className="g-recaptcha"
data-sitekey={CAPTCHA_SITEKEY}
data-callback="onCaptcha"
data-size="invisible"
/>
)}
</body>
</html>
);

View File

@ -17,7 +17,7 @@ import assets from './assets.json';
// eslint-disable-next-line import/no-unresolved
import styleassets from './styleassets.json';
import { ASSET_SERVER, BACKUP_URL } from '../core/config';
import { CAPTCHA_URL, ASSET_SERVER, BACKUP_URL } from '../core/config';
/*
* generate language list
@ -31,6 +31,7 @@ const langs = Object.keys(ttags)
*/
const ssv = {
assetserver: ASSET_SERVER,
captchaurl: CAPTCHA_URL,
availableStyles: styleassets,
langs,
};

View File

@ -239,15 +239,14 @@ export function receivePixelReturn(
store.dispatch(pixelWait());
break;
case 10:
// captcha, reCaptcha or hCaptcha
if (typeof window.hcaptcha !== 'undefined') {
window.hcaptcha.execute();
} else {
window.grecaptcha.execute();
}
store.dispatch(sweetAlert(
'Captcha',
`Please prove that you are human..`,
'captcha',
t`OK`,
));
return;
case 11:
errorTitle = t`No Proxies Allowed :(`;
msg = t`You are using a Proxy.`;
break;

View File

@ -3,46 +3,15 @@
* @flow
*/
import fetch from 'isomorphic-fetch';
import logger from '../core/logger';
import redis from '../data/redis';
import {
CAPTCHA_METHOD,
CAPTCHA_SECRET,
CAPTCHA_URL,
CAPTCHA_TIME,
} from '../core/config';
const TTL_CACHE = CAPTCHA_TIME * 60; // seconds
// eslint-disable-next-line max-len
const RECAPTCHA_ENDPOINT = `https://www.google.com/recaptcha/api/siteverify?secret=${CAPTCHA_SECRET}`;
const HCAPTCHA_ENDPOINT = 'https://hcaptcha.com/siteverify';
/**
* https://stackoverflow.com/questions/27297067/google-recaptcha-how-to-get-user-response-and-validate-in-the-server-side
*
* @param token
* @param ip
* @returns {Promise.<boolean>}
*/
async function verifyReCaptcha(
token: string,
ip: string,
): Promise<boolean> {
const url = `${RECAPTCHA_ENDPOINT}&response=${token}&remoteip=${ip}`;
const response = await fetch(url);
if (response.ok) {
const { success } = await response.json();
if (success) {
logger.info(`CAPTCHA ${ip} successfully solved captcha`);
return true;
}
logger.info(`CAPTCHA Token for ${ip} not ok`);
} else {
logger.warn(`CAPTCHA Recapcha answer for ${ip} not ok`);
}
return false;
}
/*
* https://docs.hcaptcha.com/
@ -55,24 +24,12 @@ async function verifyHCaptcha(
token: string,
ip: string,
): Promise<boolean> {
const response = await fetch(HCAPTCHA_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `response=${token}&secret=${CAPTCHA_SECRET}&remoteip=${ip}`,
});
if (response.ok) {
const { success } = await response.json();
if (success) {
logger.info(`CAPTCHA ${ip} successfully solved captcha`);
return true;
}
logger.info(`CAPTCHA Token for ${ip} not ok`);
} else {
// eslint-disable-next-line max-len
logger.warn(`CAPTCHA hCapcha answer for ${ip} not ok ${await response.text()}`);
const success = true;
if (success) {
logger.info(`CAPTCHA ${ip} successfully solved captcha`);
return true;
}
logger.info(`CAPTCHA Token for ${ip} not ok`);
return false;
}
@ -88,24 +45,10 @@ export async function verifyCaptcha(
ip: string,
): Promise<boolean> {
try {
if (!CAPTCHA_METHOD) {
return true;
}
const key = `human:${ip}`;
switch (CAPTCHA_METHOD) {
case 1:
if (!await verifyReCaptcha(token, ip)) {
return false;
}
break;
case 2:
if (!await verifyHCaptcha(token, ip)) {
return false;
}
break;
default:
// nothing
if (!await verifyHCaptcha(token, ip)) {
return false;
}
await redis.setAsync(key, '', 'EX', TTL_CACHE);
@ -123,7 +66,7 @@ export async function verifyCaptcha(
* @return boolean true if needed
*/
export async function needCaptcha(ip: string) {
if (!CAPTCHA_METHOD) {
if (!CAPTCHA_URL) {
return false;
}