# HG changeset patch # User Daniele Nicolodi # Date 1313343088 -7200 # Node ID 1e144e9c18477395a3e1dc9848abbf2aef693102 # Parent ad6e52b8d07c4b4ff22b547aa275cb4ba1ab270c Sanitize the 'next' parameter of the login form before redirecting. diff -r ad6e52b8d07c -r 1e144e9c1847 src/ltpdarepo/__init__.py --- a/src/ltpdarepo/__init__.py Sun Aug 14 19:31:28 2011 +0200 +++ b/src/ltpdarepo/__init__.py Sun Aug 14 19:31:28 2011 +0200 @@ -1,4 +1,4 @@ -from urlparse import urljoin +from urlparse import urlparse, urljoin from flask import Flask, g, request, session, render_template, Markup, redirect, flash, url_for from pkg_resources import get_distribution import MySQLdb as mysql @@ -85,13 +85,21 @@ app.jinja_env.globals['url_for_other_order'] = url_for_other_order +def is_safe_url(target): + ref = urlparse(request.host_url) + test = urlparse(urljoin(request.host_url, target)) + return test.scheme in ('http', 'https') and test.netloc == ref.netloc + + @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': if authenticate(request.form['username'], request.form['password']): session['username'] = request.form['username'] - url = urljoin(url_for('.index'), request.args.get('next', '')) - return redirect(url) + target = request.args.get('next') + if not target or not is_safe_url(target): + target = url_for('index') + return redirect(target) flash('Login failed.', category='error') return render_template('login.html') diff -r ad6e52b8d07c -r 1e144e9c1847 src/ltpdarepo/security.py --- a/src/ltpdarepo/security.py Sun Aug 14 19:31:28 2011 +0200 +++ b/src/ltpdarepo/security.py Sun Aug 14 19:31:28 2011 +0200 @@ -58,10 +58,8 @@ @wraps(func) def decorated(*args, **kwargs): if 'username' not in session: - url = request.path - if url == '/': - url = None - return redirect(url_for('login', next=url)) + target = request.path != '/' and request.path or None + return redirect(url_for('login', next=target)) if self.role not in g.identity.roles: abort(403) return func(*args, **kwargs)