I am trying to build a simple numeric captcha for my contact page in Flask. The contact page end-point loads in GET mode and initializes the values of the captcha verification. When I submit the form in POST mode, I expect my user-entered form value to match the captcha value. However, it is not working as expected. I did some troubleshooting with print statements to see how the values are changing and it appears that the captcha variables are getting re-initialised with the POST operation. Can someone please suggest a workaround? My Python code is shared below:
@bp.route('/contact/', methods=('GET', 'POST'))
def contact():
# Initialize captcha values
cap_a = random.randint(0, 9)
cap_b = random.randint(0, 9)
cap_prod = cap_a * cap_b
print(cap_a, cap_b, cap_prod)
if request.method == "POST":
error = None
log_file = current_app.config['LOGFILE']
full_name = request.form['fullname']
email_addr = request.form['email']
phone_no = request.form['phone']
msg_body = request.form['message']
num_prod = request.form['verifycaptcha']
print(cap_a, cap_b, cap_prod)
print(full_name, email_addr, phone_no, msg_body, num_prod)
if not full_name:
error = 'Full name is required.'
elif not email_addr:
error = 'Email address is required.'
elif not msg_body:
error = 'Message body is required.'
if num_prod != cap_prod:
error = 'Incorrect captcha verification.'
if error is None:
# Perform some operations
pass
try:
with current_app.app_context():
mail = Mail()
mail.init_app(current_app)
mail.send(msg)
error = 'Mail sent successfully!'
except:
error = 'Mail engine error encountered. Please retry after some time.'
f = open(log_file, "a")
f.write('[' datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') '] ' error '\n')
f.close()
flash(error)
return render_template('contact.html',num_a = cap_a, num_b = cap_b)
CodePudding user response:
cap_a = random.randint(0, 9)
cap_b = random.randint(0, 9)
cap_prod = cap_a * cap_b
these are 2 random numbers and you are creating cap_prod. You are not saving the cap_prod anywhere. You are instead calculating it again when POST request comes (this time it will be 2 new random numbers)
You need to save the captcha that you created during the GET, and then when the POST comes , compare with the value that was originally sent.
If you are going to support refresh-captcha image in the future (you need to have an API call that will generate a new captcha and save it)
CodePudding user response:
I figured out the solution by storing the captcha variables in the session dictionary of Flask and checking for its existence before re-initialising the values.
def contact():
# Initialize captcha values
if 'product' not in session:
cap_a = random.randint(0, 9)
cap_b = random.randint(0, 9)
session['captcha_a'] = cap_a
session['captcha_b'] = cap_b
session['product'] = cap_a * cap_b
else:
cap_a = session.get('captcha_a')
cap_b = session.get('captcha_b')
if request.method == "POST":
pass
# Rest of the code
Working examples of the above solution can be found at Fadmeter.com and FadURL.com.