FilePizza 點對點傳檔實測:檔案不經伺服器,代價在連線層

兩個瀏覽器互傳 4MB 檔案,雜湊逐位元組一致、伺服器側錄零大流量上傳,FilePizza 的點對點宣稱在資料面是真的。但連線配對走第三方信令、官方發布的 TURN 救援位址指向 127.0.0.1、任何拿到連結的人都能讓分享立刻失效,這些控制層的前提才是決定該不該用它的關鍵。

用 AI 摘要這篇文章:

把一份 4,060,715 bytes 的測試圖檔從一個瀏覽器傳到另一個瀏覽器,接收端算出來的 MD5 與原檔逐位元組一致,而整個過程裡,file.pizza 伺服器收到的 HTTP 請求沒有任何一筆超過幾 KB。這是 FilePizza 最核心的那句宣稱,檔案不經過伺服器、兩邊瀏覽器直接互傳,實測的答案是:在資料這一層,它是真的。但同一輪實測也測出了另一面:它的官方站發給瀏覽器的網路救援位址指向 127.0.0.1、配對連線的信令走一家第三方的公共服務、而任何拿到下載連結的人都能讓你的分享瞬間失效。判斷這個工具能不能用,關鍵從來不是檔案有沒有上伺服器,而是你能不能接受這些藏在連線過程裡的前提。以下把整輪實測與原始碼查證攤開來看。

兩個瀏覽器互傳 4MB,先驗掉最核心的宣稱

實測的做法不複雜:用兩個互相獨立的瀏覽器環境,一邊開 file.pizza 選檔上傳,拿到下載連結後,另一邊打開連結按下載。傳的是一份將近 4MB 的 PNG 圖檔,事前事後都算雜湊,兩邊完全一致,連一個位元組都沒有差。同時側錄兩邊的全部 HTTP 請求:上傳端整場共發出 19 個請求、下載端 25 個,其中傳向 file.pizza 的大流量 POST 是零。換句話說,那 4MB 的圖沒有以任何形式離開過這兩台瀏覽器之間的通道,伺服器收到的只有建立頻道、回報進度這類幾 KB 等級的控制訊息。

操作體感跟一般傳檔網站不同的地方有兩個。上傳端選完檔按開始後,畫面上出現的是一組 QR code 加兩條網址,一短一長,短的是 8 個字元的隨機代碼,長的很有味道,用四個披薩配料單字組成,實測這次拿到的是 snowpeas sweetcorn avocado cajunchicken 這樣的組合(QR 分享可以順便參考我們介紹過的 QRSVG 開源 QR 產生器)。另外,傳輸進行中上傳端的頁面不能關,瀏覽器就是發送端,關了頁面等於關了伺服器,這點官方 FAQ 也明講。傳完之後,上傳端畫面會把這條連線標成綠色的完成狀態,下載端就存檔,單檔存原檔,多檔會打包成一個 zip。

FilePizza 上傳端頻道頁:QR code 加長短兩條下載網址Pin
FilePizza 上傳端頻道頁:左邊 QR code、右邊長短兩條下載網址,長網址用四個披薩配料單字組成(實測截圖)

這個資料面表現值得先肯定,因為它不是每家傳檔服務都做得到的。一般「免註冊傳檔」網站的流程是把檔案完整上傳到伺服器再給對方下載,檔案落地、伺服器看得到內容、空間額度成了成本。FilePizza 這類瀏覽器點對點工具把這一步整個拿掉了,這也是它能標榜傳多大都可以的原因,官方 FAQ 的答案是「你的瀏覽器能處理多大就行」,限制在你的記憶體,不在它的硬碟。

最反常的發現:官方發給瀏覽器的救援位址,指向你自己電腦

WebRTC 連線(瀏覽器之間的直接通道)要能建立,雙方得先拿到一份 ICE 清單,裡面列出打洞用的 STUN 伺服器,以及打洞失敗時兜底的 TURN 中繼。FilePizza 的瀏覽器在啟動時會向自家伺服器的 API 要這份清單,於是我呼叫了官方站這個 API,看它實際發給使用者的是什麼。

回應內容藏了一個不太應該出現的東西:TURN 位址是 turn:127.0.0.1:3478。127.0.0.1 是每台電腦自己的本機迴路位址,意思是你收到一份「連你自己電腦上的中繼伺服器」的指示。翻原始碼就知道原因:這個位址來自環境變數 TURN_HOST,程式裡寫死的預設值正是 127.0.0.1,官方站啟用了 TURN 支援卻沒有把這個變數設成真實的伺服器位址,預設值就這樣原封不動發給了每一個使用者。

對一般家庭網路的使用者,這個殘留暫時無感,因為多數連線靠 STUN 打洞就成功了,清單裡的 STUN 是 Google 的公共伺服器,而且瀏覽器對連不上的 TURN 候選會自動略過。真正會踩到的是兩邊都在嚴格 NAT 或企業防火牆後面的場景,這種時候唯一能救場的就是 TURN 中繼,而官方發的那個指向使用者自己的電腦,連上也沒有用。原始碼看得出這是有能力做的:倉庫裡附了完整的 Docker Compose 部署檔,裡面就有 coturn 這套 TURN 服務的容器,自架的人照著跑就能拿到真的中繼。只是官方託管站自己沒把這一格里填上,這中間的落差,官方文件沒有提。

檔案不上伺服器,但連線的生死還握在兩台伺服器手裡

把「免中轉伺服器」這句話拆開驗,會發現它只對了一半,對的是資料這一半,錯的是給人的整體印象。這種點對點連線在資料開始流動之前,有三件事必須先成立,而這三件事都依賴別人的伺服器。

連線的哪一環誰在處理實際依賴
兩邊瀏覽器怎麼找到彼此(信令)第三方公共服務預設走 0.peerjs.com,PeerJS 專案的公共雲
下載連結怎麼對到你的瀏覽器(頻道帳本)file.pizza 自己官方 API 加 Redis,一小時到期,頁面開著每分鐘自動續期
打洞與中繼的位址清單(ICE)file.pizza 自己官方 API 發放,STUN 用 Google 的,TURN 是上文那個 127.0.0.1

信令這一環最值得注意。兩個瀏覽器要建立加密通道,得先互相交換連線資訊,這個交換本身需要一個雙方都能連上的中繼點。FilePizza 用的 PeerJS 函式庫預設連的是 0.peerjs.com,一家第三方維護的公共信令服務,官方 API 的回應裡也是這個位址。也就是說,你的檔案內容沒有經過任何伺服器,但「哪個瀏覽器要跟哪個瀏覽器連線」這件事,預設狀態下是一家第三方服務在撮合。這個服務看不到你的檔案,看得到配對活動本身。重隱私的人可以把整組自架起來,倉庫附了自己跑信令的腳本,用環境變數把位址換掉就行,但用官方站的人就是把自己連線的撮合權交了出去。

頻道帳本這一環則解釋了連結的壽命。短代碼對應你瀏覽器的這筆對照資料存在伺服器的 Redis 裡,有效期一小時,上傳端頁面開著的話每分鐘自動續約,頁面一關,瀏覽器會在離開前發一個銷毀請求,連結跟著失效。所以「連結會活多久」的答案有三層:你的頁面開著就活著;頁面關了立刻死;頁面開著但超過一小時且續約失敗,也會死。這個設計對隱私是加分的,分享的時效完全由發起端控制,缺點是任何中途斷線、休眠、分頁被系統回收,都會讓拿到連結的人撲空。

拿到連結的任何人,都能讓你的分享立刻死掉

測到這裡,最有力的一輪實測登場:FilePizza 有一個銷毀頻道的 API,原始碼裡的註解寫得很明白,知道代碼的任何人都可以呼叫它,這是故意的,目的是讓舉報違規內容的人能順手讓分享失效。我實際驗了這件事:在站內用一般的 fetch 呼叫這個 API,帶上正在分享中的代碼,伺服器回應成功,下載頁再打開就是 404,頁面文案還很俏皮,寫著這片披薩已經被吃掉了。

FilePizza 下載連結被銷毀後的 404 頁面Pin
頻道被任何人以一個請求銷毀後,下載頁變成 404,文案寫著這片披薩已經被吃掉了(實測截圖)

更有意思的是第二輪測試:帶一個亂掰的代碼去呼叫,伺服器照樣回成功。這個 API 不驗證身分、不回報代碼存不存在,一律告訴你成功。從防探測的角度這反而安全,別人沒辦法用亂試的方式確認哪個代碼正在分享;但換個角度看,它也意味著沒有任何門檻擋住惡意銷毀。你在通訊軟體裡貼出的下載連結,任何看到的人、任何轉發到的地方,只要有人不高興,一個請求就能讓所有還沒下載完的人斷糧。

同一組設計裡還有一個配套:下載端有個舉報按鈕,按下去不只通知官方,還會透過連線把上傳端整個導向一個「已被舉報」的頁面,讓對方無法繼續分享。整套機制合起來看,它把內容審查的執行權直接下放給任何一個訪客。站在打擊濫用的角度這是低成本的有效設計,站在正當使用者的角度,這是你按下開始分享時就簽下的但書:這個連結是公共開關,任何人都能關。

下載的人以為匿名,其實指紋就在你畫面上

一般人的直覺是:我打開別人給的下載連結,我只是拿檔案,對方不會知道我是誰。在 FilePizza 上這個直覺一半成立一半不成立。IP 位址層面,兩邊是點對點直連,上傳者的瀏覽器看得到下載端的位址,這是直連的本質;但它的傳檔協議裡還有一條問候訊息,下載端一連上線,瀏覽器會自動送出你用的瀏覽器名稱、版本、作業系統,行動裝置還會送出廠牌與型號,而這些資訊會直接列在上傳者的畫面上。

實測截圖裡這件事一目了然:上傳端的連線清單顯示著「Chrome Headless v148,已完成 1/1 個檔案,狀態綠色完成」,那是我的下載端瀏覽器的身分,連它是由自動化環境驅動的都一清二楚。一般使用者的畫面會顯示對方的瀏覽器與系統版本,例如 Safari 17、Windows 11 這個等級的資訊。這不算嚴重洩漏,多數傳檔場景裡對方本來就知道你是誰,但如果你想在陌生社群裡低調拿檔案,先知道對方畫面上看得到什麼。

FilePizza 上傳端連線清單顯示下載端的瀏覽器與作業系統資訊Pin
上傳端連線清單列出下載端的瀏覽器身分(實測截圖,下載端為自動化環境的 Chrome,傳輸狀態 1/1 完成)

密碼保護也是類似的「方向正確、層次比想像薄」。上傳前可以設一組密碼,下載端要答對才拿得到檔案清單,而驗證就發生在上傳者的瀏覽器裡,密碼不會送到任何伺服器,這部分與宣稱相符。但原始碼顯示,比對方式是把送來的密碼與上傳端持有的密碼直接相等檢查,也就是說,這組密碼會以明文形式通過加密通道抵達對方瀏覽器。它保護的是「隨機路人打不開你的連結」,不是銀行等級的身分驗證,千萬別習慣性把自己在別處用的密碼填進去,那等於把密碼告訴了分享者。通道本身的加密倒是沒有問題,WebRTC 的資料通道強制使用 DTLS,瀏覽器之間的流量是加密的,這一層是協定內建,不用設定也不可關閉。

官方說明文件還活在上一個時代

這個專案值得敬三分的地方是年紀:2015 年由兩個 UC Berkeley 的學生在吃披薩時想出來,倉庫現在有一萬多顆星,2025 年底整個用 TypeScript 重寫成 v2 世代,介面支援深色模式與多檔上傳。但說明文件與現實之間有幾處對不起來,照著文件想像它的人會得到錯誤預期。

最典型的是 FAQ 裡這一句:上傳者關閉瀏覽器後,已經下載完成的人會繼續把檔案分享給還沒下載完的人。這是它上一個世代用 WebTorrent 的遺產,那個架構裡每個下載完的人確實能變成新的種子。但 v2 重寫後整個換掉了底層,改走瀏覽器間的直連通道,我把 v2 的原始碼整個搜過,分享種子的邏輯零命中,下載完成後的動作就是把連線關掉。README 的這段問答是舊時代的殘留說法,照它規劃「先下載的人會接手」的傳法,會在第一個關頁的瞬間全線斷糧。

這個專案的起點其實是個流傳很廣的笑話。README 開頭掛著那張經典的 XKCD 漫畫,講的就是檔案傳輸這件小事多年來始終沒有被好好解決,2015 年兩個 UC Berkeley 的學生決定認真回應這個笑話,十年下來,笑話的註腳變成了一萬多顆星與累積數十萬次的容器拉取。行動裝置的支援官方也標榜到位,稱大部分行動瀏覽器都能用,包含對 WebRTC 最挑剔的 Mobile Safari;這次實測在桌面瀏覽器完成,手機端沒有親測,但原始碼裡為行動裝置寫了對應的偵測與介面調整,判斷上是認真對待而非宣傳話術。

發布管道也有同樣的時代感。它的 npm 套件停在 2019 年的 1.1.0,那是上一個架構的最後版本,之後再也沒發過;v2 的正式部署管道是 Docker,官方映像在 Docker Hub 上累積了約 79 萬次拉取,最後更新停在 2026 年 1 月底,與原始碼主分支的最後一次更新同一時間點。換句話說,這個一萬星的專案目前處於一種穩定凍結的狀態,七個多月沒有新的程式碼進版,但託管站活著、傳輸正常、容器映像完整,它更像一個完成品而非進行中的專案。授權倒是乾淨,BSD 3-Clause,商用修改再散布都可以,只是授權檔尾端附加了字型的另一份授權,導致 GitHub 的授權欄顯示 Other,這是檔案混合造成的偵讀,不是授權有問題。

還有一個小地方先講在前頭:下載過程中的暫停按鈕,在原始碼裡留著開發者自己的待辦註記,實際行為是直接把連線關掉,沒有恢復下載的邏輯。大檔傳到一半斷線,沒有斷點續傳,就是重來。

自架的人拿得到真隱私,一般使用者拿到的是便利

如果讀到這裡的結論是「這工具不行」,那倒是矯枉過正了。把它放回使用場景看,它的位置其實很清楚。

臨時要把一個檔案從筆電送到手機、跟同事收一份大簡報、在兩台不屬於同一個雲端帳號的機器之間搬東西,這類場景裡它是乾淨利落的選擇:不用註冊、檔案不落地、頁面關了連結就死,用完即走。怕檔案內容被傳檔服務的伺服器看見的人,它確實解決了這個問題,實測的雜湊對帳與流量側錄都支持這一點。對照需要落地的傳檔服務,例如之前介紹過的 YDRay 免費檔案分享,兩者的差異是 FilePizza 完全不留存,代價是兩邊都要同時開著瀏覽器。

想要真正完整的掌控,路徑是自架。倉庫附的 Docker Compose 一次起三個容器:本體、Redis、coturn,環境變數把信令位址、TURN 位址都換成自己的,配對與帳本就全在你手裡,對隱私要求高的團隊內部使用是成立的(對 NAS 上跑各種自架服務有興趣的,可以看我們整理過的 NAS Docker Compose 模板合集)。要記得補上 TURN 的真實位址,別重蹈官方站那個 127.0.0.1 的覆轍。

不適合的場景也一樣明確:雙方都在嚴格網路管制後面(此時可能連線都建不起來)、需要長時間多次發送(上傳端要一直開著頁面,連結還可能被任何人銷毀)、需要斷點續傳(沒有這個功能)、以及需要留傳輸記錄的正式流程(它天生不留)。把這些邊界畫出來之後,FilePizza 的價值反而更清楚了:它把「檔案不出瀏覽器」這件最難也最核心的事做到了,其餘的取捨都是這個選擇的自然後果。知道邊界在哪,用它就放心。

Sliven 褚崇名
Sliven 褚崇名

每日分享科技新知、免費資源以及 WordPress、虛擬主機相關主題,任何問題歡迎在科技月球下方留言,或是發送 Email 至 [email protected] 與我聯繫。

文章: 1462

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


Share to...