用Python的hashcash打擊垃圾郵件(轉)

post0發表於2007-08-12
用Python的hashcash打擊垃圾郵件(轉)[@more@]

   Hashcash 是一個拒絕服務(denial-of-service)計數器度量工具。當前它的主要作用是幫助 hashcash 使用者避免因為使用了基於內容和基於黑名單的(blacklist-based)反垃圾郵件系統而丟失郵件。

hashcash 基礎知識

  hashcash 的靈感來自於這樣一個想法,即一些數學結果難於發現而易於校驗。一個眾所周知的例子是因數分解一個大的數字(尤其是因數較少的數字)。將一些數字相乘來獲得它們的積的代價是低廉的,但首先找到那些因數,而這項操作的代價卻要高得多。

  RSA 公鑰密碼系統就是基於這種因數分解特性的。如果應答者能夠回答因數分解質詢(Challenge),則說明他已經做了相當多的工作(或者偷偷地從生成那個組合的人那裡得到了因數)。

  對互動式質詢來說,因數分解足以勝任。比如,我有一個線上資源,希望您能象徵性地為其付出代價。我可以向您傳送一個訊息,說“只要您能因數分解這個數,我將讓您得到這個資源”。沒有誠意的人將無法得到我的資源,只有那些能夠證明自己有足夠的興趣、付出一些 CPU 週期來回答這個質詢的人才能得到這個資源。

非互動式質詢

  不過,有一些資源無法很方便地進行互動式協商。

  我的電子郵件收件箱是我非常重視的一個資源。但不期而至的訊息佔用了我的一些磁碟空間和頻寬,最糟糕的是,它們吸引了我的注意力。我並不介意陌生人給我寫信,但是,我希望他們能以稍微認真的態度,親自透過對我有價值的郵件與我取得聯絡。至少,我不希望他們是垃圾郵件製造者,那些人向我和上百萬的其他人傳送包含同樣訊息的郵件,期望我們中的某些人能購買某種產品或陷入一個騙局。

  為了實現非互動式的“支付(payment)”,hashcash 讓我向所有想給我傳送電子郵件的人都分發一個標準質詢。在您的訊息頭中,必須包括一個合法的 hashcash 戳記(hashcash stamp);具體地說,該標誌中包含我的收件人地址。

  hashcash 提出質詢的方式是,當透過安全雜湊演算法(Secure Hash Algorithm)進行雜湊時,要求“minters”生成一個字串(戳記,stamps),在它們的雜湊中有很多前導零。所找到的前導零的數目就是特定戳記的位元值。給定 SHA-1 的一致性與加密強度,找出給定位元值的 hashcash 戳記的惟一已知途徑是平均 2^b 次執行 SHA-1。

  然而,要確認一個戳記,只需要進行一次 SHA-1 計算即可。對於電子郵件中的應用來說,當前推薦使用的是 20-位元值:為了找到一個合法的戳記,傳送者需要進行大約一百萬次嘗試,在最新的 CPU 和經過編譯的應用程式上,這將需要不到一秒的時間。在相對舊一些的機器上它也只需要幾秒鐘的時間。

SHA 有多麼強大

  在一次被證明是密碼界中具有重大意義的事件中,披露出一個對 SHA-0 的碰撞(collision)。所使用的攻擊需要大約 2^51 步,遠遠少於我們所期望的暴力構造碰撞所需要的大約 2^80 步(以及儲存空間)(遵循“生日悖論(birthday paradox);關於生日悖論以及如何將它應用於雜湊函式的更多資訊的連結,請參閱參考資料)。

  在過於擔心這種與 bashcash 相關的攻擊之前,要緊記兩點:一是這種方法攻擊的是 SHA-0,不是 SHA-1(目前還不是)。另一相關的保證是,在當前最快的 CPU 上,2^51 步需要的時間仍會超過 9 CPU 年。即便有類似的方法可以應用於 SHA-1,構造虛假碰撞的代價也不可能低於構造更大數量的 20-位戳記(或者甚至是 40-位 hashcash 戳記)。

hashcash(版本 1)格式

  只有一個特定的 SHA-1 雜湊值是不夠的。我們還希望戳記特定於被請求的資源 ―― 也就是說,用於 mertz@gnosis.cx 的戳記應該與用於 someuser@yahoo.com 的戳記具有不同的適用性。如果不是這樣,垃圾郵件製造者就可以只生成一個高位元值的戳記併到處去使用它。

  另外,一旦生成戳記,我不希望每一個想給我傳送郵件的垃圾郵件製造者都能共享它。所以,hashcash 採用了以下兩個額外步驟(或者至少建議它們應該作為協議的一部分):

  首先,戳記攜帶一個日期。使用者可能會決定認為位元定期限更早的戳記是非法的。

  其次,hashcash 客戶機可能(並且多半應該)實現一個 double spend 資料庫。

  在 double spend 資料庫中,每一個戳記都只能使用一次;如果第二次收到它,那麼就認為它是非法的(非常類似於郵票在使用後會被做標記)。具體地說,hashcash(版本 1)戳記類似於下面的程式碼:

1:bits:date:resource:ext:salt:suffix

戳記包括 7 個域。

版本號(版本 0 更簡單,但是有一些侷限性)。

宣告的位元值。如果戳記沒有真正地使用宣告的前導零位元進行雜湊,那麼它就是非法的。

生成戳記的日期(和時間)。可以認為當前時間之後的戳記以及那些在很久以前的戳記是非法的。

戳記為哪個資源而生成。可能是一個電子郵件地址,但是也可能是一個 URI 或者其他命名的資源。

特定應用程式可能需要的擴充套件。任何附加的資料都可以放置在這裡,但是,在到目前為止的使用中,這個域通常是空的。

  將該戳記與其他所有人為相同的資源在同一日期生成的戳記區別開來的隨機因子(salt)。例如,兩個不同的人可以合情合理地在同一天向我的同一個地址傳送電子郵件。他們不應該由於我使用了 double spend 資料庫而無法傳送成功。但是,如果他們每個人都使用一個隨機因子,那麼完整戳記將是不同的。

  字尾是演算法真正起作用的部分。假定給出了前 6 個域,為了生成一個透過期望數目的前導零進行雜湊的的戳記,minter 必須嘗試很多連續的字尾值。

  現在讓我們來看 bashcash 如何在電子郵件中起作用。

bashcash 如何在電子郵件中起作用

  在理想的世界中,所有傳送者都應該在他們的訊息中包含 bashcash 標記;接收者在接收時都將檢查它們的合法性。不過,在實際生活中,hashcash 還沒有得到那麼廣泛的應用。雖然如此,開始使用 bashcash(不管是作為傳送者還是作為接收者)並不會對現有電子郵件工具產生任何影響。換句話說,在電子郵件中使用 bashcash,您不會有任何損失。

  為了給發出的訊息加上戳記,只需要向電子郵件新增標頭檔案即可:用於電子郵件的每一個 To: 或 Cc: 接收者的 X-Hashcash 頭。例如,某個想給我傳送訊息的人可能會在訊息中包含一個與示例 rfc2822 標頭檔案類似的標頭檔案:

X-Hashcash: 1:20:040927:mertz@gnosis.cx::odVZhQMP:7ca28

  顯然,應該由 MUA(郵件使用者代理,mail user agents)、過濾器或者 MTA(郵件傳輸代理,mail transport agents)來做這件事情,而不是要求使用者手工完成。不過,手工完成也不太難,至少實驗時如此。首先,透過檢視戳記的雜湊來校驗它,如下所示:

$ echo -n 1:20:040927:mertz@gnosis.cx::odVZhQMP:7ca28 | sha

00000b50b85a61e7ba8ac4d5fed317c737706ae5

  注意前導零(每一個十六進位制數是 4 個位元)。當然,還需要校驗哪個資源是您識別出來的那個資源(比如您的收件人地址之一),那個戳記還沒有被使用過,日期是當前日期。另外,一個合法的戳記擁有的前導零的數目應該與其宣告要擁有的數目相同(不過您可以決定強制實行您自己的允許郵件透過的最小代價:20 位元是一個不完全標準(semi-standard),它最終可能會隨著 Moore 定律而發生改變)。

什麼這會起作用?

  生成一個 20-位元的戳記只需要幾秒鐘的時間。當您一天中只傳送幾十封電子郵件時,這個代價並不大。但是,對那些想要傳送數百萬訊息的垃圾郵件製造者來說,不能容忍每條訊息使用額外幾秒的 CPU 時間。一天之中只有 86,400 秒。即使垃圾郵件製造者利用植入木馬(trojans)的殭屍(zombies)的技術,需要使用具體的 hashcash 戳記至少也會減少那些殭屍程式的發出量。當然,校驗一個戳記所需的時間只是一秒的一小部分。

  另一方面,向您自己的 MUA 新增 hashcash 生成和校驗對其他所有人沒有任何負面影響(不像其他一些反垃圾郵件方法)。對那些不使用該協議的接收者而言,這些只是一個他們很容易忽略的附加標頭檔案。對那些沒有新增 hashcash 戳記的傳送者而言,檢驗 X-Hashcash: 的接收者不用校驗任何內容。如果傳送者沒有新增戳記,那麼您的境況不會因為進行檢驗而變得更糟;也不會因此變得更好。

  一個好的 MUA 或者垃圾郵件過濾系統可以將擁有合法 hashcash 戳記的電子郵件列入白名單(whitelist)。SpamAssassin 甚至更巧妙地為更多合法 hashcash 位元提供了更高的 +ve 分數。我認為,將基於 bashcash 的方法應用於白名單是對 TMDA 等互動式質詢系統的改進 ―― 質詢訊息在返回時不會丟失,傳送者不會忘記響應質詢。質詢響應就在原始訊息之中(作為一個 hashcash 戳記)。

hashcash 的其他應用

  hashcash 對非互動式質詢最為實用。不過,沒有理由使得它不能同樣用於互動式上下文中。隨著更多工具增加了對 hashcash 的支援,尤其是 Mozilla 套件等多用途應用程式,在互動式和非互動式條件下使用 bashcash 都同樣變得更加簡單。

  例如,如果 Thunderbird 郵件工具得到了進行 hashcash 計算的 API 呼叫,那麼它應該直接讓它的同屬工具 Firefox Web 瀏覽器用與生成 hashcash 戳記的 API 去響應互動式質詢。

什麼是 Wiki?

  Wiki 是“可以運轉的最簡單的線上資料庫”。它支援設計用於動態建立新頁面和頁面之間交叉連結的超連結和簡單文字語法處理。

  Wiki 是伺服器軟體,允許使用者使用瀏覽器自由地構建和 編輯 Web 頁面的內容,提供了一種“開放編輯”服務,從而促生了一種不同尋常的群組通訊機制。它不僅允許所有使用者編輯頁面的內容,還允許使用者編輯對頁面或者站點做出貢獻的組織。

保護 Wiki

  Wiki 有時會遭遇到與垃圾郵件十分類似的破壞,bashcash 在非電子郵件上下文中似乎是一個不錯的解決方案。由於 Wiki 通常開放給任何人進行編輯,所以 Wiki 社群的災難之一是 Wiki-crawling 破壞程式,它們向 Wiki 站點新增一些無關的商業連結。

  我幫助維護的一個 Wiki 最近不斷遭到惡意破壞,迫使我們做出了有些不受歡迎的回應,要求所有張貼者擁有一個使用者帳號。這些帳號都是在一視同仁的基礎上給出的,並根據自動使用電子郵件傳送的質詢來返回一個證明已經收到隨機金鑰的訊息。不過,要求使用這樣的帳號從根本上說與 Wiki 精神是相違背的。

  新增 hashcash 質詢並不能防止對 Wiki 站點的自動破壞,但是它可以使破壞行為變得更慢。如果破壞一個站點需要的時間是很多秒,而不是一秒的一小部分,那麼檢索 Wiki 找出無用資訊就不那麼引人注目了。實際上,我認為在這種應用中,使用大於 20-位元的傳輸率是一個好主意。也許 24 位元或 28 位元是合理的負荷(已經登入的使用者仍然可以避開它)。

  您可能會認為,在接受 Wiki 編輯時,普通的時間延遲會有類似效果,不過這種思維方式中有一個漏洞。破壞者可以並行化其破壞行為 ―― 例如,如果每個站點新增了 5 秒的延遲,那個破壞者可以利用這 5 秒鐘的時間來開始對其列表上的其他 Wiki 進行修改。透過要求保證有效 CPU 的利用率,比如使用 bashcash,破壞者再也不能並行地進行破壞。

  Wiki 質詢可以是互動式的,也可以是非互動式的。站點在將使用者引導到實際的編輯螢幕之前,可以直接將使用者引導到一個質詢螢幕。可以生成一個隨機資源來作為這個保護螢幕的質詢。

  不過,更好的方法是使這項要求具有非互動性。例如,在一個已有的 Wiki 系統中,可以使用與下方所示類似的 URL 來編輯某個資源:

  在一個假定使用 bashcash 進行保護的 Wiki 中,可能需要使用不同的 URL,比如:

  在允許編輯之前,Wiki 伺服器可以校驗該戳記。不過,進行編輯不需要建立一個帳號和透露任何個人資訊。double spending 和(可能持續時間較短)過期校驗進一步為真正要進行編輯的行為提供了保證。對我而言,生成上面的 URL 並不難,使用下面的命令即可:

hashcash -mCb 24 -x edit SomeTopic

  不過,通常,為了確保更少的延遲,Web 瀏覽器可能會選擇在後臺生成類似的戳記。例如,當我正在讀取資源時,上述 URL 可能已經建立在快取記憶體中:

  或許還將快取其他一些編輯戳記,將它們用於當前 Wiki 頁所連結的頁面。

檢驗 CPU 資源

  hashcash 的一個互動式應用可能是用於分散式處理任務中。一些專案(比如 Great Internet Mersenne Prime Search(GIMPS))和 SETI@home 及其任務(比如蛋白質摺疊和密碼方面的難題)有時會借用大量的志願者機器,這裡只列出了其中少數專案和任務的名稱。每個志願者都只需要下載一些程式碼,並將其作為一項大任務的一部分來執行,然後將中間計算發回中央伺服器即可。這些工作是對空閒 CPU 週期的極好利用。

  我所知道的所有分散式任務幾乎都允許任何人加入。不過,不難想像,對於有協同要求的任務而言,如果一個節點不能在期望的時間段內完成其任務,那麼這個行動遲緩的節點對整體計算造成的損害要比它所做貢獻多一些。

  在這種情況下,應該要求每一個參與節點都有最小限度的 CPU 速度。雖然使用具體型別的計算來檢驗速度更為精確,不過,hashcash 還提供了一個相對通用的 CPU 基準。SHA-1 是一種非常典型的數學計算。如果參與節點已經安裝了 hashcash(而不是一些定製的軟體工具),那麼,對 hashcash 質詢的回答就可以作為一種“必須達到某種高度才能登堂入室(you must be this tall to enter this ride)”風格的校驗。

  校驗 CPU 能力的方法是,要求在短期間內得到高位元值。只有足夠快的 CPU 才能回答這個質詢。為此,必須半互動式地提供資源名。否則,參與者完全可以遲籤他們的日期戳的日期,製造出建立速度很快的假象。

  例如,一個快速的 Pentium III 或者 G4 可以在不到一秒鐘之內生成一個 20-位元的戳記,但是 Pentium-II 或者 G3 做不到。我們可以假定一個 32-位元的質詢,試執行的候選機器必須在一個小時之內回答它。請求者可能會發一封電子郵件,說:“向我傳送一個質詢”;協同伺服器作出響應:“時間是 040927124732;質詢資源是 a37tQk。”如果伺服器在當天下午的 1:47 之前得到了一個正確的雜湊,那麼該請求者將獲得訪問該資源的資格。

  顯然,我所建議的協議不能確保在每個節點上都能真正地完成工作。即使是最快的機器,也可能會出現斷電的意外情況。使用者可能會改變他們執行分散式軟體的想法。不過,至少可以證明其具備似乎可信的資格。

通用的 hashcash 以及我的貢獻

  從 hashcash 概念整體來看,具體域和分隔符的使用從某種程度上說是任意的。實際上,hashcash 版本 0 使用了與版本 1 不同的域。這些選擇都很好,不過,我認為“實際的 hashcash”只是某個家族的一名成員,我們可能會稱這個家族為“通用 hashcash”。也就是說,只要給定任何質詢字串,都可以合理地提出以下要求:“給我一個字尾,一旦 challenge+suffix 被雜湊,它將生成 b 位元的碰撞”。真正的 hashcash 只不過是這種通用質詢的一個例項。

  現在,確實存在過於通用的問題。建立很多不相容的、近似 bashcash 的協議實際上對誰都沒有好處。例如,有一個“hashcash”的 Python 實現,使用了一個與 bashcash 有一點類似的質詢協議(可能對加密價值而言也是如此),但是幾乎不能使用它生成 hashcash 戳記。

  所以,我決定編寫一個真正適應的 bashcash 的 Python 實現,它甚至可以接受與用 C 編寫的 hashcash 工具大致相同的命令列開關(不過,可能最為實用的是作為一個匯入模組用於其他應用程式)。即使是在得到了 Psyco-ization 的幫助(只是一點點)的平臺上,Python 版本最快執行也要比最佳化的 C 版本慢 10 倍。不過與 C 相比,它在靈活性方面依然可以勝出。

  除了正確無誤,我的 hashcash.py 模組還提供了一個內部函式 _mint() 以及一個公共函式 mint()。後者生成真正的 hashcash 版本 1 戳記。那是您應該使用的。

  不過,前者,即 _mint(),完成了尋找 generalized hashcash 字尾的底層工作。您可能不應該使用它,但是,如果您想要使用它(並且保證您會小心使用它),它就在那裡,您可以使用。

  在不同尋常的上下文中,bashcash 的變種可能很實用。無論如何,我希望 C 工具有類似的開關,即使是在 man 頁中有關於您為什麼不應該那樣做的危險警告,它們也能夠找到通用的 hashcash 字尾。我們電腦駭客喜歡深入到事物內部。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-951087/,如需轉載,請註明出處,否則將追究法律責任。

相關文章