Compare commits

...

3 Commits

Author SHA1 Message Date
e2465e2874 fix field control 2020-10-08 20:17:48 +02:00
8f88cd6d2c tested timer field 2020-09-17 16:19:39 +02:00
e66ac2e8bd First work on timer field against bots 2020-09-17 16:08:46 +02:00
3 changed files with 43 additions and 16 deletions

View File

@ -74,6 +74,14 @@ function jeanCloudContactFormIntercept (formId, notifier) {
loadingText.classList.add("contact-mailer-sending");
loadingText.textContent = 'Envoi en cours…'
submitButton.after(loadingText)
/* Add the filling timer in seconds */
const timerField = document.createElement('input')
timerField.value = Math.round((Date.now() - contactMailerPageLoadedTime) / 1000)
timerField.name = 'timerfield'
timerField.hidden = 'hidden'
formElem.appendChild(timerField)
/* XHR */
fetch(formElem.action, {
method: formElem.method,
@ -98,6 +106,9 @@ function jeanCloudContactFormIntercept (formId, notifier) {
loadingText.parentNode.removeChild(loadingText)
notifier.error('Impossible denvoyer le formulaire. Vérifiez votre connexion internet ou réessayez plus tard.')
})
/* Remove timer field after xhr. So we can try again. */
formElem.removeChild(timerField)
}
}
@ -115,3 +126,5 @@ function jeanCloudContactFormIntercept (formId, notifier) {
// cat style.css | openssl dgst -sha384 -binary | openssl base64 -A
document.head.appendChild(link);
})()
var contactMailerPageLoadedTime = Date.now()

43
main.py
View File

@ -46,7 +46,6 @@ class EnableCors(object):
return _enable_cors
app = application = bottle.Bottle(catchall=False)
#app.install(EnableCors())
##################################################### Configuration ############################################
@ -86,7 +85,7 @@ smtp_server_password = get_env('SMTP_SERVER_PASSWORD')
smtp_server_sender = get_env('SMTP_SERVER_SENDER')
# Get mongodb connection
mongodb_host = get_env('MONGODB_HOST')
mongodb_host = get_env('MONGODB_HOST')
mongodb_port = get_env('MONGODB_PORT', '27017')
mongodb_dbname = get_env('MONGODB_DBNAME', 'contact_mailer')
@ -134,16 +133,24 @@ def submission ():
response.status = 500
return resp('error', 'La base de donnée nest pas accessible.')
try:
subject_fields = fill_fields(request, get_fields(form['subject']))
content_fields = fill_fields(request, get_fields(form['content']))
# Did the bot filled the honeypot field?
if 'honeypotfield' in form and form['honeypotfield'] in request.forms and request.forms.get(form['honeypotfield']) != '':
# Did the bot filled the honeypot field?
if 'honeypotfield' in form and form['honeypotfield'] in request.forms and request.forms.get(form['honeypotfield']) != '':
response.status = 400
print('honeypotfield')
return resp('error', 'We identified you as a bot. If this is an error, try to contact us via another way.')
# Is the js timer enabled?
if 'timerdelay' in form:
# Did it work?
if 'timerfield' not in request.forms or int(request.forms.get('timerfield')) < int(form['timerdelay']):
print('timer : {}/{}'.format(request.forms.get('timerfield'), form['timerdelay']))
response.status = 400
return resp('error', 'We identified you as a bot. If this is an error, try to contact us via another way.')
try:
subject_fields = fill_fields(request, get_fields(form['subject']))
content_fields = fill_fields(request, get_fields(form['content']))
except MissingParameterException as e:
response.status = 404
response.status = 400
return resp('error', str(e))
subject = re.sub(form_regex, r'{\1}', form['subject']).format(**subject_fields)
@ -184,9 +191,13 @@ def fill_fields(request, fields):
"""Look for fields in request and fill fields dict with values or let default ones. If the value is required, throw exception."""
for field in fields:
if field in request.forms:
if request.forms.get(field).strip() == '' and fields[field] is None: # If empty and mandatory
raise MissingParameterException("Le champs {} doit être rempli".format(field))
fields[field] = request.forms.getunicode(field)
if fields[field] is None: # if unicode failed
fields[field] = request.forms.get(field)
if fields[field] is None: # if get failed too
raise Exception("Error, field '{}' not gettable".format(field))
elif fields[field] is None:
raise MissingParameterException("Le champs {} est obligatoire".format(field))
return fields
@ -264,10 +275,6 @@ def create_form ():
response.status = 400
return resp('error', 'Le champs « contenu » est requis')
if 'honeypotfield' in request.forms:
honeypotfield = request.forms.getunicode('honeypotfield')
else:
honeypotfield = None
# Getting from address
if 'mail' in request.forms:
@ -284,14 +291,19 @@ def create_form ():
# TODO limit the insertion rate
token = ''.join(random.sample(token_chars, token_len))
try:
inserted = mongodb_database['forms'].insert_one({
newEntry = {
'mail': mail,
'content': content,
'subject': subject,
'user_id': user['_id'],
'token': token,
'honeypotfield': honeypotfield,
})
}
if 'honeypotfield' in request.forms:
newEntry['honeypotfield'] = request.forms.getunicode('honeypotfield')
if 'timerdelay' in request.forms:
newEntry['timerdelay'] = request.forms.getunicode('timerdelay')
inserted = mongodb_database['forms'].insert_one(newEntry)
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return resp('error', 'La base de donnée nest pas accessible')
@ -411,6 +423,7 @@ def delete_user (username):
##################################################### app startup ############################################
if __name__ == '__main__':
app.install(EnableCors())
bottle.run(app=StripPathMiddleware(app), host=listen_address, port=listen_port, debug=True)
else:
prod_app = StripPathMiddleware(app)

View File

@ -7,7 +7,8 @@
<body>
<div id="contact-mailer-message"></div>
<form action="http://localhost:8080/submit" method="POST" id="contact-mailer-form">
<input type="hidden" name="token" value="sYMXDz5UKuRF38LbQl20ikrmp7nhHcxTCgGZodqAaBtSvPOV4f" />
<noscript>Les protections anti-spam, nécéssitent lutilisation de javascript. Rien dintrusif normalement.</noscript>
<input type="hidden" name="token" value="PK8gQHDx9VoJ7yuEhbj5iCZkcUOAqTYlRSN14XFtdfr3LBs0zn" />
<div>
<label for="nom">Votre nom&nbsp;:</label>
<input type="text" name="nom" required="required"/>