Web 是否應該公開硬件功能?
已發表: 2022-03-10最近,我對不同瀏覽器供應商對 Web 未來的不同意見很感興趣——特別是在推動 Web 平台功能更接近原生平台的各種努力中,例如 Chromium 的 Project Fugu。
主要立場可以概括為:
- 谷歌(與英特爾、微軟和三星等合作夥伴一起)正在積極推進和創新,使用大量新的 API,如 Fugu 中的 API,並在 Chromium 中發布它們;
- 蘋果正在以更保守的方式進行反擊,將許多新的 API 標記為引發安全和隱私問題;
- 這(連同 Apple 在 iOS 中對瀏覽器選擇的限制)創造了一種立場,將 Safari 標記為新的 IE,同時聲稱 Apple 正在減慢 Web 的進展;
- 在這點上,Mozilla 似乎更接近於蘋果而不是谷歌。
我在本文中的目的是查看 Google 確定的聲明,特別是 Fugu 項目負責人 Alex Russell 在平台鄰接理論中的聲明,查看這些聲明中提供的證據,也許會得出我自己的結論。
具體來說,我打算深入研究 WebUSB(Fugu 項目中一個特別有爭議的 API),檢查針對它的安全聲明是否有價值,並嘗試看看是否會出現替代方案。
平台鄰接理論
上述理論提出以下主張:
- 軟件正在轉向網絡,因為它是更好的計算版本;
- 網絡是一個元平台——一個從其操作系統中抽像出來的平台;
- 元平台的成功是基於它完成了我們期望大多數計算機做的事情;
- 以安全為由拒絕向 Web 元平台添加相鄰功能,同時忽略原生平台中的相同安全問題,最終將使 Web 變得越來越不相關;
- Apple 和 Mozilla 正是這樣做的——拒絕向網絡添加相鄰的計算能力,從而“將網絡投射成琥珀”。
我與作者保持開放網絡相關性的熱情有關,並且擔心使用新功能增強網絡速度太慢會使其無關緊要。 我對應用商店和其他圍牆花園的厭惡加劇了這一點。 但作為一個用戶,我可以理解相反的觀點——當我不知道我正在瀏覽的網站能夠做什麼或不能做什麼時,我有時會頭暈目眩,我發現平台限制和審核令人欣慰。
元平台
為了理解“元平台”這個術語,我研究了這個理論使用這個名稱的含義——Java 和 Flash,它們都是世紀之交的產品。
我發現將 Java 或 Flash 與 Web 進行比較是令人困惑的。 正如理論中提到的,Java 和 Flash 在當時都通過瀏覽器插件廣泛分佈,使它們更像是瀏覽器平台之上的替代運行時。 今天,Java 主要用於服務器和作為 Android 平台的一部分,除了語言之外,兩者沒有太多共同點。
今天,服務器端 Java 可能是一個元平台,node.js 也是服務器端元平台的一個很好的例子。 它是一組 API、一個跨平台運行時和一個包生態系統。 事實上,node.js 一直在添加更多功能,以前只能作為平台的一部分。
在客戶端,基於 C++ 的跨平台框架 Qt 沒有單獨的運行時,它只是一個(好!)用於 UI 開發的跨平台庫。
Rust 也是如此——它是一種語言和包管理器,但不依賴於預安裝的運行時。
開發客戶端應用程序的其他方法主要是特定於平台的,但也包括一些跨平台的移動解決方案,如 Flutter 和 Xamarin。
能力與時間
理論中的主圖顯示了元平台在能力與時間的二維軸上的相關性:
當談到上面提到的跨平台開發框架(如 Qt、Xamarin、Flutter 和 Rust)以及服務器平台(如 node.js 和 Java/Scala)時,我可以看到上圖的意義。
但是以上所有內容都與網絡有一個關鍵的區別。
第三維度
前面提到的元平台確實在與它們的主機操作系統競爭能力,但與網絡不同的是,它們對信任和分佈沒有固執己見——在我看來,上圖中缺少的第三個維度。
Qt 和 Rust 是創建應用程序的好方法,這些應用程序通過 WebAssembly 分發,直接下載並安裝在主機操作系統上,或者通過 Cargo 等包管理器或 Ubuntu 等 Linux 發行版進行管理。 React Native、Flutter 和 Xamarin 都是創建通過應用商店分發的應用的不錯方式。 node.js 和 Java 服務通常通過 docker 容器、虛擬機或其他一些服務器機制分發。
用戶大多不知道用於開發他們的內容的內容,但在一定程度上知道它是如何分發的。 用戶不知道 Xamarin 和 node.js 是什麼,如果有一天他們的 Swift 應用程序被 Flutter 應用程序取代,大多數用戶不會而且理想情況下不應該關心它。
但用戶確實了解網絡——他們知道當他們在 Chrome 或 Firefox 中“瀏覽”時,他們是“在線”的,可以訪問他們不一定信任的內容。 他們知道下載和安裝軟件可能存在危險,並且可能會被 IT 管理員阻止。 事實上,對於網絡平台來說,用戶知道他們當前正在“瀏覽網絡”是很重要的。 這就是為什麼,例如,切換到全屏模式會向用戶顯示一個清晰的提示,並說明如何從中恢復。
網絡之所以成功,是因為它不透明——但顯然與其主機操作系統分離。 如果我不能相信我的瀏覽器會阻止隨機網站讀取我硬盤上的文件,我可能不會去任何網站。
用戶還知道他們的計算機軟件是“Windows”還是“Mac”,他們的手機是基於 Android 還是基於 iOS,以及他們當前是否正在使用應用程序(在 iOS 或 Android 上,以及在某種程度上在 Mac OS 上) . 操作系統和分發模型通常為用戶所知——用戶信任他們的操作系統和網絡來做不同的事情,並且信任程度不同。
因此,如果不考慮其獨特的分發模型,則無法將 Web 與跨平台開發框架進行比較。
另一方面,Web 技術也用於跨平台開發,例如 Electron 和 Cordova 等框架。 但這些並不完全是“網絡”。 與 Java 或 node.js 相比,“Web”一詞需要替換為“Web Technologies”。 並且以這種方式使用的“網絡技術”不一定需要基於標准或在多個瀏覽器上工作。 關於 Fugu API 的討論與 Electron 和 Cordova 有點無關。
原生應用
在為 Web 平台添加功能時,不能忽視或輕視第三個維度——信任和分發模型。 當作者聲稱“Apple 和 Mozilla 關於新功能風險的姿態被公認的現有原生平颱風險所掩蓋”時,他將 Web 和原生平台在信任方面置於同一維度。
誠然,原生應用程序有其自身的安全問題和挑戰。 但我不明白這是支持更多網絡功能的論據,就像這裡一樣。 這是一個謬論——結論應該是解決本機應用程序的安全問題,而不是放鬆網絡應用程序的安全性,因為它們處於與操作系統功能相關的追趕遊戲中。
如果不考慮信任和分發模型的第三維,Native 和 Web 無法在能力上進行比較。
應用商店限制
理論中對原生應用程序的批評之一是 iOS 上缺乏瀏覽器引擎選擇。 這是對蘋果的普遍批評,但對此有不止一個觀點。
批評專門針對 Apple 應用商店審查指南的第 2.5.6 條:
“瀏覽網頁的應用程序必須使用適當的 WebKit 框架和 WebKit JavaScript。”
這似乎是反競爭的,而且我對 iOS 的限製程度有自己的保留。 但是如果沒有應用商店審查指南的其餘部分的上下文,則無法閱讀第 2.5.6 項,例如第 2.3.12 項:
“應用程序必須在其‘新增功能’文本中清楚地描述新功能和產品變化。”
如果一個應用程序可以接收設備訪問權限,然後包含可以執行來自任何網站的代碼的自己的框架,那麼應用程序商店審查指南中的那些項目將變得毫無意義。 與應用程序不同,網站不必在每次修訂時描述其功能和產品更改。
當瀏覽器提供實驗性功能時,這將成為一個更大的問題,例如 Fugu 項目中的功能,這些功能尚未被視為標準。 誰定義了瀏覽器是什麼? 通過允許應用程序發布任何 Web 框架,應用程序商店實質上將允許“應用程序”運行任何未經審計的代碼,或完全更改產品,從而繞過商店的審查流程。
作為網站和應用程序的用戶,我認為他們都在計算世界中佔有一席之地,儘管我希望盡可能多地轉移到網絡上。 但考慮到網絡標準的當前狀態,以及圍繞藍牙和 USB 等事物的信任和沙盒維度如何遠未得到解決,我不認為允許應用程序自由執行來自網絡的內容對用戶有什麼好處.
Appiness的追求
在另一篇相關的博客文章中,同一位作者在談到原生應用程序時談到了其中的一些問題:
“成為‘一個應用程序’只是滿足一組任意且可變的操作系統約定。”
我同意“應用程序”的定義是任意的,它的定義依賴於定義應用程序商店政策的人的觀點。 但是今天,瀏覽器也是如此。 帖子中聲稱Web 應用程序默認安全的說法也有些武斷。 誰在“什麼是瀏覽器”這個問題上劃清界限? 帶有內置瀏覽器的 Facebook 應用程序是“瀏覽器”嗎?
應用程序的定義是任意的,但也很重要。 事實上,使用低級功能的應用程序的每個修訂都由我可能信任的人審核,即使這個人是任意的,這使得應用程序成為它們的樣子。 如果那個人是我購買的硬件的製造商,那就更不隨意了——我購買計算機的公司是對該計算機功能較低的審計軟件。
一切都可以是瀏覽器
無需像 Apple 應用商店本質上那樣畫一條“什麼是瀏覽器”,每個應用都可以發布自己的網絡引擎,誘使用戶使用其應用內瀏覽器瀏覽任何網站,並添加任何跟踪代碼它想要,折疊應用程序和網站之間的第三維差異。
當我在 iOS 上使用應用程序時,我知道我的行為目前會暴露給兩個玩家:Apple 和指定的應用程序製造商。 當我在 Safari 或 Safari WebView 中使用網站時,我的操作會暴露給 Apple 和我當前正在查看的網站的頂級域的所有者。 當我使用帶有未識別引擎的應用內瀏覽器時,我會接觸到應用程序製造商 Apple 和頂級域的所有者。 這可以創建可避免的同源違規,例如應用程序的所有者跟踪我在外國網站上的所有點擊。
我同意也許“Only WebKit”的界限太苛刻了。 對於不會創建用於跟踪用戶瀏覽的後門的瀏覽器的另一種定義是什麼?
關於蘋果的其他批評
該理論聲稱,Apple 拒絕實施功能不僅限於隱私/安全問題。 它包含一個鏈接,該鏈接確實顯示了許多在 Chrome 中而不是在 Safari 中實現的功能。 但是,當向下滾動時,它還列出了在 Safari 中實現而不是在 Chrome 中實現的大量其他功能。
這兩個瀏覽器項目有不同的優先級,但這與“遊戲在縮小時變得清晰”的明確聲明以及對蘋果試圖將網絡投射為琥珀色的嚴厲批評相去甚遠。
此外,標題為it's hard 的鏈接,我們不想嘗試導致 Apple 聲明,如果滿足安全/隱私問題,他們將實施功能。 我覺得將這些鏈接與這些標題放在一起是一種誤導。
我同意一個更平衡的說法,即在實現功能和推進網絡方面,谷歌比蘋果更樂觀。
權限提示
谷歌在 3rd 維度上進行了長期的創新,開發了新的方法來在用戶、開發人員和平台之間建立信任,有時取得了巨大的成功,例如可信網絡活動。
但是,與設備 API 相關的第 3 維中的大部分工作都集中在權限提示上,使它們更加可怕,或者諸如時間框權限授予和黑名單域之類的東西。
“可怕的”提示,就像我們不時看到的這個例子中的提示一樣,看起來它們是為了阻止人們去那些看起來可能是惡意的頁面。 因為它們如此明目張膽,這些警告鼓勵開發人員轉向更安全的 API 並更新他們的證書。
我希望對於設備訪問功能,我們可以提出鼓勵參與並確保參與安全的提示,而不是阻止它並將責任轉移給用戶,而 Web 開發人員沒有可用的補救措施。 稍後再談。
我確實同意 Mozilla 和 Apple 至少應該嘗試在該領域進行創新而不是“拒絕實施”的論點。 但也許他們是? 例如,我認為來自 Apple 的 isLoggedIn 是一個有趣且相關的提議,它是未來設備 API 可以構建的第 3 維度——例如,噹噹前網站已經知道用戶身份時,可以提供易於指紋識別的設備 API用戶。
網絡USB
在下一節中,我將深入研究 WebUSB,檢查它允許什麼,以及它是如何在第三維處理的——什麼是信任和分發模型? 夠了嗎? 有哪些替代方案?
前提
WebUSB API 允許對未列入黑名單的設備類的 USB 協議進行完全訪問。
它可以實現強大的功能,例如連接到 Arduino 板或調試 Android 手機。
看到 Suz Hinton 關於這個 API 如何幫助實現以前非常昂貴的事情的視頻令人興奮。
例如,我真的希望平台能夠找到更開放的方法,並允許對教育硬件/軟件項目進行快速迭代。
有趣的感覺
但是,當我看到 WebUSB 支持什麼以及 USB 存在的一般安全問題時,我仍然有一種有趣的感覺。
USB 感覺太強大了,因為它是一種暴露在網絡上的協議,即使有權限提示也是如此。
所以我進一步研究了。
Mozilla 的官方觀點
我首先閱讀了 David Baron 在 Mozilla 的官方標準立場中談到了 Mozilla 最終拒絕 WebUSB 的原因:
“由於許多 USB 設備並非旨在處理通過 USB 協議進行的潛在惡意交互,而且這些設備可能會對它們所連接的計算機產生重大影響,因此我們認為將 USB 設備暴露於 Web 的安全風險也很大。寬泛的風險讓用戶接觸他們或向最終用戶正確解釋以獲得有意義的知情同意。”
當前的許可提示
這是發布這篇文章時 Chrome 的 WebUSB 權限提示的樣子:
特定域 Foo 想要連接到特定設備 Bar。 做什麼? 我怎麼能確定呢?
在授予對打印機、攝像頭、麥克風、GPS 的訪問權限,甚至是一些包含更多 WebBluetooth GATT 配置文件(如心率監測)的權限時,這個問題相對清晰,並且側重於內容或操作而不是設備。 清楚地了解我想從外圍設備獲得什麼信息或我想用它執行什麼操作,並且用戶代理進行調解並確保處理此特定操作。
USB 是通用的
與上面提到的通過特殊 API 公開的設備不同,USB 不是特定於內容的。 如規範介紹中所述,WebUSB 走得更遠,並且是專門為未知或尚未發明的設備類型設計的,而不是為鍵盤或外部驅動器等知名設備類別設計的。
因此,與打印機、GPS 和相機的情況不同,如果沒有深入了解特定設備並審核訪問它的代碼。
Yubikey 事件和緩解措施
不久前的一個很好的例子是 Yubikey 事件,其中 Chrome 的 WebUSB 被用來從 USB 供電的身份驗證設備中釣魚數據。
由於這是一個據說已經解決的安全問題,我很想深入研究 Chrome 在 Chrome 67 中的緩解工作,其中包括阻止一組特定的設備和一組特定的類。
類/設備塊列表
因此,除了當前非常通用的權限提示之外,Chrome 對野外發生的 WebUSB 漏洞利用的實際防禦是阻止特定設備和設備類。
對於新技術或實驗來說,這可能是一個簡單的解決方案,但是當(以及如果)WebUSB 變得更流行時,它會變得越來越難實現。
恐怕通過 WebUSB 在教育設備上進行創新的人們可能會遇到困難。 當他們完成原型設計時,他們可能會面臨一組不斷變化的非標準阻止列表,這些列表僅與瀏覽器版本一起更新,基於與他們無關的安全問題。
我認為在不解決這個問題的情況下標準化這個 API 最終會對依賴它的開發人員產生反作用。 例如,有人可能會花費數週時間為運動檢測器開發 WebUSB 應用程序,但後來發現運動檢測器成為一個被阻止的類,要么是出於安全原因,要么是因為操作系統決定處理它們,導致他們的整個 WebUSB 工作都轉移到了浪費。
安全與功能
平台鄰接理論在某些方面認為能力和安全是一場零和遊戲,在安全和隱私問題上過於保守會導致平台失去相關性。
讓我們以Arduino為例。 使用 WebUSB 可以進行 Arduino 通信,這是一個主要用例。 開發 Arduino 設備的人現在必須考慮一個新的威脅場景,其中一個站點嘗試使用 WebUSB(在某些用戶許可下)訪問他們的設備。 根據規範,這家設備製造商現在必須“將他們的設備設計為只接受簽名固件”。 這會增加固件開發人員的負擔,並增加開發成本,而規範的整個目的是做相反的事情。
是什麼讓 WebUSB 與其他外設不同
在瀏覽器中,用戶交互和合成交互(由網頁實例化的交互)之間有明顯的區別。
例如,網頁不能自行決定點擊鏈接或喚醒 CPU/顯示器。 但是外部設備可以——例如,鼠標設備可以代表用戶單擊鏈接,幾乎任何 USB 設備都可以喚醒 CPU,具體取決於操作系統。
因此,即使使用當前的 WebUSB 規範,設備也可以選擇實現多個接口,例如調試 adb 和用於指針輸入的 HID,以及使用利用 ADB 的惡意代碼,成為鍵盤記錄器並代表用戶瀏覽網站,假設正確的可利用固件刷新機制。
對於使用 ADB 或其他允許的閃存形式破壞固件的設備而言,將該設備添加到阻止列表為時已晚,並且將使設備製造商比以前更加依賴瀏覽器版本來獲取與其設備相關的安全修復程序。
知情同意和內容
如前所述,知情同意和 USB 的問題在於 USB(特別是在非通用 WebUSB 用例中)不是特定於內容的。 用戶知道什麼是打印機,什麼是相機,但對於大多數用戶來說,“USB”只是一根電纜(或一個插座)——一種達到目的的手段——很少有用戶知道 USB 是一種協議以及在網站之間啟用它的原因和設備手段。
一個建議是有一個“可怕的”提示,類似於“允許此網頁接管設備”(這是對看似無害的“想要連接”的改進)。
但就像提示一樣可怕,他們無法解釋通過原始訪問瀏覽器不知道的 USB 外圍設備可以完成的可能事情的廣度,如果他們這樣做了,他們頭腦正常的用戶不會點擊“是” ”,除非它是他們完全相信沒有錯誤的設備,並且是他們真正相信是最新且無惡意的網站。
像這樣的可能提示將顯示“允許此網頁可能接管您的計算機”。 我不認為像這樣的可怕提示對 WebUSB 社區有益,對這些對話框的不斷更改會使社區感到困惑。
原型與產品
我可以看到一個可能的例外。 如果 WebUSB 和 Fugu API 的其他項目的前提是支持原型設計而不是產品級設備,那麼包羅萬象的通用提示可能是有意義的。
不過,為了實現這一目標,我認為必須做到以下幾點:
- 在規範中使用對原型設計設定期望的語言;
- 只有在某些選擇加入手勢後才能使用這些 API,例如讓用戶在瀏覽器設置中手動啟用它們;
- 有“可怕的”權限提示,例如無效 SSL 證書的提示。
沒有上述內容讓我認為這些 API 是針對真實產品而不是原型的,因此,反饋是成立的。
另一種建議
我同意原始博客文章中的一個部分是說“不”是不夠的——網絡世界中拒絕某些 API 有害的主要參與者也應該採取進攻並提出這些功能重要的方式可以安全地暴露給用戶和開發人員。 我不代表任何主要玩家,但我會謙虛地嘗試一下。
我相信這個問題的答案在於信任和關係的第三個維度,並且它超出了許可提示和阻止列表的範圍。
直截了當且經過驗證的提示
我要提出的主要情況是提示應該是關於內容或操作,而不是關於外圍設備,並且可以為具有一組特定驗證參數的特定直接操作授予知情同意,而不是針對一般操作,例如“接管”或“連接”設備。
3D 打印機示例
在 WebUSB 規範中,以 3D 打印機為例,所以我將在這裡使用它。
在為 3D 打印機開發 WebUSB 應用程序時,我希望瀏覽器/操作系統提示詢問我是否允許 AutoDesk 3ds-mask 將模型打印到您的 CreatBot 3D 打印機? ,顯示一個瀏覽器/操作系統對話框,其中包含一些打印參數,如細化、厚度和輸出尺寸,以及將要打印的內容的預覽。 所有這些參數都應該由受信任的用戶代理驗證,而不是通過路過的網頁。
目前,瀏覽器不知道打印機,它只能驗證提示中的一些聲明:
- 請求域具有註冊到 AutoDesk 的證書,因此可以確定這是 AutoDesk Inc;
- 請求的外圍設備稱自己為“CreatBot 3d 打印機”;
- 在瀏覽器的阻止列表中找不到該設備、設備類和域;
- 用戶對他們被問到的一般問題回答“是”或“否”。
但為了顯示真實的提示並與上述詳細信息對話,瀏覽器還必須驗證以下內容:
- 獲得許可後,執行的操作將是打印 3D 模型,僅此而已;
- 將尊重所選參數(細化/厚度/尺寸等);
- 向用戶顯示了將要打印的內容的經過驗證的預覽;
- 在某些敏感情況下,需要額外驗證這實際上是 AutoDesk,可能使用可撤銷的短期令牌之類的東西。
在不驗證上述情況的情況下,被授予“連接”或“接管”3D 打印機權限的網站可以由於錯誤(或其依賴項之一中的惡意代碼)開始打印巨大的 3D 模型。
此外,想像中的成熟網絡 3D 打印功能將比 WebUSB 提供的功能多得多——例如,假脫機和排隊不同的打印請求。 如果瀏覽器窗口關閉,將如何處理? 我還沒有研究所有可能的 WebUSB 外圍用例,但我猜測從內容/操作的角度來看它們時,大多數需要的不僅僅是 USB 訪問。
由於上述原因,使用 WebUSB 進行 3D 打印可能會很笨拙且壽命短,依賴它的開發人員必須在某個時候為他們的打印機提供“真正的”驅動程序。 例如,如果操作系統供應商決定添加對 3D 打印機的內置支持,則所有使用該打印機和 WebUSB 的站點都將停止工作。
提案:司機審計機關
所以,像“接管外圍設備”這樣的總體權限是有問題的,我們沒有足夠的信息來顯示一個完整的參數對話框並驗證它的結果是否會得到尊重,我們不想發送用戶在不安全的旅程中從網絡下載隨機可執行文件。
但是如果有一段經過審計的代碼,一個驅動程序,在內部使用 WebUSB API 並執行以下操作:
- 實現了“打印”命令;
- 顯示頁外打印對話框;
- 連接到一組特定的 USB 設備;
- 當頁面在後台時(例如在服務工作者中),甚至在瀏覽器關閉時執行它的一些操作。
像這樣對驅動程序進行審計可以確保它所做的相當於“打印”,它尊重參數,並顯示打印預覽。
我認為這類似於證書頒發機構,這是 Web 生態系統中與瀏覽器供應商有些脫節的重要部分。
司機聯合
驅動程序不必由 Google/Apple 審核,但瀏覽器/操作系統供應商可以選擇自行審核驅動程序。 它可以像 SSL 證書頒發機構一樣工作——頒發者是一個高度信任的組織; 例如,特定外圍設備的製造商或認證許多驅動程序的組織,或像 Arduino 這樣的平台。 (我想像會出現類似於 Let's Encrypt 的組織。)
對用戶說:“Arduino 相信這段代碼會用這個固件刷新你的 Uno”(固件預覽)可能就足夠了。
注意事項
這當然不是沒有潛在的問題:
- 驅動程序本身可能是錯誤的或惡意的。 但至少它是經過審計的;
- 它不那麼“webby”,並產生了額外的開發負擔;
- 它今天不存在,也無法通過瀏覽器引擎的內部創新來解決。
其他選擇
其他替代方案可能是以某種方式標準化和改進跨瀏覽器 Web 擴展 API,並使現有的瀏覽器插件商店(如 Chrome Web Store)成為某種驅動審計機構,在用戶請求和外圍設備訪問之間進行調解。
意見摘要
作者、谷歌和合作夥伴通過增強其功能來保持開放網絡相關性的大膽努力令人鼓舞。
當我深入了解細節時,我認為 Apple 和 Mozilla 對網絡的更為保守的看法,以及他們對新設備功能的防禦方法,具有技術優勢。 圍繞開放式硬件功能的知情同意的核心問題遠未得到解決。
在討論中,蘋果可能會更加積極地尋找啟用設備功能的新方法,但我相信這來自於計算的不同觀點,這是蘋果幾十年來身份的一部分,而不是從反競爭的角度來看。
為了支持 Fugu 項目中有些開放的硬件功能,特別是 WebUSB,Web 的信任模型需要超越權限提示和域/設備阻止列表,從證書頒發機構等信任生態系統中汲取靈感,以及包分發。
關於 SmashingMag 的進一步閱讀:
- 提高網站性能如何幫助拯救地球
- 邁向無廣告網絡:使在線經濟多樣化
- 除了編寫出色的代碼之外還有未來嗎?
- 在網頁設計中使用道德規範