Add verification through ALTCHA
Open-sourced and self-hosted alternative to Google CAPTCHA services. Requires JS, but auto verifies on page load.
This commit is contained in:
parent
97943471f4
commit
497b0da036
8
app/static/altcha.js
Normal file
8
app/static/altcha.js
Normal file
File diff suppressed because one or more lines are too long
@ -8,6 +8,9 @@
|
|||||||
<!-- Encourage indexing -->
|
<!-- Encourage indexing -->
|
||||||
<meta name="robots" content="index, follow">
|
<meta name="robots" content="index, follow">
|
||||||
|
|
||||||
|
<!-- Add altcha.js file -->
|
||||||
|
<script async defer src="{{ url_for('static', filename='altcha.js') }}" type="module"></script>
|
||||||
|
|
||||||
<meta property="og:title" content="Contact | pkrm.dev"/>
|
<meta property="og:title" content="Contact | pkrm.dev"/>
|
||||||
<meta property="og:url" content="https://pkrm.dev/contact"/>
|
<meta property="og:url" content="https://pkrm.dev/contact"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
@ -35,6 +38,8 @@
|
|||||||
<input type="text" name="email" placeholder="Email">
|
<input type="text" name="email" placeholder="Email">
|
||||||
<textarea name="message" placeholder="Message"></textarea>
|
<textarea name="message" placeholder="Message"></textarea>
|
||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Submit">
|
||||||
|
<!-- Add altcha widget which auto verifies on page load and is invisible -->
|
||||||
|
<altcha-widget challengeurl="/altcha-challenge" auto="onload" style="display: none; opacity: 0;"></altcha-widget>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if success %}
|
{% if success %}
|
||||||
@ -204,6 +209,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
altcha-widget {
|
||||||
|
visibility: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Success/Error messages */
|
/* Success/Error messages */
|
||||||
.success-message {
|
.success-message {
|
||||||
color: #71A172;
|
color: #71A172;
|
||||||
|
45
app/views.py
45
app/views.py
@ -2,10 +2,17 @@ import flask
|
|||||||
import os
|
import os
|
||||||
import discord
|
import discord
|
||||||
import dotenv
|
import dotenv
|
||||||
|
import hashlib
|
||||||
|
import secrets
|
||||||
|
import hmac
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import random
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
webhook_url = os.getenv("WEBHOOK_URL")
|
webhook_url = os.getenv("WEBHOOK_URL")
|
||||||
|
hmac_key = random.randbytes(500)
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
@app.route('/', methods=['GET'])
|
||||||
def index():
|
def index():
|
||||||
@ -22,6 +29,25 @@ def contact():
|
|||||||
|
|
||||||
if flask.request.method == 'POST':
|
if flask.request.method == 'POST':
|
||||||
try:
|
try:
|
||||||
|
# Decode payload
|
||||||
|
data = json.loads(base64.b64decode(flask.request.form['altcha']).decode())
|
||||||
|
|
||||||
|
# Validate algorithm
|
||||||
|
if data['algorithm'] != 'SHA-256':
|
||||||
|
return flask.render_template('contact.html', error=True)
|
||||||
|
# Validate challenge
|
||||||
|
expected_challenge = hashlib.sha256(
|
||||||
|
(data['salt'] + str(data['number'])).encode()
|
||||||
|
).hexdigest()
|
||||||
|
if data['challenge'] != expected_challenge:
|
||||||
|
return flask.render_template('contact.html', error=True)
|
||||||
|
# Validate signature
|
||||||
|
signature = hmac.new(hmac_key, data['challenge'].encode(), hashlib.sha256).hexdigest()
|
||||||
|
if data['signature'] != signature:
|
||||||
|
return flask.render_template('contact.html', error=True)
|
||||||
|
|
||||||
|
# All checks passed, send off form data
|
||||||
|
|
||||||
name = flask.request.form['name']
|
name = flask.request.form['name']
|
||||||
email = flask.request.form['email']
|
email = flask.request.form['email']
|
||||||
message = flask.request.form['message']
|
message = flask.request.form['message']
|
||||||
@ -40,6 +66,25 @@ def contact():
|
|||||||
except:
|
except:
|
||||||
return flask.render_template('contact.html', error=True)
|
return flask.render_template('contact.html', error=True)
|
||||||
|
|
||||||
|
@app.route('/altcha-challenge', methods=['GET'])
|
||||||
|
def altcha_challenge():
|
||||||
|
salt = secrets.token_urlsafe(25)
|
||||||
|
secret_number = random.randint(10000, 50000)
|
||||||
|
|
||||||
|
challenge_data = f"{salt}{secret_number}".encode()
|
||||||
|
challenge = hashlib.sha256(challenge_data).hexdigest()
|
||||||
|
|
||||||
|
signature = hmac.new(hmac_key, challenge.encode(), hashlib.sha256).hexdigest()
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'algorithm': 'SHA-256',
|
||||||
|
'challenge': challenge,
|
||||||
|
'salt': salt,
|
||||||
|
'signature': signature
|
||||||
|
}
|
||||||
|
|
||||||
|
return flask.jsonify(response)
|
||||||
|
|
||||||
@app.route('/pgp', methods=['GET'])
|
@app.route('/pgp', methods=['GET'])
|
||||||
def pgp():
|
def pgp():
|
||||||
return flask.render_template('pgp.html')
|
return flask.render_template('pgp.html')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user