paint-brush
Iwasan ang Email Spam Sa Pamamagitan ng Pagbuo ng Secure Form sa Python sa pamamagitan ng@tom2
338 mga pagbabasa
338 mga pagbabasa

Iwasan ang Email Spam Sa Pamamagitan ng Pagbuo ng Secure Form sa Python

sa pamamagitan ng Rutkat11m2024/09/04
Read on Terminal Reader

Masyadong mahaba; Upang basahin

Ang isang wastong email address ay isang gateway para sa pagtatatag ng direktang komunikasyon, pagbuo ng mga lead, pagkuha ng mga benta, pribadong imbitasyon sa mga online na komunidad, atbp. Hindi namin kailangang umasa sa paggamit ng mga serbisyo ng 3rd party gaya ng Auth0, Facebook, o Google upang magkaroon ng access sa iyong app at mga serbisyo. Gagamitin namin ang mga umiiral nang Python module na nagpapasimple sa paglilinis ng input ng user, pagbuo ng link sa pag-verify, at pakikipag-ugnayan sa database.
featured image - Iwasan ang Email Spam Sa Pamamagitan ng Pagbuo ng Secure Form sa Python
Rutkat HackerNoon profile picture

Ang isang wastong email address ay isang gateway para sa pagtatatag ng direktang komunikasyon, pagbuo ng mga lead, pagkuha ng mga benta, pribadong imbitasyon sa mga online na komunidad, atbp. Huwag itong balewalain dahil nagbabago ang social media. Sa pamamagitan ng mga ebolusyon sa tech, ang email pa rin ang sinubukan at totoong paraan para kumonekta. Pananatilihin naming simple ang mga bagay at hindi ang code mula sa simula dahil ang Python ay may mga umiiral nang module upang matulungan kang mapabilis ang coding.


Mayroon akong mga customer na humiling sa akin na bumuo ng mga email sign-up form para i-promote ang kanilang mga produkto, ngunit wala sa mga customer na iyon ang gustong magbayad ng buwanang bayarin para sa isang 3rd party na off-the-shelf na serbisyo, kaya nakatipid ako sa kanila ng pera sa pamamagitan ng pagbuo ng mga customized na contact form na magagamit nila magpakailanman. Matutulungan kitang gawin ang parehong para sa iyong startup, kliyente, layunin ng marketing, o higit sa lahat, upang mabawasan ang spam.


Ito ay para sa sinumang gustong matutong mag-code sa Python at lalong kapaki-pakinabang para sa mga baguhan na maaaring hindi isaalang-alang ang mga tampok na panseguridad tulad ng pag-filter ng input ng user, pagpapatunay ng mga email address, at email double opt-in. Sa tutorial na ito, sinasaklaw namin ang mga hakbang 1-3:


  1. Pag-filter ng input ng user para sa isang wastong email address
  2. Double opt-in sign-up
  3. Pag-iwas sa bot/spam


Hindi namin kailangang umasa sa paggamit ng mga serbisyo ng 3rd party gaya ng Auth0, Facebook, o Google para magkaroon ng access sa iyong app at mga serbisyo na maaaring mag-shut down sa iyo sa anumang partikular na oras o ibahagi ang iyong data. Panatilihing sa iyo ang data ng iyong app!


Sa simula, dapat ay mayroon kang karanasan sa Python dahil gagamitin namin ang Flask framework na may MySQL database . Ito ay magiging mas masaya (marahil) kaysa sa paggamit ng WordPress, ang pinakasikat na CMS. Kailangan mong magbayad para sa ilang WordPress plugin upang magkaroon ng parehong kakayahan bilang isang libreng Flask extension. Nakagawa na ako dati sa Wordpress (PHP) at mas gusto ko ang Python Flask para sa mga web app kahit na napakahusay ng Wordpress na gumawa ng mga web app.


Gagamitin namin ang mga umiiral nang Python module na nagpapasimple sa paglilinis ng input ng user, pagbuo ng link sa pag-verify, at pakikipag-ugnayan sa database.


Ang bawat snippet ng code ay ipapaliwanag at magsasama ng ilang komento sa code. Kung sakaling hindi ka pa nakagawa ng pagpaparehistro ng user o alam ang mga panloob na gawain, ilalarawan ko ang mga detalye para sa iyo, at pagkatapos ay makikita mo ang panghuling code sa dulo (huwag laktawan ang unahan).


Narito ang isang buod ng mga tampok na aming ipapatupad gaya ng nakasaad sa unang talata:


  1. Maaaring suriin ang isang wastong email address sa pamamagitan ng pag-parse ng input string mula sa user gamit ang isang regular na expression o isang Flask extension. Hindi namin papayagan ang random na text o SQL injection na uri ng mga hack.


  2. Ang double opt-in na paraan ay nangangailangan ng tatanggap na magbigay ng pahintulot para sa iyo na mag-email sa kanila sa pamamagitan ng pagtanggap ng validation link sa kanilang inbox. Pangunahing ginagamit ito upang pigilan ang ibang tao na gamitin ang iyong email address. Pinipigilan din nito ang mga pagsubok na user na kaka-sign up lang at aalis sa kanilang mga account.


  3. Ang pag-iwas sa bot ay maaaring gawin gamit ang isang nakatagong field na hindi ipinapakita sa user ngunit karaniwang awtomatikong pinupunan ng mga bot na nagko-crawl para sa mga vulnerable na form sa pag-sign up, ngunit hindi ito kasing maaasahan ng isang "captcha" mula sa isang serbisyo ng 3rd party.


Magsimula tayo sa pag-coding. Lumikha ng isang gumaganang direktoryo:

 mkdir signup cd signup


Lumikha ng iyong kapaligiran sa Python gamit ang python3 -m venv signup o conda create -n double-opt-contact python3 . Mas gusto ko ang conda, at kung gusto mong matuto nang higit pa, maaari mong basahin ang aking artikulo sa Python environment.


I-install ang mga sumusunod na dependencies:
pip flask flask-mail secure SQLAlchemy Flask-WTF Flask-SQLAlchemy mysql-connector-python bleach

Bilang kahalili, maaari kang magkaroon ng parehong mga dependency na nakalista sa isang requirements.txt file at magpatakbo pip install -r requirements.txt


Lumikha ng app.py file na may kasamang mga sumusunod na dependency:


 from flask import Flask, render_template, request, url_for, redirect, flash from flask_mail import Mail, Message from datetime import datetime from flask_sqlalchemy import SQLAlchemy from sqlalchemy.sql import func from itsdangerous import URLSafeTimedSerializer, SignatureExpired import secrets import bleach


Simulan ang app object gamit ang default na lokasyon ng folder ng template:

 app = Flask(__name__, template_folder='templates')


Ipasok ang iyong sariling data ng configuration ng server gamit ang mga linyang ito:

 secret = secrets.token_urlsafe(32) app.secret_key = secret app.config['SECRET_KEY'] = secret # auto-generated secret key # SQLAlchemy configurations app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector://admin:user@localhost/tablename' # Email configurations app.config['MAIL_SERVER'] = 'smtp.example.com' app.config['MAIL_PORT'] = 465 #check your port app.config['MAIL_USERNAME'] = '[email protected]' app.config['MAIL_PASSWORD'] = 'your_password' app.config['MAIL_USE_TLS'] = True app.config['MAIL_USE_SSL'] = False db = SQLAlchemy(app) mail = Mail(app) sserialzer = URLSafeTimedSerializer(app.config['SECRET_KEY']) #set secret to the serliazer


Sa huli, dapat ay nasa isang .env file ang iyong impormasyon sa config.


Kakailanganin namin ang isang database ng MySQL upang mag-imbak ng mga user na maaaring gawin nang manu-mano o sa pamamagitan ng Python code. Bilang bahagi ng proseso ng pag-aaral, maaari mong ilagay ang sumusunod na code gamit ang command-line o gamit ang Python with app.app_context() db_create_all() na pamamaraan.


Ang na-validate na field ay para sa isang token string na nagbibigay-daan sa double opt-in technique.

 CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(120) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, validated BOOLEAN DEFAULT FALSE );


Ang susunod na seksyon ay gumagamit ng SQLAlchemy's ORM structure upang i-query ang database para sa iyo. Tandaan na ang pangalan ng klase ay dapat tumugma sa iyong pangalan ng talahanayan ng database, kung hindi, magkakaroon ka ng error. Kinakatawan ng db.model ang iyong mga setting ng talahanayan na kinabibilangan ng pangalan ng column, uri nito, haba, key, at null na halaga:


 class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(120), unique=True, nullable=False) created_at = db.Column(db.DateTime, server_default=db.func.now()) validated = db.Column(db.Boolean, default=False)


Kung hindi mo pa man nagagawa ang MySQL database table, magagawa mo ito gamit ang Flask code na ito nang direkta pagkatapos ng class User code block:

 # Create the database table with app.app_context(): db.create_all()


Ngayon, ipinasok namin ang back-end code na 2 pahina/ruta (index, signup), ang email na mensahe, at kumpirmasyon. Kasama sa pahina ng pag-signup ang mga GET/POST na pamamaraan na nagpapahintulot sa form na isumite. Ang bleach object ay isang Python extension na nililinis ang input mula sa user upang matiyak ang seguridad at mabawasan ang mga nakakahamak na script. Pagkatapos ay bubuo ang sserializer ng isang beses na token para i-email ang link sa pag-verify.


 @app.route('/') def index(): return '<h1>Index page</h1>' @app.route('/signup', methods=['GET', 'POST']) def signup(): if request.method == 'POST': email = bleach.clean(request.form.get('email')) # Insert user into the database new_user = User(email=email) try: db.session.add(new_user) db.session.commit() except Exception as e: print(f"Error occurred saving to db: {e}") # Send confirmation email token = sserialzer.dumps(email, salt='email-confirm') msg = Message('Confirm your Email', sender='[email protected]', recipients=[email]) link = url_for('confirm_email', token=token, _external=True) msg.body = f'Your link is {link}' try: mail.send(msg) except Exception as e: print(f"Error occurred sending message: {e}") flash("Error occurred sending message!") return render_template('signup.html') flash('A confirmation email has been sent to your email address.', 'success') return redirect(url_for('index')) return render_template('signup.html')


Bago idagdag ang HTML sign-up form, kumpletuhin natin ang backend sa pamamagitan ng pagdaragdag ng ruta para sa pagpapatunay ng double opt-in na feature. Ginagamit ng rutang ito ang s variable na ginawa namin kanina na bumubuo ng sensitibo sa oras, sikretong token. Tingnan mo ang mga doc para sa mga detalye .


Ang max-age ay ang mga segundo bago mag-expire ang link, kaya sa kasong ito, may 20 minuto ang user upang kumpirmahin ang kanilang email address.


 @app.route('/confirm_email/<token>') def confirm_email(token): try: email = sserialzer.loads(token, salt='email-confirm', max_age=1200) # Token expires after 1 hour except SignatureExpired: return '<h1>The token is expired!</h1>' # Update field in database user = User.query.filter_by(email=email).first_or_404() user.validated = True db.session.commit() return '<h1>Email address confirmed!</h1>'


Ngayon, para sa ubiquitous na pangunahing pahayag na nagsasabi sa Python na isagawa ang script kung ang file ay direktang isinasagawa (kumpara sa isang na-import na module):

 if __name__ == '__main__': app.run()


Bago namin kumpletuhin ang back-end na code na ito, kailangan pa rin namin ang front-end na HTML para sa input ng user. Gagawin namin ito gamit ang built-in na Jinja template ng Flask. Lumikha ng file na pinangalanang templates/signup.html na dapat ay tumutugma sa pangalan sa rutang ginawa mo kanina sa app.py . Bilang default, ginagamit ni Jinja ang direktoryo /templates para sa mga html na file. Maaari mong baguhin ang setting na ito, ngunit para sa tutorial na ito, gagamitin namin ang direktoryo ng /templates ng app.

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Email Sign Up</title> </head> <body> <h1>Sign Up</h1> <form action="{{ url_for('signup') }}" method="POST"> <input type="email" name="email" placeholder="Enter your email" required> <input type="submit" value="Sign Up"> </form> {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} <ul> {% for category, message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} </body> </html>


Ang iyong code ay dapat na gumagana mula sa puntong ito kapag pinatakbo mo ang flask command na pinagana ang pag-debug. Papayagan ka nitong makita ang anumang mga error sa command line pati na rin ang window ng browser:


 flask --app app.py --debug run


Buksan ang iyong browser sa domain na ipinapakita sa command-line (localhost) at dapat mag-render ang index page. Subukang isumite ang form gamit ang isang wastong email address upang matanggap ang link sa pag-verify. Kapag nakuha mo na ang link, ito ay dapat magmukhang http://localhost:5000/confirm_email/InRvbUByYXRldG91cmd1aWRlcy5jb20i.ZteEvQ.7o1_L0uM9Wl8uii7KhJdiWAH , maaari mong sundin ito at makuha ang email address na na-validate dito gamit ang validator na ruta:


 @app.route('/confirm_email/<token>') def confirm_email(token): try: email = sserializer.loads(token, salt='email-confirm', max_age=1200) # Token expires after 1 hour except SignatureExpired: return '<h1>Oops, the token expired!</h1>' # Update field in database user = Users.query.filter_by(email=email).first_or_404() user.validated = True try: db.session.commit() except Exception as e: print(f"Error occurred saving to db: {e}") return '<h1>Email address confirmed!</h1>'


Ang rutang ito ay tumatanggap ng token string na dati nang ipinadala sa iyo at sinusuri ito upang makita kung ito ay tumutugma sa kaukulang database entry. Kung gagawin nito, ia-update nito ang validated field sa True , at maaari kang magpahinga nang alam mong hindi inabandona ang iyong form sa pag-sign up.


Ito ay isang mahalagang hakbang na ginagamit ng lahat ng matagumpay na negosyo sa kanilang mga sistema ng pagpaparehistro at ngayon ay mayroon ka rin nito. Ngunit teka, paano kung makatanggap tayo ng mga pag-atake ng bot na nagsusumite ng mga random na email address nang hindi pinapatunayan ang mga ito? Pagkatapos ay magkakaroon ka ng maruming database na puno ng mga walang kwentang entry. Pigilan natin yan!


Upang maiwasan ang mga pag-atake ng bot o hindi bababa sa pag-iwas sa mga advanced, maaari kang bumuo ng sarili mong solusyon sa pag-ubos ng oras, kabilang ang isang IP limiter na nangangailangan ng in-memory na database gaya ng Redis, o maaari kang gumamit ng 3rd party na serbisyo gaya ng captcha ng Google o hCaptcha.


Sa aming tutorial, magdadagdag kami libreng plano ng hcaptcha . Sa oras ng pagsulat na ito, ang captcha ng google ay hindi libre at hcaptcha ay. Upang gumana ito para sa iyong site, kailangan mong magrehistro sa kanila upang makuha ang API key mula sa captcha.


Kailangan namin ng mga bagong kinakailangan kaya i-install ang mga ito:
pip install flask-hcaptcha requests


Ang mga kahilingan ay kinakailangan upang ipadala ang email address sa hcaptcha para sa pagpapatunay. Kunin ang susi, at isama ang javascript file ng hcaptcha sa iyong HTML signup form. Idagdag ang file sa head ng iyong HTML page at ang iyong site key sa iyong form:


 <head> ... <script src="https://hcaptcha.com/1/api.js" async defer></script> </head> <body> ... <form action="{{ url_for('signup') }}" method="POST"> <input type="email" name="email" placeholder="Enter your email" required> <input type="submit" value="Sign Up"> <div class="h-captcha" data-sitekey="b62gbcc-5cg2-41b2-cd5a-de95dd1eg61h" data-size="compact"></div> </form>


Ang susi ng site sa code na ito ay isang halimbawa; kakailanganin mo ang iyong sarili mula sa libreng plano. Ang key ng site na ito ay nagpapatunay sa iyong form at sinisiyasat ang bisita ng site gamit ang isang komprehensibong listahan ng mga spam bot na kilala ng hcaptcha.


Susunod, baguhin ang iyong app.py file upang isama ang secret key ng hcaptcha (hindi ang site key) sa app.config object, at i-post ang tugon sa hcaptcha bago ito i-save sa sarili mong database.


 app.config['HCAPTCHA_SECRET_KEY'] = 'your-secret-hcaptcha-key' ... @app.route("/signup", methods=['GET', 'POST']) def signup(): if request.method == 'POST': email = bleach.clean(request.form.get('email')) hcaptcha_response = request.form.get('h-captcha-response') # Verify hCaptcha response payload = { 'secret': app.config['HCAPTCHA_SECRET_KEY'], 'response': hcaptcha_response } try: response = requests.post('https://hcaptcha.com/siteverify', data=payload, timeout=10) result = response.json() except requests.exceptions.RequestException as e: print(f"Request failed: {e}") if not result.get('success'): flash('CAPTCHA validation failed, please try again.', 'danger') ... # Insert user into the database new_user = Users(email=email)


Kapag tapos na ito, makikita mo ang icon ng hcaptcha sa iyong sign-up form, at dapat itong paganahin upang maiwasan ang anumang spam. Ngayon, mayroon kang mas matatag na form para sa iyong bagong app.


Kung sakaling makatagpo ka ng anumang mga error o mayroon kang typo sa code, maaari mong suriin ang nakumpletong code sa ang aking github.com


Comment kung gusto mo pa.