Email verification is a crucial aspects of web and mobile application development. When implemented correctly, it ensures that the users are genuine, reduces spam accounts, and maintains the integrity of your user database. However, many developers make common mistakes that can compromise the effectiveness of email verification. In this blog post, we’ll explore these pitfalls and how to avoid them to enhance your user’s experience and maintain a secure, efficient system.
A common mistake developers make is relying solely on regular expressions for email validation. While regex can swiftly check the syntax of an email address, it doesn’t confirm whether the email address actually exists or is deliverable.
Combine regular expressions with real-time email verification APIs that check MX records (Mail Exchange records) and SMTP status. Services like Mailgun, SendGrid, and ZeroBounce provide APIs that can validate the existence of an email address beyond just its format.
import re
import requests
def is_valid_email(email):
regex = re.compile(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
# Check the pattern
if not regex.match(email):
return False
# Check existence using an API, e.g., Mailgun
response = requests.get("https://api.mailgun.net/v4/address/validate", params={"address": email}, auth=("api", "YOUR_API_KEY"))
result = response.json()
return result["is_valid"]
email = "test@example.com"
print(is_valid_email(email))
If users enter an invalid email address, poor or generic error messages can frustrate them and lead to higher abandonment rates. Generic messages like “Invalid email address” don’t provide specific guidance on what went wrong.
Provide clear and informative error messages. Tailor the messages to what the validation logic detects, such as invalid domain, invalid characters, or disposable email addresses.
function validateEmail(email) {
const regex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if (!regex.test(email)) {
return "Email format is invalid. Please enter a valid email.";
}
// Example additional validation
const domain = email.split('@')[1];
if (domain === "tempmail.com") {
return "Disposable email addresses are not allowed.";
}
return "Email is valid.";
}
const email = "user@tempmail.com";
console.log(validateEmail(email));
Some applications don’t handle email address changes by users properly. If a user wants to change their registered email address, failing to re-verify the new email address can lead to issues like account recovery problems.
Always re-verify the user's new email address. Send a verification email to the new address and only update the user's profile once the new address is validated.
def update_email(user_id, new_email):
if is_valid_email(new_email):
send_verification_email(new_email)
print(f"Verification email sent to {new_email}")
# Update the user's email after they verify it
# db.update_user_email(user_id, new_email)
else:
print("The new email address is not valid.")
Restricting email validation to certain domains or using ASCII-only characters in email addresses can exclude a significant portion of global users. Many non-English speaking countries have domains and email providers that may not be covered by such restrictive validation.
Use libraries and services that support internationalized email addresses. The IETF has standardized email address internationalization in RFC 6530, and many modern libraries have adopted these standards.
import email_validator
def validate_international_email(email):
try:
v = email_validator.validate_email(email, allow_smtputf8=True)
return f"Email ({v['email']}) is valid."
except email_validator.EmailNotValidError as e:
return str(e)
email = "用户@例子.广告"
print(validate_international_email(email))
Forgetting to implement rate limiting on email verification endpoints can lead to abuse. Malicious users can launch a Denial-of-Service (DoS) attack by sending high volumes of verification requests.
Implement rate limiting and CAPTCHA mechanisms to prevent abuse. Use services like reCAPTCHA to ensure that real humans are interacting with your form, and leverage tools like Redis to track request rates.
from flask import Flask, request, jsonify
import time
import redis
app = Flask(__name__)
r = redis.Redis()
@app.route('/verify', methods=['POST'])
def verify_email():
ip = request.remote_addr
email = request.form.get('email')
# Rate limiting
if r.exists(ip) and int(r.get(ip)) >= 5:
return jsonify({"error": "Too many requests. Please try again later."})
if not is_valid_email(email):
return jsonify({"error": "Invalid email format."})
send_verification_email(email)
r.incr(ip)
r.expire(ip, 3600)
return jsonify({"message": "Verification email sent."})
if __name__ == "__main__":
app.run()
Sending verification emails without secure links can pose significant security risks. If the verification link is not secured, it can be hijacked, leading to account takeovers.
Generate secure, time-limited tokens for verification links, and use HTTPS to ensure safe communication. Ensure tokens are stored securely and invalidate them after a single use or a short period.
import hashlib
import time
import hmac
def generate_secure_token(email, secret_key):
timestamp = int(time.time())
message = f"{email}{timestamp}".encode('utf-8')
secret = secret_key.encode('utf-8')
token = hmac.new(secret, message, hashlib.sha256).hexdigest()
return token, timestamp
def validate_token(token, email, secret_key, timestamp, expiration=3600):
now = int(time.time())
if now > timestamp + expiration:
return False
expected_token, _ = generate_secure_token(email, secret_key)
return hmac.compare_digest(token, expected_token)
secret_key = "your_secret_key"
email = "user@example.com"
token, timestamp = generate_secure_token(email, secret_key)
print(validate_token(token, email, secret_key, timestamp))
If the email verification process takes too long or is too complicated, users may abandon the registration process. Long delays in sending the verification email or a cumbersome verification flow can drive users away.
Streamline the verification process to minimize friction. Ensure emails are sent promptly and UI/UX is user-friendly. Provide clear instructions and keep users informed about each step through progress indicators or notifications.
<form id="signup-form" method="POST" action="/signup">
<input type="email" id="email" name="email" placeholder="Your email">
<button type="submit">Sign Up</button>
</form>
<div id="notification" style="display:none;">Verification email has been sent. Please check your inbox.</div>
<script>
document.getElementById("signup-form").addEventListener("submit", function(event){
event.preventDefault();
fetch('/signup', {
method: 'POST',
body: new FormData(this),
})
.then(response => response.json())
.then(data => {
if(data.message){
document.getElementById("notification").style.display = "block";
document.getElementById("notification").innerText = data.message;
}
}).catch(error => {
console.error('Error:', error);
});
});
</script>
Emails sent from an unverified domain or a free email service may be marked as spam by recipient email providers. This can significantly reduce the deliverability rate of your verification emails.
Always verify your sending domain with your email service provider and set up proper DNS records like SPF, DKIM, and DMARC. This will improve your email deliverability and ensure your verification emails land in users’ inboxes.
v=spf1 include:spf.example.com ~all
default._domainkey IN TXT ("v=DKIM1; k=rsa; p=...")
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com"
Email verification is a critical component that ensures the validity and security of user accounts on your platform. By avoiding these common mistakes, you can improve user experience, maintain a high level of security, and ensure the integrity of your user demographic.
Implementing adequate validation logic, providing clear user feedback, addressing email changes properly, supporting international addresses, enforcing rate limiting, adhering to security best practices, enhancing user experience, and verifying the sender domain are all essential practices. By following these guidelines, you can create a robust and user-friendly email verification system that enhances both security and user satisfaction.
For more insights and best practices in web and mobile development, stay tuned to our blog!