paint-brush
Hindari Spam Email dengan Membangun Formulir Aman di Pythonoleh@tom2
338 bacaan
338 bacaan

Hindari Spam Email dengan Membangun Formulir Aman di Python

oleh Rutkat11m2024/09/04
Read on Terminal Reader

Terlalu panjang; Untuk membaca

Alamat email yang valid merupakan gerbang untuk membangun komunikasi langsung, menghasilkan prospek, mendapatkan penjualan, undangan pribadi ke komunitas daring, dll. Kita tidak perlu bergantung pada penggunaan layanan pihak ketiga seperti Auth0, Facebook, atau Google untuk mengakses aplikasi dan layanan Anda. Kita akan menggunakan modul Python yang sudah ada yang menyederhanakan pembersihan masukan pengguna, pembuatan tautan verifikasi, dan komunikasi dengan basis data.
featured image - Hindari Spam Email dengan Membangun Formulir Aman di Python
Rutkat HackerNoon profile picture

Alamat email yang valid merupakan gerbang untuk membangun komunikasi langsung, menghasilkan prospek, mendapatkan penjualan, undangan pribadi ke komunitas daring, dll. Jangan anggap remeh karena media sosial terus berubah. Melalui evolusi teknologi, email masih menjadi cara yang teruji dan benar untuk terhubung. Kita akan menjaga semuanya tetap sederhana dan tidak membuat kode dari awal karena Python memiliki modul yang ada untuk membantu Anda mempercepat pengodean.


Saya pernah memiliki pelanggan yang meminta saya membuat formulir pendaftaran email untuk mempromosikan produk mereka, tetapi tidak ada satu pun pelanggan yang mau membayar biaya bulanan untuk layanan pihak ketiga yang sudah jadi, jadi saya menghemat uang mereka dengan membuat formulir kontak khusus yang dapat mereka gunakan selamanya. Saya dapat membantu Anda melakukan hal yang sama, baik untuk keperluan startup, klien, pemasaran, atau yang terpenting, untuk mengurangi spam.


Tutorial ini ditujukan bagi siapa saja yang ingin belajar membuat kode dalam Python dan sangat berguna bagi pemula yang mungkin tidak mempertimbangkan fitur keamanan seperti memfilter masukan pengguna, memvalidasi alamat email, dan pendaftaran ganda melalui email. Dalam tutorial ini, kami membahas langkah 1-3:


  1. Memfilter masukan pengguna untuk alamat email yang valid
  2. Pendaftaran keikutsertaan ganda
  3. Pencegahan bot/spam


Kami tidak perlu bergantung pada penggunaan layanan pihak ketiga seperti Auth0, Facebook, atau Google untuk mengakses aplikasi dan layanan Anda yang dapat mematikan aplikasi Anda kapan saja atau membagikan data Anda. Jaga data aplikasi Anda tetap milik Anda!


Pertama-tama, Anda harus memiliki pengalaman dalam Python karena kita akan menggunakan framework Flask dengan database MySQL . Ini akan lebih menyenangkan (mungkin) daripada menggunakan WordPress, CMS paling populer. Anda harus membayar beberapa plugin WordPress untuk mendapatkan kemampuan yang sama dengan ekstensi Flask gratis. Saya pernah membuat aplikasi di Wordpress (PHP) dan lebih suka Python Flask untuk aplikasi web meskipun Wordpress sangat mampu membuat aplikasi web.


Kami akan menggunakan modul Python yang ada yang menyederhanakan pembersihan masukan pengguna, pembuatan tautan verifikasi, dan komunikasi dengan basis data.


Setiap potongan kode akan dijelaskan dan akan menyertakan beberapa komentar dalam kode tersebut. Jika Anda belum membuat registrasi pengguna atau mengetahui cara kerja internalnya, saya akan menjelaskan detailnya untuk Anda, lalu Anda dapat melihat kode finalnya di bagian akhir (jangan lewatkan bagian ini).


Berikut adalah ringkasan fitur yang akan kami terapkan seperti yang dinyatakan di paragraf pertama:


  1. Alamat email yang valid dapat diperiksa dengan mengurai string input dari pengguna menggunakan ekspresi reguler atau ekstensi Flask. Kami tidak akan mengizinkan peretasan teks acak atau injeksi SQL.


  2. Metode double opt-in mengharuskan penerima untuk memberikan izin kepada Anda untuk mengirim email kepada mereka dengan menerima tautan validasi ke kotak masuk mereka. Hal ini terutama digunakan untuk mencegah orang lain menggunakan alamat email Anda. Hal ini juga mencegah pengguna uji yang hanya mendaftar dan meninggalkan akun mereka.


  3. Pencegahan bot dapat dilakukan dengan kolom tersembunyi yang tidak diperlihatkan kepada pengguna tetapi umumnya diisi otomatis oleh bot yang mencari formulir pendaftaran yang rentan, tetapi tidak dapat diandalkan seperti "captcha" dari layanan pihak ketiga.


Mari kita mulai membuat kode. Buat direktori kerja:

 mkdir signup cd signup


Buat lingkungan Python Anda menggunakan python3 -m venv signup atau conda create -n double-opt-contact python3 . Saya lebih suka conda, dan jika Anda ingin mempelajari lebih lanjut, Anda dapat membaca artikel lingkungan Python saya.


Instal dependensi berikut:
pip flask flask-mail secure SQLAlchemy Flask-WTF Flask-SQLAlchemy mysql-connector-python bleach

Atau, Anda dapat mencantumkan dependensi yang sama dalam file requirements.txt dan menjalankan pip install -r requirements.txt


Buat file app.py dengan dependensi berikut yang disertakan:


 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


Inisialisasi objek aplikasi dengan lokasi folder templat default:

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


Masukkan data konfigurasi server Anda sendiri menggunakan baris berikut:

 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


Akhirnya, Anda harus memiliki info konfigurasi dalam file .env .


Kita akan memerlukan basis data MySQL untuk menyimpan pengguna yang dapat dibuat secara manual atau dengan kode Python. Sebagai bagian dari proses pembelajaran, Anda dapat memasukkan kode berikut menggunakan baris perintah atau menggunakan metode with app.app_context() db_create_all() Python.


Bidang yang divalidasi adalah untuk string token yang memperbolehkan teknik keikutsertaan ganda.

 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 );


Bagian berikutnya menggunakan struktur ORM SQLAlchemy untuk melakukan kueri pada basis data untuk Anda. Perhatikan bahwa nama kelas harus sesuai dengan nama tabel basis data Anda, jika tidak, Anda akan mendapatkan kesalahan. db.model mewakili pengaturan tabel Anda yang mencakup nama kolom, jenisnya, panjangnya, kuncinya, dan nilai null-nya:


 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)


Jika Anda belum membuat tabel database MySQL secara manual, Anda dapat melakukannya dengan kode Flask ini langsung setelah blok kode class User :

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


Sekarang, kita masukkan kode back-end yang terdiri dari 2 halaman/rute (indeks, pendaftaran), pesan email, dan konfirmasi. Halaman pendaftaran menyertakan metode GET/POST yang memungkinkan formulir dikirimkan. Objek bleach adalah ekstensi Python yang membersihkan input dari pengguna untuk memastikan keamanan dan mengurangi skrip berbahaya. Kemudian sserializer menghasilkan token satu kali untuk mengirim tautan verifikasi melalui email.


 @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')


Sebelum menambahkan formulir pendaftaran HTML, mari selesaikan backend dengan menambahkan rute untuk memvalidasi fitur double opt-in. Rute ini menggunakan variabel s yang kita buat sebelumnya yang menghasilkan token rahasia yang sensitif terhadap waktu. Lihat dokumen untuk rinciannya .


Usia maksimal adalah detik sebelum tautan kedaluwarsa, jadi dalam kasus ini, pengguna memiliki waktu 20 menit untuk mengonfirmasi alamat email mereka.


 @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>'


Sekarang, untuk pernyataan main yang ada di mana-mana yang memberi tahu Python untuk mengeksekusi skrip jika file dieksekusi secara langsung (bukan modul yang diimpor):

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


Sebelum kita melengkapi kode back-end ini, kita masih memerlukan HTML front-end untuk input pengguna. Kita akan melakukannya dengan templat bawaan Jinja Flask. Buat file bernama templates/signup.html yang harus sesuai dengan nama rute yang Anda buat sebelumnya di app.py . Secara default, Jinja menggunakan direktori /templates untuk file html. Anda dapat mengubah pengaturan ini, tetapi untuk tutorial ini, kita akan menggunakan direktori /templates dari aplikasi.

 <!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>


Kode Anda seharusnya berfungsi mulai saat ini saat Anda menjalankan perintah flask dengan debugging diaktifkan. Ini akan memungkinkan Anda melihat kesalahan apa pun di baris perintah dan juga jendela browser:


 flask --app app.py --debug run


Buka browser Anda ke domain yang ditampilkan di baris perintah (localhost) dan halaman indeks akan ditampilkan. Cobalah untuk mengirimkan formulir menggunakan alamat email yang valid untuk menerima tautan verifikasi. Setelah Anda mendapatkan tautan, tampilannya akan seperti http://localhost:5000/confirm_email/InRvbUByYXRldG91cmd1aWRlcy5jb20i.ZteEvQ.7o1_L0uM9Wl8uii7KhJdiWAH , Anda dapat mengikutinya dan memvalidasi alamat email menggunakan rute validator yang ditampilkan di sini:


 @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>'


Rute ini menerima string token yang sebelumnya dikirimkan kepada Anda dan memeriksanya untuk melihat apakah cocok dengan entri basis data terkait. Jika cocok, ia memperbarui bidang validated menjadi True , dan Anda dapat yakin bahwa formulir pendaftaran Anda tidak diabaikan.


Ini adalah langkah penting yang digunakan semua bisnis yang sukses dalam sistem registrasi mereka dan sekarang Anda juga mengalaminya. Tapi tunggu dulu, bagaimana jika kita mendapatkan serangan bot yang mengirimkan alamat email acak tanpa memvalidasinya? Maka Anda akan memiliki basis data yang kotor yang diisi dengan entri yang tidak berguna. Mari kita cegah itu!


Untuk mencegah serangan bot atau setidaknya mengurangi serangan tingkat lanjut, Anda dapat membangun solusi yang memakan waktu, termasuk pembatas IP yang memerlukan basis data dalam memori seperti Redis, atau Anda dapat menggunakan layanan pihak ketiga seperti captcha atau hCaptcha milik Google.


Dalam tutorial kami, kami akan menambahkan paket gratis hcaptcha Pada saat tulisan ini dibuat, captcha Google tidak gratis dan hcaptcha gratis. Agar ini berfungsi di situs Anda, Anda perlu mendaftar dengan mereka untuk mendapatkan kunci API dari captcha.


Kami memerlukan persyaratan baru, jadi instal persyaratan tersebut:
pip install flask-hcaptcha requests


Permintaan diperlukan untuk mengirim alamat email ke hcaptcha untuk validasi. Dapatkan kuncinya, dan integrasikan berkas javascript hcaptcha dengan formulir pendaftaran HTML Anda. Tambahkan berkas ke bagian atas halaman HTML dan kunci situs Anda ke formulir Anda:


 <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>


Kunci situs dalam kode ini adalah contoh; Anda akan memerlukan kunci situs Anda sendiri dari paket gratis. Kunci situs ini memvalidasi formulir Anda dan memeriksa pengunjung situs dengan daftar lengkap bot spam yang dikenal dengan hcaptcha.


Berikutnya, ubah file app.py Anda untuk menyertakan kunci rahasia hcaptcha (bukan kunci situs) dalam objek app.config, dan posting respons ke hcaptcha sebelum menyimpannya ke basis data Anda sendiri.


 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)


Setelah ini selesai, ikon hcaptcha akan muncul di formulir pendaftaran Anda, dan ikon tersebut harus diaktifkan untuk mencegah spam. Sekarang, Anda memiliki formulir yang lebih kuat untuk aplikasi baru Anda.


Jika Anda menemukan kesalahan atau kesalahan ketik pada kode, Anda dapat memeriksa kode yang sudah lengkap di github.com saya


Berikan komentar jika Anda ingin lebih lanjut.