import base64, json, time import os, sys, binascii from dataclasses import dataclass, asdict from typing import Dict, Tuple from secret import KEY, ADMIN_PASSWORD from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from flask import ( Flask, render_template, render_template_string, request, redirect, url_for, flash, session, )
app = Flask(__name__) app.secret_key = KEY
@dataclass(kw_only=True) class APPUser: name: str password_raw: str register_time: int
users: Dict[str, APPUser] = { "admin": APPUser(name="admin", password_raw=ADMIN_PASSWORD, register_time=-1) }
def validate_cookie(cookie: str) -> bool: //判断cookie值是否有效 if not cookie: return False
try: cookie_encrypted = base64.b64decode(cookie, validate=True) except binascii.Error: return False
if len(cookie_encrypted) < 32: //至少由16字节的iv和16字节加密数据构成 return False
try: iv, padded = cookie_encrypted[:16], cookie_encrypted[16:] //分别提取前16位为iv和16位后为填充 cipher = AES.new(KEY, AES.MODE_CBC, iv) cookie_json = cipher.decrypt(padded) except ValueError: return False
try: _ = json.loads(cookie_json) except Exception: return False
return True
def parse_cookie(cookie: str) -> Tuple[bool, str]: //解析cookie并给出信息 if not cookie: return False, ""
try: cookie_encrypted = base64.b64decode(cookie, validate=True) except binascii.Error: return False, ""
if len(cookie_encrypted) < 32: return False, ""
try: iv, padded = cookie_encrypted[:16], cookie_encrypted[16:] cipher = AES.new(KEY, AES.MODE_CBC, iv) decrypted = cipher.decrypt(padded) cookie_json_bytes = unpad(decrypted, 16) //使用unpad去除填充 cookie_json = cookie_json_bytes.decode() except ValueError: return False, ""
try: cookie_dict = json.loads(cookie_json) except Exception: return False, ""
return True, cookie_dict.get("name")
def generate_cookie(user: APPUser) -> str: //产生含用户信息的cookie cookie_dict = asdict(user) cookie_json = json.dumps(cookie_dict) cookie_json_bytes = cookie_json.encode() iv = os.urandom(16) padded = pad(cookie_json_bytes, 16) cipher = AES.new(KEY, AES.MODE_CBC, iv) encrypted = cipher.encrypt(padded) return base64.b64encode(iv + encrypted).decode()
@app.route("/") def index(): if validate_cookie(request.cookies.get("jwbcookie")): return redirect(url_for("home")) return redirect(url_for("login"))
@app.route("/register", methods=["GET", "POST"]) def register(): if request.method == "POST": user_name = request.form["username"] password = request.form["password"] if user_name in users: flash("Username already exists!", "danger") else: users[user_name] = APPUser( name=user_name, password_raw=password, register_time=int(time.time()) ) flash("Registration successful! Please login.", "success") return redirect(url_for("login")) return render_template("register.html")
@app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": username = request.form["username"] password = request.form["password"] if username in users and users[username].password_raw == password: resp = redirect(url_for("home")) resp.set_cookie("jwbcookie", generate_cookie(users[username])) return resp else: flash("Invalid credentials. Please try again.", "danger") return render_template("login.html")
@app.route("/home") def home(): valid, current_username = parse_cookie(request.cookies.get("jwbcookie")) if not valid or not current_username: return redirect(url_for("logout"))
user_profile = users.get(current_username) if not user_profile: return redirect(url_for("logout"))
if current_username == "admin": payload = request.args.get("payload") html_template = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Home</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}"> </head> <body> <div class="container"> <h2 class="text-center">Welcome, %s !</h2> <div class="text-center"> Your payload: %s </div> <img src="{{ url_for('static', filename='interesting.jpeg') }}" alt="Embedded Image"> <div class="text-center"> <a href="/logout" class="btn btn-danger">Logout</a> </div> </div> </body> </html> """ % ( current_username, payload, ) else: html_template = ( """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Home</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}"> </head> <body> <div class="container"> <h2 class="text-center">server code (encoded)</h2> <div class="text-center" style="word-break:break-all;"> {%% raw %%} %s {%% endraw %%} </div> <div class="text-center"> <a href="/logout" class="btn btn-danger">Logout</a> </div> </div> </body> </html> """ % base64.b64encode(open(__file__, "rb").read()).decode() ) return render_template_string(html_template)
@app.route("/logout") def logout(): resp = redirect(url_for("login")) resp.delete_cookie("jwbcookie") return resp
if __name__ == "__main__": app.run()
|