 Provider 響應(yīng)中的 URL 以防止 SSRF 攻擊)
AI SDK 如何校驗(yàn) Provider 響應(yīng)中的 URL 以防止 SSRF 攻擊【免費(fèi)下載鏈接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ai/ai很多 AI Provider 會(huì)在響應(yīng)體里返回一個(gè) URL——待下載的生成圖片、音頻、視頻或者用于輪詢?nèi)蝿?wù)狀態(tài)的地址。AI SDK 會(huì)在服務(wù)端代你抓取這些 URL 并把結(jié)果交回你的代碼。由于 URL 來自外部服務(wù)一個(gè)被攻破或被篡改的 Provider 可以把它指向云元數(shù)據(jù)端點(diǎn)如http://169.254.169.254/…、內(nèi)網(wǎng)主機(jī)http://10.0.0.5/…或localhost從而讓你的服務(wù)器發(fā)起針對(duì)內(nèi)網(wǎng)的請(qǐng)求即 SSRF 攻擊。AI SDK 的應(yīng)對(duì)方式是在抓取之前校驗(yàn)每一個(gè)來自響應(yīng)的 URL。這件事在 Provider 包內(nèi)部自動(dòng)發(fā)生你不需要配置任何東西。本文說明校驗(yàn)覆蓋了哪些環(huán)節(jié)、被攔截的 URL 在你的代碼里如何表現(xiàn)、非 Node.js 運(yùn)行時(shí)的限制以及如何按官方建議進(jìn)一步加固部署。SDK 自動(dòng)校驗(yàn)的內(nèi)容當(dāng) SDK 抓取一個(gè)取自 Provider 響應(yīng)的 URL 時(shí)會(huì)執(zhí)行以下檢查完整說明見 Secure URL Fetching 文檔拒絕私有、環(huán)回和鏈路本地目標(biāo)——IPv4 的10/8、172.16/12、192.168/16、127/8、169.254/16、CGNAT、多播等區(qū)間及對(duì)應(yīng)的 IPv6 區(qū)間外加localhost和.local域名非http(s)協(xié)議同樣被拒絕。對(duì)每一個(gè)重定向跳重新校驗(yàn)——一個(gè) URL 本身合法、隨后重定向到內(nèi)網(wǎng)地址的情況會(huì)被攔截SDK 不會(huì)盲目跟隨重定向。在 Node.js 上于連接時(shí)校驗(yàn) DNS——每一個(gè)解析出的地址都會(huì)被檢查并且 socket 會(huì)被固定到已校驗(yàn)的 DNS 結(jié)果上因此 DNS rebinding 無法在校驗(yàn)與連接之間引入另一個(gè)地址。剝離高危請(qǐng)求頭——代理轉(zhuǎn)發(fā)、云元數(shù)據(jù)和 Cookie 類請(qǐng)求頭在發(fā)出請(qǐng)求前被移除。跨源丟棄憑據(jù)——調(diào)用方請(qǐng)求頭Authorization、Cookie以及各 Provider 的 API-key 頭不會(huì)發(fā)往與 Provider 不同源的宿主跨源重定向會(huì)丟棄除 user-agent 以外的全部這些頭。被攔截的 URL 會(huì)以DownloadError的形式拋到你的代碼中。兩個(gè)需要注意的例外路徑自托管與本地端點(diǎn)。與你配置的 Provider 端點(diǎn)同源的 URL例如自定義baseURL指向自托管或localhost部署會(huì)豁免上述檢查——它指向的正是你明確要求 SDK 訪問的主機(jī)任務(wù)狀態(tài)輪詢同樣適用。任何離開該源的重定向仍會(huì)在發(fā)出前被校驗(yàn)。帶認(rèn)證的任務(wù)狀態(tài)輪詢。Provider 可以從你配置的 API 端點(diǎn)構(gòu)造第一個(gè) URL。SDK 對(duì)初始請(qǐng)求信任這個(gè)已配置的源但會(huì)手動(dòng)跟隨并校驗(yàn)之后離開該源的每一個(gè)重定向。文檔指出 MiniMax、Kling AI 和 ByteDance 的視頻輪詢走的就是這條受保護(hù)路徑。在代碼中識(shí)別被攔截的下載校驗(yàn)攔截、下載失敗、超出大小限制等情況統(tǒng)一表現(xiàn)為DownloadError。你可以用靜態(tài)方法isInstance判斷并單獨(dú)處理完整屬性說明見 AI_DownloadError 參考import { DownloadError } from ai; if (DownloadError.isInstance(error)) { // error.url —— 下載失敗的 URL // error.statusCode / error.statusText —— 服務(wù)端返回的狀態(tài)碼與狀態(tài)文本可選 // error.cause —— 導(dǎo)致下載失敗的底層錯(cuò)誤可選 // error.message —— 下載失敗詳情自動(dòng)生成可選 }例如語音轉(zhuǎn)寫場(chǎng)景傳入 URL 時(shí)audio: new URL(https://example.com/audio.mp3)SDK 默認(rèn)以 2 GiB 大小限制下載超限時(shí)拋出的就是DownloadError可以按上面的方式捕獲并記錄error.message。這也是判斷URL 被安全校驗(yàn)攔截與普通網(wǎng)絡(luò)錯(cuò)誤的實(shí)際落點(diǎn)。非 Node.js 運(yùn)行時(shí)的差異默認(rèn)行為針對(duì) Node.js校驗(yàn)用的下載 fetch 通過node:dns與undici連接器鉤子在連接時(shí)校驗(yàn)每一個(gè)解析出的地址連接器直接使用這些結(jié)果建立連接同時(shí)堵死 hostname-to-private-IP 和 DNS rebinding 兩類繞過。由此推出兩條部署約束如果你注入或全局替換了fetch等價(jià)的 DNS 校驗(yàn)與連接固定由你的實(shí)現(xiàn)負(fù)責(zé)SDK 不再替你兜底。其他運(yùn)行時(shí)不暴露 Node 的 DNS/socket 鉤子在這些運(yùn)行時(shí)部署服務(wù)端時(shí)應(yīng)當(dāng)用網(wǎng)絡(luò)層手段限制到私有、環(huán)回、鏈路本地和云元數(shù)據(jù)區(qū)間的出站流量。進(jìn)一步加固部署官方建議對(duì)服務(wù)端會(huì)抓取 Provider 返回的 URL的部署盡量同時(shí)采用以下兩種方式完整命令與代碼見 Secure URL Fetching 文檔方式一在網(wǎng)絡(luò)層限制出站流量在服務(wù)器的網(wǎng)絡(luò)層拒絕發(fā)往169.254.0.0/16、RFC-1918 區(qū)間和環(huán)回地址的出站流量。文檔明確指出這是最穩(wěn)健的控制手段因?yàn)樗?dú)立于應(yīng)用代碼——即使應(yīng)用層校驗(yàn)出現(xiàn)缺口流量也出不去。方式二加固注入的fetchNode.js 默認(rèn)路徑已經(jīng)做了連接期固定如果你注入或全局替換了fetch官方文檔給出的做法是用一個(gè)undiciAgent承載它在connect.lookup中校驗(yàn)解析出的 IP只允許連接安全地址。文檔示例代碼如下import { Agent, fetch as undiciFetch } from undici; import { lookup } from node:dns; // Your own check that returns true for private/loopback/link-local addresses. declare function isUnsafeAddress(ip: string): boolean; const safeLookup: typeof lookup (hostname, options, callback) { lookup(hostname, options as any, (err, address, family) { if (!err typeof address string isUnsafeAddress(address)) { callback(new Error(Refusing to connect to ${address}), , 0); return; } (callback as any)(err, address, family); }); }; const safeDispatcher new Agent({ connect: { lookup: safeLookup } }); const safeFetch: typeof fetch (input, init) undiciFetch(input, { ...init, dispatcher: safeDispatcher }) as any;注意示例中的isUnsafeAddress是一個(gè)需要你自己實(shí)現(xiàn)的判斷函數(shù)契約如文檔注釋所述對(duì)私有/環(huán)回/鏈路本地地址返回true。其余代碼可直接使用。然后把safeFetch傳給 Provider 構(gòu)造函數(shù)例如import { createFal } from ai-sdk/fal; const fal createFal({ fetch: safeFetch });最后一條原則SDK 的 URL 校驗(yàn)與你自定義 fetch 的連接期固定是互補(bǔ)關(guān)系文檔明確要求兩者都保留。邊界與延伸閱讀校驗(yàn)只針對(duì)URL 來自響應(yīng)體的場(chǎng)景主機(jī)固定由你的配置決定的請(qǐng)求validateUrl: false路徑不會(huì)觸發(fā)這條校驗(yàn)鏈。如果你在開發(fā) Provider 包validateUrl、trustedOrigin、credentialedOrigin的取舍規(guī)則見倉庫內(nèi)的 Secure URL handling 貢獻(xiàn)指南。實(shí)現(xiàn)入口在核心包的下載工具 download.ts它調(diào)用ai-sdk/provider-utils的fetchWithValidatedRedirects完成帶重定向校驗(yàn)的抓取相關(guān)實(shí)現(xiàn)位于 packages/provider-utils。對(duì)自托管含localhost的 Replicate 兼容服務(wù)端、內(nèi)部 fal 部署用戶你的端點(diǎn)天然與配置的baseURL同源不會(huì)被誤攔但離開該源的任何重定向仍會(huì)被校驗(yàn)遇到DownloadError時(shí)應(yīng)先確認(rèn) Provider 是否返回了異常的重定向目標(biāo)。【免費(fèi)下載鏈接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ai/ai創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考