From c4383e25505d9d7bfd274191e57cb8117b43b7ae Mon Sep 17 00:00:00 2001 From: Evan Niederwerfer Date: Sun, 26 Jan 2025 22:37:34 -0500 Subject: [PATCH] Added features and security & stability improvements --- accounts.py | 9 +++ codelistener.py | 151 ++++++++++++++++++++++++++++++++++++++---------- db.py | 92 +++++++++++++++++++++++++++++ sendmail.py | 34 +++-------- verification.py | 58 +++++-------------- 5 files changed, 241 insertions(+), 103 deletions(-) create mode 100644 accounts.py create mode 100644 db.py diff --git a/accounts.py b/accounts.py new file mode 100644 index 0000000..04ded83 --- /dev/null +++ b/accounts.py @@ -0,0 +1,9 @@ +from db import * + +class accounts: + def register(email, password): + if (db.add_account(email, password)): + print("Registered " + str(email) + " with password " + str(password)) + return True + def authenticate_account(email, password): + pass \ No newline at end of file diff --git a/codelistener.py b/codelistener.py index 1e9f58f..9476eb8 100644 --- a/codelistener.py +++ b/codelistener.py @@ -1,21 +1,35 @@ from http.server import BaseHTTPRequestHandler, HTTPServer from socket import * -import requests from sendmail import mail from verification import otp +from db import * +from accounts import * +from urllib.parse import urlparse, parse_qs +import os.path class MyRequestHandler(BaseHTTPRequestHandler): + + base_path = "/home/arcodeskel/Desktop/Verification Platt Discord/" + def do_GET(self): - self.send_response(200) - self.send_header("Content-type", "text/html") - if self.path == '/': - self.path = '/pages/index.html' + + requested_path = os.path.join(self.base_path, "pages", "index.php") + + if not os.path.abspath(requested_path).startswith(os.path.abspath(self.base_path)): + self.send_response(403) + self.end_headers() + self.wfile.write(b"Forbidden") + return + try: - file_to_open = open(self.path[1:]).read() - self.send_response(200) + with open(requested_path, 'r') as file: + file_to_open = file.read() + self.send_response(200) + except: file_to_open = "File Not Found" self.send_response(404) + self.end_headers() self.wfile.write(bytes(file_to_open, 'utf-8')) @@ -24,45 +38,118 @@ class MyRequestHandler(BaseHTTPRequestHandler): # getting the content length from the header information and then rfile is the POST request, with content_length being the number of bytes it needs to grab (optional for if you want to grab less bytes for whatever reason) content_length = int(self.headers['Content-Length']) data_input = bytes.decode(self.rfile.read(content_length)) + parsed_data = parse_qs(data_input) if (data_input.startswith("email=")): - data_str = str(data_input.replace('%40', '@')) - formatted = data_str.replace('email=', '') - mail.send(formatted, mail.gen_code(formatted)) - self.path = '/pages/otp.html' + + email = parsed_data.get('email', [None])[0] # defaults to none if email is not found + password = parsed_data.get('passwd', [None])[0] + + code = mail.gen_code() + + db.add_session(email, password, code) + mail.send(email, code) + + + requested_path = os.path.join(self.base_path, "pages", "otp.php") + + if not os.path.abspath(requested_path).startswith(os.path.abspath(self.base_path)): + self.send_response(403) + self.end_headers() + self.wfile.write(b"Forbidden") + return + try: - file_to_open = open(self.path[1:]).read() - self.send_response(200) + with open(requested_path, 'r') as file: + file_to_open = file.read() + if email: + file_to_open = file_to_open.replace('', email) # Replace a placeholder in the HTML + self.send_response(200) except: file_to_open = "File Not Found" self.send_response(404) self.end_headers() self.wfile.write(bytes(file_to_open, 'utf-8')) - if (data_input.startswith("verifEmail=")): - data_str = str(data_input.replace('%40', '@')) - - # get email - formatted_verifemail = data_str.replace('verifEmail=', '') - formatted_verifnums = formatted_verifemail.replace('&verifOtp=', '') - email = formatted_verifnums[:-4] - - #get otp code - otp_code = data_str[-4:] - - print(email) - print(otp) - if (otp.authenticate(email, otp_code)): - print("OK!") - else: - print("BAD!") - - + if (data_input.startswith("verifEmail=")): + + email = parsed_data.get('verifEmail', [None])[0] # defaults to none if email is not found + otp_code = parsed_data.get('verifOtp', [None])[0] + + if (db.get_session(email) is not None or False): + if (otp.authenticate_otp(email, otp_code)): + if (accounts.register(email, db.get_session_passwd(email))): + db.del_session(email) + requested_path = os.path.join(self.base_path, "pages", "success.html") + if not os.path.abspath(requested_path).startswith(os.path.abspath(self.base_path)): + self.send_response(403) + self.end_headers() + self.wfile.write(b"Forbidden") + return + + try: + with open(requested_path, 'r') as file: + file_to_open = file.read() + self.send_response(200) + + except: + file_to_open = "File Not Found" + self.send_response(404) + + self.end_headers() + self.wfile.write(bytes(file_to_open, 'utf-8')) + else: + print("error handling") + + else: + db.del_session(email) + requested_path = os.path.join(self.base_path, "pages", "success.html") + if not os.path.abspath(requested_path).startswith(os.path.abspath(self.base_path)): + self.send_response(403) + self.end_headers() + self.wfile.write(b"Forbidden") + return + + try: + with open(requested_path, 'r') as file: + file_to_open = file.read() + self.send_response(200) + + except: + file_to_open = "File Not Found" + self.send_response(404) + + self.end_headers() + self.wfile.write(bytes(file_to_open, 'utf-8')) + else: + db.del_session(email) + requested_path = os.path.join(self.base_path, "pages", "success.html") + if not os.path.abspath(requested_path).startswith(os.path.abspath(self.base_path)): + self.send_response(403) + self.end_headers() + self.wfile.write(b"Forbidden") + return + + try: + with open(requested_path, 'r') as file: + file_to_open = file.read() + self.send_response(200) + + except: + file_to_open = "File Not Found" + self.send_response(404) + + self.end_headers() + self.wfile.write(bytes(file_to_open, 'utf-8')) Handler = MyRequestHandler +if (db.init()): + pass +else: + print("Init returned false, there might be an issue!") hostName = "localhost" serverPort = 8080 diff --git a/db.py b/db.py new file mode 100644 index 0000000..6ca11c5 --- /dev/null +++ b/db.py @@ -0,0 +1,92 @@ +import sqlite3 +from datetime import datetime, timedelta + +class db: + def init(): + try: + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS sessions (email text UNIQUE, password integer, code integer, datedel text)''') + cursor.execute('''CREATE TABLE IF NOT EXISTS accounts (email text UNIQUE, password integer)''') + conn.commit() + conn.close() + return True + except: + return False + + def get_session_passwd(email): + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + passwd = '''SELECT password FROM sessions WHERE email = ? LIMIT 1''' + cursor.execute(passwd, (email, )) + fetch_passwd = cursor.fetchone() + conn.close() + return fetch_passwd[0] + + + def del_session(email): + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + session = '''DELETE FROM sessions WHERE email = ?''' + cursor.execute(session, (email, )) + conn.commit() + conn.close() + return True + + def get_session(email): + exists = '''SELECT email FROM sessions WHERE email = ? LIMIT 1''' + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + cursor.execute(exists, (email, )) + result = cursor.fetchone() + conn.close() + return result + + def check_date(email): + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + past_sql = '''SELECT datedel FROM sessions WHERE email = ? LIMIT 1''' + cursor.execute(past_sql, (email, )) + fetch_past = cursor.fetchone() + past = fetch_past[0] + conn.close() + return past + + def fetch_session_code(email): + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + match_sql = '''SELECT code FROM sessions WHERE email = ? LIMIT 1''' + cursor.execute(match_sql, (email, )) + fetch_otp = cursor.fetchone() + otp = fetch_otp[0] + conn.close() + return otp + + def add_session(email, password, code): + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + + insert_email = "INSERT OR IGNORE INTO sessions (email) VALUES (?)" + insert_password = "UPDATE sessions SET password = ? WHERE email = ?" + insert_code = "UPDATE sessions SET code = ? WHERE email = ?" + insert_datedel = "UPDATE sessions SET datedel = ? WHERE email = ?" + + cursor.execute(insert_email, (email, )) + cursor.execute(insert_password, (password, email, )) + cursor.execute(insert_code, (code, email, )) + cursor.execute(insert_datedel, (datetime.now() + timedelta(minutes=5), email, )) + + conn.commit() + conn.close() + return True + + def add_account(email, password): + conn = sqlite3.connect('accounts.db') + cursor = conn.cursor() + insert_email = "INSERT OR IGNORE INTO accounts (email) VALUES (?)" + insert_password = "UPDATE accounts SET password = ? WHERE email = ?" + cursor.execute(insert_email, (email, )) + cursor.execute(insert_password, (password, email, )) + conn.commit() + conn.close() + return True \ No newline at end of file diff --git a/sendmail.py b/sendmail.py index e37ba86..8fdf2ce 100644 --- a/sendmail.py +++ b/sendmail.py @@ -5,53 +5,33 @@ from random import randrange import sqlite3 from datetime import datetime, timedelta from verification import otp +from db import * class mail: def __init__(self, email): self.email = email - def add_db(email, code): - conn = sqlite3.connect('otp.db') - cursor = conn.cursor() - - insert_email = "INSERT OR IGNORE INTO sessions (email) VALUES (?)" - insert_code = "UPDATE sessions SET code = ? WHERE email = ?" - insert_datedel = "UPDATE sessions SET datedel = ? WHERE email = ?" - - cursor.execute(insert_email, (email, )) - cursor.execute(insert_code, (code, email, )) - cursor.execute(insert_datedel, (datetime.now() + timedelta(minutes=5), email, )) - - - conn.commit() - conn.close() - return True - - def gen_code(email): - if (otp.check_code(email)): - code = randrange(1000, 9999) - mail.add_db(email, code) - return code - else: - print("Code already in progress!") + def gen_code(): + code = randrange(1000, 9999) + return code def send(email, code): try: - smtpObj = smtplib.SMTP_SSL('mail.__server__.com', 465) + smtpObj = smtplib.SMTP_SSL('mail.example.com', 465) # Identify yourself to an ESMTP server using EHLO smtpObj.ehlo() # Login to the server (if required) - smtpObj.login('username', 'passwd') + smtpObj.login('email', 'password') # Send an email msg = EmailMessage() msg.set_content(f'You have requested a verification code. Your code is {code}. This code expires in 5 minutes.') msg['Subject'] = 'UWP Community Verification Code' - msg['From'] = "" + msg['From'] = "email@example.com" msg['To'] = email smtpObj.send_message(msg) diff --git a/verification.py b/verification.py index 01a9280..2eb1d06 100644 --- a/verification.py +++ b/verification.py @@ -1,55 +1,25 @@ import sqlite3 from datetime import datetime, timedelta +from db import * class otp: - def connection(): - try: - conn = sqlite3.connect('otp.db') - cursor = conn.cursor() - cursor.execute('''CREATE TABLE IF NOT EXISTS sessions (email text UNIQUE, code integer, datedel text)''') - conn.commit() - conn.close() - return True - except: - return False - - def check_code(email): - if (otp.connection()): - exists = '''SELECT email FROM sessions WHERE email = ? LIMIT 1''' - conn = sqlite3.connect('otp.db') - cursor = conn.cursor() - cursor.execute(exists, (email, )) - result = cursor.fetchone() - if (result == None): - conn.close() - else: - past_sql = '''SELECT datedel FROM sessions WHERE email = ? LIMIT 1''' - cursor.execute(past_sql, (email, )) - fetch_past = cursor.fetchone() - past = fetch_past[0] - present = datetime.now() - if (str(present) > past): - del_sql = '''DELETE FROM sessions WHERE email = ?''' - cursor.execute(del_sql, (email, )) - conn.commit() - conn.close() - return False - return True + if (db.get_session(email) == None): + return False + else: + past = db.check_date(email) + present = datetime.now() + if (str(present) > past): + db.del_session(email) + return False + return True - def authenticate(email, user_otp): + def authenticate_otp(email, user_otp): if (otp.check_code(email)): - conn = sqlite3.connect('otp.db') - cursor = conn.cursor() - match_sql = '''SELECT code FROM sessions WHERE email = ? LIMIT 1''' - cursor.execute(match_sql, (email, )) - fetch_otp = cursor.fetchone() - print(fetch_otp[0]) - print(user_otp) - if (str(fetch_otp[0]) == str(user_otp)): - conn.close() + if (str(db.fetch_session_code(email)) == str(user_otp)): return True else: - conn.close() return False + else: + return -1