HIGH api key exposureflasksaml

Api Key Exposure in Flask with Saml

Api Key Exposure in Flask with Saml — how this specific combination creates or exposes the vulnerability

When a Flask application handles API keys and integrates SAML-based authentication, misconfigurations can expose secrets through logs, error messages, or session handling. API keys are often stored in environment variables or configuration files and injected into requests as bearer tokens or custom headers. SAML in Flask is typically implemented using libraries such as python3-saml, where the service provider (SP) configuration defines endpoints, certificates, and bindings. If the SP configuration is incomplete or debug output is enabled, the framework may inadvertently include sensitive headers or keys in SAML requests or responses.

For example, consider a Flask route that calls an external service using an API key stored in app.config["API_KEY"]. If the route builds a SAML AuthNRequest without sanitizing headers, the API key might be reflected in logs when the request is printed or when an error occurs during signing or serialization. Additionally, Flask’s default development server provides verbose tracebacks that can reveal the full environment, including configuration variables. A compromised SAML endpoint or an improperly secured metadata endpoint could also expose the SP’s entity ID or certificate, indirectly weakening the boundary between authentication and resource access where API keys are used.

Real-world attack patterns include log injection via crafted SAML responses that trigger exceptions, leading to stack traces that contain API key values. OWASP API Security Top 10 category API1:2023 Broken Object Level Authorization can intersect here if access control checks are bypassed due to weak session binding between SAML assertions and API key usage. PCI-DSS and SOC2 controls emphasize protection of credentials; storing or transmitting API keys alongside SAML metadata without encryption or strict scoping increases compliance risk. The interaction between Flask’s routing, session management, and SAML message construction must ensure that sensitive values are never serialized into logs, query strings, or assertion attributes.

Saml-Specific Remediation in Flask — concrete code fixes

Remediation centers on strict separation between authentication context and API key usage, secure configuration of the SAML library, and defensive coding in Flask. Use environment-based secrets with runtime validation, disable debug mode in production, and ensure that SAML settings do not leak into logs or error pages. The following example shows a hardened Flask configuration using python3-saml.

from flask import Flask, request, jsonify
from onelogin.saml2.auth import OneLogin_Saml2_Auth
import os
import logging

app = Flask(__name__)

# Load secrets securely; do not commit to source control
SAML_SETTINGS = {
    'strict': True,
    'debug': False,
    'sp': {
        'entityId': 'https://sp.example.com/metadata',
        'assertionConsumerService': {
            'url': 'https://sp.example.com/acs',
            'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
        },
        'singleLogoutService': {
            'url': 'https://sp.example.com/sls',
            'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
        },
        'x509cert': '',
        'privateKey': ''
    },
    'idp': {
        'entityId': 'https://idp.example.com/metadata',
        'singleSignOnService': {
            'url': 'https://idp.example.com/sso',
            'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
        },
        'x509cert': 'IDP_CERTIFICATE_HERE'
    },
    'security': {
        'nameuid': False,
        'nameid_format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
        'authn_requests_signed': False,
        'logout_request_signed': False,
        'logout_response_signed': False,
        'sign_metadata': False,
        'want_messages_signed': True,
        'want_assertions_signed': True,
        'want_assertions_encrypted': True,
        'want_nameid': True,
        'want_nameid_encrypted': False
    }
}

def init_saml_auth(req):
    return OneLogin_Saml2_Auth(req, custom_base_path='/tmp/saml/')

@app.route('/login')
def login():
    req = prepare_flask_request(request)
    auth = init_saml_auth(req)
    auth.login()
    return '', 200

@app.route('/acs', methods=['POST'])
def acs():
    req = prepare_flask_request(request)
    auth = init_saml_auth(req)
    auth.process_response()
    errors = auth.get_errors()
    if len(errors) == 0:
        attributes = auth.get_attributes()
        # Use API key in a controlled context, e.g., call downstream service
        api_key = os.getenv('EXTERNAL_API_KEY')
        if not api_key:
            app.logger.error('API key not configured')
            return jsonify({'error': 'configuration'}), 500
        # Example: call external service with key in header, isolated from SAML flow
        # response = external_service.get('/resource', headers={'Authorization': f'Bearer {api_key}'})
        return jsonify({'attributes': attributes})
    else:
        app.logger.warning('SAML processing errors: %s', errors)
        return jsonify({'error': 'invalid_saml'}), 400

def prepare_flask_request(request):
    url_data = {
        'https': 'on' if request.scheme == 'https' else 'off',
        'http_host': request.host,
        'script_name': request.path,
        'get_data': request.args.copy(),
        'post_data': request.form.copy()
    }
    return url_data

if __name__ == '__main__':
    app.run(debug=False)

Key remediation practices:

  • Set debug: False in SAML settings and Flask’s app.run(debug=False) to prevent verbose error output that may leak API keys or configuration.
  • Never log raw SAML requests, responses, or headers that could contain or reflect API keys; use structured logging with redaction.
  • Store API keys in environment variables or a secrets manager, and access them only at the point of use, keeping them out of SAML session objects or assertion attributes.
  • Enforce strict signature and encryption expectations in SAML settings (want_assertions_signed, want_assertions_encrypted) to prevent tampering that could lead to privilege escalation or token substitution.
  • Apply defense in depth: use short-lived SAML assertions, validate audience and issuer, and ensure Flask routes that handle API keys also enforce proper access controls independent of SAML session state.

Frequently Asked Questions

Can SAML metadata endpoints expose API keys if not secured?
SAML metadata itself does not contain API keys, but an unprotected metadata endpoint can reveal SP entity IDs, certificates, and endpoints. If an API key is mistakenly embedded in metadata or related configuration, it may be exposed. Always restrict metadata access and avoid storing secrets in metadata files.
How does middleBrick help detect API key exposure in Flask with SAML?
middleBrick scans the unauthenticated attack surface of your Flask endpoints, including SAML flows, and flags findings such as exposed secrets in logs or error pages. It cross-references OpenAPI specs with runtime behavior and provides prioritized findings with remediation guidance, helping you identify and remediate misconfigurations before they are exploited.