codeql icon indicating copy to clipboard operation
codeql copied to clipboard

Python: Add Support for CORS Middlewares

Open Kwstubbs opened this issue 1 year ago • 1 comments

Model FastAPI & Starlette CORS Middlewares for misconfiguration

Kwstubbs avatar Aug 27 '24 04:08 Kwstubbs

QHelp previews:

python/ql/src/experimental/Security/CWE-942/CorsMisconfigurationMiddleware.qhelp

Cors misconfiguration with credentials

Web browsers, by default, disallow cross-origin resource sharing via direct HTTP requests. Still, to satisfy some needs that arose with the growth of the web, an expedient was created to make exceptions possible. CORS (Cross-origin resource sharing) is a mechanism that allows resources of a web endpoint (let's call it "Peer A") to be accessed from another web page belonging to a different domain ("Peer B").

For that to happen, Peer A needs to make available its CORS configuration via special headers on the desired endpoint via the OPTIONS method.

This configuration can also allow the inclusion of cookies on the cross-origin request, (i.e. when the Access-Control-Allow-Credentials header is set to true) meaning that Peer B can send a request to Peer A that will include the cookies as if the request was executed by the user.

That can have dangerous effects if the origin of Peer B is not restricted correctly. An example of a dangerous scenario is when Access-Control-Allow-Origin header is set to a value obtained from the request made by Peer B (and not correctly validated), or is set to special values such as * or null. The above values can allow any Peer B to send requests to the misconfigured Peer A on behalf of the user.

Example scenario: User is client of a bank that has its API misconfigured to accept CORS requests from any domain. When the user loads an evil page, the evil page sends a request to the bank's API to transfer all funds to evil party's account. Given that the user was already logged in to the bank website, and had its session cookies set, the evil party's request succeeds.

Recommendation

When configuring CORS that allow credentials passing, it's best not to use user-provided values for the allowed origins response header, especially if the cookies grant session permissions on the user's account.

It also can be very dangerous to set the allowed origins to null (which can be bypassed).

Example

The first example shows a possible CORS misconfiguration case:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "*"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/")
async def main():
    return {"message": "Hello World"}

The second example shows a better configuration:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "http://localhost.tiangolo.com",
    "https://localhost.tiangolo.com",
    "http://localhost",
    "http://localhost:8080",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/")
async def main():
    return {"message": "Hello World"}

References

github-actions[bot] avatar Aug 27 '24 04:08 github-actions[bot]

@tausbn let me know if theres any other tests needed 🎈

Kwstubbs avatar Sep 23 '24 23:09 Kwstubbs

Oh, we should run a performance test before merging. I'll kick that off.

tausbn avatar Sep 24 '24 12:09 tausbn

Performance looks good. In it goes. 👍

tausbn avatar Sep 24 '24 13:09 tausbn