CVE-2014-125028 (published 2022-12-31T20:15Z)

A vulnerability was found in valtech IDP Test Client and classified as problematic. Affected by this issue is some unknown functionality of the file python-flask/main.py. The manipulation leads to cross-site request forgery. The attack may be launched remotely. The name of the patch is f1e7b3d431c8681ec46445557125890c14fa295f. It is recommended to apply a patch to fix this issue. The identifier of this vulnerability is VDB-217148.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         

Fix information:
repo_url: https://github.com/valtech/valtech-idp-test-clients
commit_details: repo_url: https://github.com/valtech/valtech-idp-test-clients
author: Jonas Oscarsson
author_date: 2014-09-25 15:52:10+02:00
msg: Add CSRF protection using state to python-flask
repository: repo_url: https://github.com/valtech/valtech-idp-test-clients
repo_name: valtech/valtech-idp-test-clients
description: Test/example clients in various languages for integrating with Valtech IDP
repo_language: C#
owner: valtech
file_changes: filename: main.py
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,8 @@
-from flask import Flask, render_template, request, session, redirect
+from flask import Flask, render_template, request, session, redirect, make_response
 import os, sys
 import requests
 import jwt
+import uuid
 
 CLIENT_ID = 'valtech.idp.testclient.local'
 CLIENT_SECRET = os.environ.get('CLIENT_SECRET')
@@ -27,12 +28,27 @@ def index():
 @app.route('/sign-in')
 def sign_in():
   if session.get('signed_in') != None: return redirect('/')
-  authorize_url = 'https://stage-id.valtech.com/oauth2/authorize?response_type=%s&client_id=%s&scope=%s' % ('code', CLIENT_ID, 'email openid')
-  return redirect(authorize_url)
+
+  # state is used for CSRF protection. the client generates a value and stores it
+  # for the user somewhere (in a cookie or in a session). it then passes the same value
+  # in the state parameter in the authorize request. IDP will mirror the state value
+  # to the redirect URI. the client should then make sure the state value it has stored
+  # matches what it receives in the callback
+  state = str(uuid.uuid4())
+
+  authorize_url = 'https://stage-id.valtech.com/oauth2/authorize?response_type=%s&client_id=%s&scope=%s&state=%s' % ('code', CLIENT_ID, 'email openid', state)
+
+  resp = make_response(redirect(authorize_url))
+  resp.set_cookie('python-flask-csrf', state)
+  return resp
 
 @app.route('/sign-in/callback')
 def sign_in_callback():
   code = request.args.get('code')
+  state = request.args.get('state')
+
+  if state != request.cookies.get('python-flask-csrf'):
+    raise Exception("Possible CSRF detected (state does not match stored state)")
 
   # as both scope openid and email was requested on authorize request above, the client
   # will receive both an access_token (according to OAuth 2) AND an id_token (according to OpenID Connect)
@@ -49,8 +65,10 @@ def sign_in_callback():
 
   session['signed_in'] = True
   session['email'] = user_info['email']
-  
-  return redirect('/')
+
+  resp = make_response(redirect('/'))
+  resp.set_cookie('python-flask-csrf', '', expires=0)
+  return resp
 
 @app.route('/sign-out')
 def sign_out():

programming_language: Python