{"title":"Open Redirect via Unvalidated Redirect Target","language":"Python","severity":"Medium","cwe":"CWE-601","source_lines":[11],"flow_lines":[11,15],"sink_lines":[15],"vulnerable_code":"from flask import Flask, request, redirect\nimport boto3\n\napp = Flask(__name__)\ns3_client = boto3.client('s3')\n\n@app.route('/cloud/resource-access')\ndef grant_temporary_access():\n    resource_id = request.args.get('resource_id')\n    bucket_name = request.args.get('bucket')\n    return_path = request.args.get('return_path', '/dashboard')\n    if resource_id and bucket_name:\n        presigned_url = s3_client.generate_presigned_url('get_object', Params={'Bucket': bucket_name, 'Key': resource_id}, ExpiresIn=3600)\n        return redirect(return_path + '?presigned=' + presigned_url)\n    return redirect(return_path)","explanation":"The application accepts user-controlled input via the 'return_path' parameter without any validation and directly passes it to the redirect() function. An attacker can supply an external URL (e.g., https://evil.com) to redirect users to malicious sites after legitimate cloud operations, enabling phishing attacks.","remediation":"The fix adds a validation function `is_safe_redirect_path()` that ensures the `return_path` parameter is a relative path on the same host by rejecting any URL containing a scheme, netloc, or protocol-relative prefix. If validation fails, the redirect defaults to the safe '/dashboard' path, preventing attackers from redirecting users to external malicious sites.","secure_code":"from flask import Flask, request, redirect, abort\nfrom urllib.parse import urlparse\nimport boto3\n\napp = Flask(__name__)\ns3_client = boto3.client('s3')\n\nALLOWED_RETURN_PATHS = ['/dashboard', '/files', '/shared', '/downloads']\n\ndef is_safe_redirect_path(target):\n    \"\"\"Validate that the redirect target is a safe relative path.\"\"\"\n    parsed = urlparse(target)\n    # Reject any URL with a scheme or netloc (external URLs)\n    if parsed.scheme or parsed.netloc:\n        return False\n    # Reject protocol-relative URLs (e.g., //evil.com)\n    if target.startswith('//'):\n        return False\n    # Ensure the path starts with / (relative to current host)\n    if not target.startswith('/'):\n        return False\n    return True\n\n@app.route('/cloud/resource-access')\ndef grant_temporary_access():\n    resource_id = request.args.get('resource_id')\n    bucket_name = request.args.get('bucket')\n    return_path = request.args.get('return_path', '/dashboard')\n    \n    # Validate return_path to prevent open redirect\n    if not is_safe_redirect_path(return_path):\n        return_path = '/dashboard'\n    \n    if resource_id and bucket_name:\n        presigned_url = s3_client.generate_presigned_url('get_object', Params={'Bucket': bucket_name, 'Key': resource_id}, ExpiresIn=3600)\n        return redirect(return_path + '?presigned=' + presigned_url)\n    return redirect(return_path)"}