Security hardening + bug fixes: rotate admin pw, /api/v1/dedicated/locations alias (404 fix), rate-limit login/signup, SameSite+HttpOnly session cookies, protect /dedicated/poll (403), whitelist admin.py set_field
This commit is contained in:
24
app.py
24
app.py
@@ -17,6 +17,8 @@ import iproyal
|
||||
CFG = db.CFG
|
||||
app = Flask(__name__)
|
||||
app.secret_key = CFG["secret_key"]
|
||||
app.config["SESSION_COOKIE_SAMESITE"] = "Lax" # mitigate CSRF on modern browsers
|
||||
app.config["SESSION_COOKIE_HTTPONLY"] = True # block JS session theft (XSS)
|
||||
|
||||
# simple SQLite-backed rate limiter (shared across gunicorn workers)
|
||||
def _client_ip():
|
||||
@@ -129,6 +131,9 @@ def index():
|
||||
@app.route("/signup", methods=["GET", "POST"])
|
||||
def signup():
|
||||
if request.method == "POST":
|
||||
if not _rate_ok(_client_ip(), limit=20, window=300):
|
||||
flash("Too many attempts. Slow down.", "warn")
|
||||
return render_template("signup.html")
|
||||
username = request.form.get("username", "").strip()
|
||||
password = request.form.get("password", "")
|
||||
if len(username) < 3 or len(password) < 6:
|
||||
@@ -158,6 +163,9 @@ def signup():
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
if request.method == "POST":
|
||||
if not _rate_ok(_client_ip(), limit=20, window=300):
|
||||
flash("Too many attempts. Slow down.", "warn")
|
||||
return render_template("login.html")
|
||||
username = request.form.get("username", "").strip()
|
||||
password = request.form.get("password", "")
|
||||
d = db.get_db()
|
||||
@@ -377,6 +385,16 @@ def dedicated_locations():
|
||||
return jsonify({"error": str(e)}), 502
|
||||
|
||||
|
||||
@app.route("/api/v1/dedicated/locations")
|
||||
def api_dedicated_locations():
|
||||
pid = request.args.get("product", 9, type=int)
|
||||
try:
|
||||
return jsonify({"locations": [{"id": i, "name": n, "in_stock": s}
|
||||
for i, n, s in iproyal.list_locations(pid)]})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 502
|
||||
|
||||
|
||||
@app.route("/dedicated/buy", methods=["POST"])
|
||||
@login_required
|
||||
def dedicated_buy():
|
||||
@@ -405,7 +423,9 @@ def dedicated_buy():
|
||||
|
||||
@app.route("/dedicated/poll", methods=["POST"])
|
||||
def dedicated_poll():
|
||||
"""Admin/agent hook: poll provisioning for pending dedicated orders."""
|
||||
"""Admin hook: poll provisioning for pending dedicated orders."""
|
||||
if not session.get("admin"):
|
||||
abort(403)
|
||||
ok, failed = poll_provisioning()
|
||||
return jsonify({"provisioned": ok, "failed": failed})
|
||||
|
||||
@@ -457,7 +477,7 @@ def poll_provisioning():
|
||||
|
||||
|
||||
# ---------- admin ----------
|
||||
ADMIN_PASSWORD = CFG.get("admin_password", "czapiewski")
|
||||
ADMIN_PASSWORD = CFG.get("admin_password", "")
|
||||
|
||||
|
||||
@app.route("/admin", methods=["GET", "POST"])
|
||||
|
||||
Reference in New Issue
Block a user