簡體   English   中英

如何從一個域重定向到另一個域並在響應中設置 cookies 或標頭?

[英]How to redirect from one domain to another and set cookies or headers in the response?

我正在使用 FastAPI 的RedirectResponse並嘗試將用戶從一個應用程序(域)重定向到另一個設置了一些 cookie 的應用程序,但 cookie 總是被刪除。 如果我嘗試添加一些標頭,我添加到redirect_response的所有標頭也會被刪除。

@router.post("/callback")
async def sso_callback(request: Request):
   jwt_token = generate_token(request)
   redirect_response = RedirectResponse(url="http://192.168.10.1/app/callback", 
                             status_code=303)
   redirect_response.set_cookie(key="accessToken", value=jwt_token, httponly=True)
   redirect_response.headers["Authorized"] = str(jwt_token)
   return redirect_response

我該如何解決這個問題? 在此先感謝您的幫助。

本答案中所述,無論您使用哪種語言或框架,都無法重定向到設置了自定義標頭的另一個域。 HTTP 協議中的重定向基本上只是與響應關聯的 header(即Location標頭),它不允許添加任何標頭到目標位置。 當您在您提供的代碼片段中使用redirect_response.headers添加Authorized header (通常應稱為'Authorization' )時,您基本上將 header 設置為指示瀏覽器/客戶端重定向的響應,而不是重定向本身。 換句話說,您將 header 發送回客戶端

As for the HTTP cookies , the browser stores the cookies sent by the server with the response (using the Set-Cookie response headers), and later sends the cookies with requests made to the same server inside a Cookie HTTP header. 根據文檔

Set-Cookie HTTP 響應 header 用於將 cookie 從服務器發送到用戶代理,以便用戶代理稍后將其發送回服務器。 要發送多個 cookies,應在同一個響應中發送多個Set-Cookie標頭。

因此,如果這是從一個應用程序(具有子域,例如abc.example.test )重定向到另一個(具有子域,例如xyz.example.com )兩者都具有相同(父)的應用程序域(並且 cookies 已設置為example.test域),cookies 將在兩個應用程序之間成功共享。 無論使用哪種協議(HTTP/HTTPS)或端口,瀏覽器都會為給定域(包括任何子域)提供 cookie; 您可以使用domainpathsecurehttpOnly標志來限制它的可用性(參見此處此處)。

但是,不能為不同的域設置 cookies 如果允許這樣做,它將帶來巨大的安全漏洞。 因此,由於您“試圖通過一些 cookie 集將用戶從一個應用程序(域)重定向到另一個應用程序,......” ,它不會起作用,因為 cookie 只會與對同一域的請求一起發送。

解決方案

如此處所述,解決問題的一種方法是讓域(應用程序)A 將用戶重定向到域(應用程序)B,並在 URL 中傳遞“訪問令牌”作為查詢參數。 然后域 B 將讀取令牌並設置自己的 cookie,以便瀏覽器將該 cookie 與每個后續請求一起發送到域 B。示例如下。

請注意,您應該考慮使用安全 (HTTPS) 通信,以便加密傳輸令牌。 另外,請注意,在查詢字符串中包含令牌會帶來嚴重的安全風險,因為敏感數據永遠不應在查詢字符串中傳遞。 這是因為作為 URL 一部分的查詢字符串出現在瀏覽器的地址欄中; 因此,允許用戶查看帶有令牌的 URL 並為其添加書簽(意味着它保存在磁盤上)。 此外,URL 將進入瀏覽歷史記錄,這意味着它將被寫入磁盤並出現在History選項卡中(您可以按Ctrl+H訪問瀏覽器的歷史記錄)。 上述兩種情況都將允許攻擊者(以及與您共享計算機/移動設備/等的人)竊取此類敏感信息。 此外,許多瀏覽器插件/擴展程序會跟蹤用戶的瀏覽活動——您訪問的每個 URL 都會發送到他們的服務器進行分析,以便檢測惡意網站並在訪問此類網站時向您發出警告。 因此,在使用以下方法時,您應該考慮以上所有因素(有關此主題的相關帖子,請參見此處此處此處)。

為了防止在地址欄中顯示 URL,以下方法也在域 B 上使用重定向。 一旦域 B 在 URL 的查詢參數中收到帶有令牌的/submit路由請求,域 B 就會響應重定向到沒有令牌的裸 URL(即其home )。 由於這種重定向,帶有令牌的 URL 不會出現在瀏覽歷史記錄中。 盡管這提供了針對前面描述的某些攻擊的某種保護,但這並不意味着瀏覽器擴展/工具/等。 仍然無法捕獲帶有令牌的 URL。

如果你在 localhost 上測試這個,你需要給應用 B 一個不同的域名; 否則,如前所述,cookies 將在具有相同域的應用程序之間共享,因此,您最終也會收到為應用程序 A 設置的 cookies,並且無法判斷該方法是否有效。 為此,您必須編輯/etc/hosts文件(在 Windows 上,該文件位於C:\Windows\System32\drivers\etc )並將域名分配給127.0.0.1 例如:

127.0.0.1 example.test

請勿將方案或端口添加到域中,也不應使用常見的擴展名,例如.com.net等,否則可能會與訪問 Internet 上的其他網站發生沖突。

在下面的應用 A 中,您需要在瀏覽器中訪問主路由(即/ ),您可以在其中找到並單擊submit按鈕,對/submit路由執行POST請求以開始重定向。 POST請求的唯一原因是因為您在示例中使用了它,並且我假設您必須發布一些form-data 否則,您也可以使用GET請求。 在應用程序 B 中,當執行從POST路由(即/submit )到GET路由(即/ )的RedirectResponse時,響應狀態代碼必須更改為status.HTTP_303_SEE_OTHER ,如此此處此處所述,以及如下圖所示。 應用程序 A 正在偵聽端口8000 ,而應用程序 B 正在偵聽端口8001 (根據需要調整端口)。

應用程序A.py

from fastapi import FastAPI
from fastapi.responses import RedirectResponse, HTMLResponse
import uvicorn

app = FastAPI()

@app.get('/', response_class=HTMLResponse)
def home():
    html_content = """
    <!DOCTYPE html>
    <html>
       <body>
          <h2>Click the "submit" button to be redirected to domain B</h2>
          <form method="POST" action="/submit">
             <input type="submit" value="Submit">
          </form>
       </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)
        
@app.post("/submit")
def submit():
    token = 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3'
    redirect_url = f'http://example.test:8001/submit?token={token}'  
    return RedirectResponse(redirect_url)
 
if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8000)

應用程序B.py

from fastapi import FastAPI, Request, status
from fastapi.responses import RedirectResponse
import uvicorn

app = FastAPI()

@app.get('/')
def home(request: Request):
    token = request.cookies.get('access-token')
    print(token)
    return 'You have been successfully redirected to domain B!' \
           f' Your access token ends with: {token[-4:]}'
 
@app.post("/submit")
def submit(request: Request, token: str):
    redirect_url = request.url_for('home')
    response = RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
    response.set_cookie(key="access-token", value=token, httponly=True)
    return response
 
if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8001)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM