Execution After Redirect ('EAR')

ID

python.execution_after_redirect

Severity

low

Resource

Other

Language

Python

Tags

CWE:698, NIST.SP.800-53

Description

Execution after redirect ('EAR') occurs when web application logic allows further processing of a request after an HTTP redirect is intended.

In such scenarios, subsequent code execution could inadvertently expose sensitive information or bypass access controls, posing a security risk.

Rationale

Execution After Redirect vulnerabilities typically arise when programmers assume that code execution halts after an HTTP redirect.

In reality, most web frameworks allow further execution after a redirect unless explicitly stopped. This can lead to security weaknesses, such as subsequent actions that should be conditional upon the redirect.

In Python web frameworks like Flask or Django, this can cause sensitive logic to run unintentionally, resulting in issues like information leakage, authorization bypass, or unintended side effects.

Consider the following Flask example:

from flask import Flask, redirect, request

app = Flask(__name__)

@app.route('/logout')
def logout():
    session.pop('user_id', None)
    return redirect('/login')
    do_cleanup()  # This will still execute!

Here, do_cleanup() is executed even after the redirect is issued, which can lead to undefined behavior or security problems.

Another example showing the vulnerability more clearly:

from flask import Flask, redirect

app = Flask(__name__)

@app.route('/transfer')
def transfer():
    if not user_is_authenticated():
        return redirect('/login')
    perform_transfer()  # This still runs even if the user is unauthenticated!

The call to perform_transfer() should not execute after the redirect(), but in this structure, it will unless explicitly returned early.

Remediation

include::../common/execution_after_redirect.adoc[tag=remediation]y.

Correct usage:

from flask import Flask, redirect, session

app = Flask(__name__)

@app.route('/logout')
def logout():
    session.pop('user_id', None)
    return redirect('/login')  # Execution stops here

For more complex logic:

@app.route('/transfer')
def transfer():
    if not user_is_authenticated():
        return redirect('/login')  # Prevents perform_transfer() from running
    return perform_transfer()  # Safe to call now

Best Practices:

  • Always pair redirect() with a return keyword.

  • Avoid side-effect-prone logic after redirection unless absolutely required and well-audited.

  • Document all intentional post-redirect logic to make exceptions explicit and justifiable.

References

  • CWE-698 : Execution After Redirect (EAR).

  • OWASP EAR : Execution After Redirect (EAR).