題:
利用節日票被掃描時的延遲
O'Niel
2019-07-29 16:18:55 UTC
view on stackexchange narkive permalink

票務系統如何工作

票務系統(您在節日期間看到的票證系統)的工作方式如下:當用戶付款時,會在數據庫中添加一行,列名為 is_scanned ,其默認值設置為false。

節日期間,守衛一使用設備掃描條形碼(包含ID和唯一的哈希),就會發送請求到數據庫以檢查:

  1. 與ID和哈希匹配的用戶是否已經付款,並且
  2. is_scanned 的值是否仍然設置為 false
  3. ol>

    如果同時滿足兩個條件,則會將值 is_scanned 設置為 true 以防止其他人復制票證/條形碼進入。

    漏洞問題

    這裡的問題是請求方發送請求到發送請求之間的時間掃描設備,並且值 is_scanned false 切換為 true

    請考慮以下場景里約:愛麗絲(Alice)擁有一張有效的機票,並已付款,但隨後她讓夏娃(Eve)複製條形碼,並將假機票上的可見名稱從愛麗絲(Alice)更改為夏娃(Eve)。所以現在我們有兩張票。一個有效和一個欺詐,但都具有相同的條形碼,唯一的區別是名稱。

    如果進入節日的愛麗絲和夏娃的票被完全同時掃描怎麼辦? ?票務系統不會及時將 is_scanned 切換為 true ,以確保Eve不能輸入與Alice相同的條形碼。導致檢票員(有效票證和欺詐票證)對守衛都顯示為“有效”。運氣,雖然在理論上有可能...在實際情況下,這可能會失敗。

    但是,在理論上我們又如何打敗這種利用?

    我已經使用以下方法考慮了此漏洞:掃描條形碼時,我不僅顯示票證是否有效(滿足前面所述的條件),還顯示 數據庫中的名稱。如果名稱與故障單上的名稱不匹配,我們就知道故障單是通過某種方式操縱的。另外,如果掃描設備上顯示的名稱與ID上的名稱不匹配(每個人都必須出示該名稱以證明年齡),則也不允許輸入該名稱。

    唯一的方法是繞過此解決方案的是身份欺詐,這當然不屬於票務系統的責任。

    Delay

    從理論上講,解決此問題的另一種方法是添加一個對數據庫/驗證API的每個請求之間的隨機延遲時間。這樣,沒人會同時掃描他們的票...因為每次的驗證時間都被延遲了隨機的毫秒數。

    我不喜歡這個,因為它:

    1. 如果入口沒有足夠的延遲,則會使入口處的一切變慢
    2. 無效。因為如果數據庫花費50毫秒將 is_scanned false 更新為 true ,則唯一的解決方案是將其延遲最小間隔每次50毫秒。
    3. ol>

      其他解決方案?

      您認為還有哪些其他解決方案可以解決此漏洞?

評論不作進一步討論;此對話已[移至聊天](https://chat.stackexchange.com/rooms/96932/discussion-on-question-by-oniel-exploiting-the-delay-when-a-festival-ticket-is)。
八 答案:
Benoit Esnard
2019-07-29 16:32:12 UTC
view on stackexchange narkive permalink

您描述的漏洞是競賽條件

有幾種處理方法,但是我會使用 SELECT ... FOR UPDATE SQL查詢,該查詢將鎖定選定的行,以防止在提交當前事務之前進行新的寫操作。

請務必檢查您的RDBMS文檔,以檢查如何正確實現它:

非常感謝!我只是想問問是否有辦法在相同的時間跨度內對同一行的請求進行排隊。但這更好。;)
實際上,這如何解決問題?因為您實際上應該阻止讀取而不是寫入。因為當Foo的票被掃描時,第X行被鎖定以進行寫...但是,當Bar的票被掃描時,他仍然可以讀取X行,並且由於is_scanned為false,它仍將顯示有效。是否可以更新無關緊要?
像這樣進行手動鎖定會更好嗎?https://stackoverflow.com/a/16119066/6533037嗎?當列數量小於默認值時,我還可以返回false。還是SELECT ... FOR UPDATE完全一樣?
@O'Niel:的“ SELECT ... FOR UPDATE”查詢也會阻止其他“ SELECT ... FOR UPDATE”查詢!
鎖定絕對是防止此問題的最簡單方法。
您還可以使用樂觀鎖定
-1
@O'Niel在這種情況下,我認為樂觀更合適,因為實際爭用的機會非常非常低。
您用於API的編程語言也_likely_為您提供了創建鎖(鎖定,讀取,更新,解鎖)的可能性。優點是即使您更改了數據層,您仍保持鎖定機制。
對於眼前的問題,很好的答案,但是下面有很多失敗的情況。如果無線掃描儀中途斷開連接怎麼辦?如果有人不小心兩次掃描了票怎麼辦?如果某個人被掃描但由於某種阻礙而無法立即進入地面怎麼辦?實際的問題是:**您有兩項交易:一項是在數據庫中,另一項是在現實世界中的狀態(如果此人已通過登機口進入,則需要以原子方式進行理想同步)**這是不容易實現的。因此,您將不得不包含很多錯誤處理
在QR碼上添加一些類似於JWT的HMAC非對稱密鑰驗證可以通過對實際有效載荷進行簽名來幫助防止“照片購物” /偽造的QR碼。支持分佈式Redis(或類似)的鎖可以防止類似雙重支出的行為,而不會降低數據庫的速度。
-1
@Falco您的評論很好。確實,人為因素可以緩解這些事件。即最終**如果他們有過失,警衛可能會讓您進來**
儘管“ SELECT ... FOR UPDATE”肯定會緩解競爭狀況,但這是以增加資源(在數據庫內)為代價的。如果每個事件都有一個單獨的數據庫,那沒什麼大不了的。但是,假設您同時發生數百或數千個事件,並且每個事件有成千上萬的參與者都試圖在短時間內掃描其票證。您需要確保有足夠的資源來支持用例。對於大多數RDBMS,附加鎖需要更多內存。也許不多,但乘以成千上萬的並發交易加起來。
@ChristopherSchultz這在很大程度上取決於數據庫的實現。如果您的數據庫支持真正的行級鎖定,那麼擴展就應該沒有問題。許多數據庫僅支持塊級鎖定,在這種情況下,可以通過對相關表進行智能存儲或分區來解決。而且我不能太強調:數據庫是為數百萬行和數千個並發事務而創建的,只要您的事件不包括地球上所有人類的相關部分,您就可以了;-)
如果有人“同時發生數百或數千個事件”,那麼@ChristopherSchultz肯定會擁有足夠的資源和專家來處理它,而無需在這裡詢問,對吧?當公司規模如此之大時,它已經擁有了學習擴展所需的所有時間。
-1
@ChristopherSchultz為什麼要追溯?我在美國尋找最大的機票銷售商。我的搜索返回了Ticketmaster。他們在本週末出售1214個活動的門票。如果他們能以某種方式賺錢來擴大數百倍,他們為什麼不能提前計劃呢?如果增長緩慢,為什麼他們同時不能切換平台呢?他們將比目前最大的門票銷售商擁有數百倍的錢,對吧?並假定他們將隨時決定不針對每個事件,每個城市或其他可管理的工作負載單獨分配服務器。
nvoigt
2019-07-29 17:56:18 UTC
view on stackexchange narkive permalink

這裡的另一個解決方案是絕對正確的,並且對於不那麼容易的大型系統有意義。

使用相對簡單的數據,您可以使用非阻塞選項:

  UPDATE [FESTIVAL_TICKET] SET IS_SCANNED = TRUE TICKET_ID = @ScannedKey AND IS_SCANNED = FALSE  

現在,這是一個原子操作。數據庫中沒有兩個用戶可以發布此 並讓其更新行。返回的人“受影響的第一行”(顯然,有一種方法可以在代碼中查找出來,對此不進行語法分析)可以進入。其他人將受到該語句影響的零行。如果您希望變得用戶友好,現在可以檢查為什麼找不到它,它是錯誤的ID還是已經被掃描。

但是重要的細節是該語句是原子的。無論距零時差有多近,只有一個人會贏。因為您不再具有讀取功能,而又沒有 then 編寫功能。您具有一次原子操作的讀寫功能。

評論不作進一步討論;此對話已[移至聊天](https://chat.stackexchange.com/rooms/96933/discussion-on-answer-by-nvoigt-exploiting-the-delay-when-a-festival-ticket-is-sc)。
J. Chris Compton
2019-07-31 03:07:34 UTC
view on stackexchange narkive permalink

的缺點似乎是一個人可能免費進入(帶有復制的票證)。

對於可能是正確的小事件。
但是,如果由於任何原因增加了過多的延遲,則冒險的風險將超過一個人
如果他們的設備卡住或速度太慢,票掃描器只會讓一些額外的人通過...因為,大多數人可能都擁有有效的票,對嗎?

我在本日曆年度的一次大型活動中看到了這種情況,成千上萬的粉絲參加了許多聽過的音樂家。
票務公司是一家重要的公司(也許是您工作的公司?),並且該公司是在專門為取票而建的網站上。
我的聚會是未經掃描就通過的公司之一(是的...我有一張有效/合法的機票)。
我不得不站在那兒,看著售票員幾分鐘,然後才能弄清楚為什麼會這樣。

TL; DR;
在涉及人員參與時,不要為所有可能的情況進行編碼
拍兩個或三個九(99%-99.9%),稱它為一天。
僅在涉及機器時保存划痕...那是您可以獲得9的一堆。

最糟糕的情況甚至不是,這是因為您的保安人員確實採取了機器人行動,數十或數百名持有合法機票的人被封鎖了。如果那件事發生了,你的故事會大不相同,而不僅僅是“一次有趣的事情發生在我身上”。
usr-local-ΕΨΗΕΛΩΝ
2019-07-30 18:58:37 UTC
view on stackexchange narkive permalink

這個答案已經是一個好而便宜的答案,但是,我將從軟件工程和安全性的角度來添加自己的答案。

這可能對將來類似的有關不太可能的利用的問題很有幫助。雖然在理論上有可能……在實際情況下,這可能會失敗。

然後,與成本相比,潛在的損失是多少?我將證明花更多的心力和增加安全性的頭痛不值得冒險。

現在,已經提出並接受了該解決方案以通過SQL事務正確處理競爭條件,從而將責任/成本轉移到了數據庫,是最好的,行業標準的和便宜的解決方案。正如答案所指出的那樣,這可能是案件的結局,因為答案的確被接受了

如前所述,兩名乘務員都在非常準確的時刻被掃描並觸發如果不是十億大小,則種族條件利用的估計值可能高達數百萬。為使您對億萬富翁賠率有一個定性的想法,請閱讀有關彩票的文章,並發現在第二個列表頂部的SuperEnalotto玩遊戲可能很容易與掃描兩張票相比,獎勵肯定是一致的。賠率表示漏洞的可利用性,通常被定為謹慎級別([極不可能],[極]可能)。我總是將不確定性與安全性相關的事件與彩票進行比較,以提供更熟悉的比較。

為進一步說明,賠率受以下因素影響:

  • 兩者同步在隊列中的移動,並同時將票證交給警衛。這意味著這兩個人之間經常保持交流並訓練有素,更不用說運氣了(奇怪!),他們的線路以可預測的速度運動
  • 警衛的身體運動。並非所有警衛都以毫秒為單位精確掃描票證,他們以不同的速度移動武器。一張票可能會掉落在警衛的手中,一名警衛可能會把票倒轉。一名警衛可能會轉身檢查線路是否在他們後面被堵塞。換句話說,沒有足夠的熵來計劃攻擊
    • 無人售票機可能不受此因素影響
  • 掃描計算機票證所花費的時間,因此兩次掃描屬於同一時間段並利用了漏洞。

因此這是軟件工程方面的考慮。

門票有價格,所以值得X $。我估計X的大小可以在50-100之間。對於每個利用此漏洞並以欺詐方式進入該設施的人,將損失$ X。

實施更複雜的檢查(例如護照名稱控制)在以下方面都是很昂貴的:兩者軟件開發階段所需的代碼,以及人員進入工廠所需的時間。保安人員按小時付費。實施 Ben-Gurion風格的安全審查 *實在是昂貴且痛苦。

現在,您要睡得更香,確保沒有人可以利用您的系統。它要多少錢?在您為保護系統付出了巨額資金之後,您可能會發現,運行“不受保護”系統的競爭對手承受了$ 80的損失,賠率超過數百萬個。很難量化這種可能性。既然您有更多的機率贏得世界上最艱難的彩票,那麼最好押掉永遠的工作!

結論:在我們的職業中,贏得機率是我們最好的熟睡伴侶!

結論2:種族條件攻擊可以可能在自動網絡系統上被利用,攻擊者顯然可以將他們同步到微秒!!!這也可能會增加損害,因此歡迎採取最佳的安全措施!

結論3:如果系統已經在運行,請使用可接受的答案為系統打補丁(設計,開發,測試,UAT,部署,PMO ...) 比潛在損壞要貴得多。請在下面發表評論

*我舉了一個例子,因為傳說中以色列的機場安全線漫長而徹底

這聽起來像是律師調動了項目經理的理由是不將工作做到100%...在您使用帖子中的示例時,我也將使用該示例,並給您解決問題的成本少於一分錢,或者您的開發人員鍵入@nvoigt's代碼花費的時間。如果不良行為者發現此漏洞,他們將找到一種利用此漏洞的方法:如果他們的隊列走得更快,讓後面的人走,在其他人處於同步位置的情況下,在袋子裡隨機移動以找到門票,以及也喜歡“ ...可能不會受到影響”,那是櫻桃inthispost
先生,我真的希望有您這樣的評論,我很高興擴大發言範圍。我通常不建議“不做”該工作,而通常建議將該工作放入積壓中,但顯然仍然可以做。那有很大的不同。除非您擁有完美的CD(連續交付)和云自我更新功能,否則進行UAT和產品推出會產生成本。特別是對於售票機,推出軟件可能意味著通過有線方式更新固件。如果您進行大量更改,則成本將混合在所有更改中。
同樣,項目經理(相對於編碼人員)通常會看到的一件事是,雖然看起來鍵入某些代碼的成本似乎微不足道,但總會有成本和與軟件更改相關的風險。需要花很長時間來解釋,但請考慮以下真實情況:在黑鴨掃描報告存在漏洞(最終無法利用)後,將Hibernate 3.5.x更新為3.5.y破壞了無法完全重新使用的生產應用程序經過測試。我花了3秒鐘的時間來更改Hibernate版本和幾天的時間來處理團隊災難
您的評論假定唯一的更新將是漏洞處理,而OP的問題看起來像他仍在開發之中。 在最後一條評論中,我想知道您在prod中更改了軟件版本,而僅更改了軟件版本,這使prod中斷了?
我不清楚該軟件仍在開發中。是將更改推送到積壓的好時機。是的,我是說我只在框架項目中更改了Hibernate版本,重新測試了框架,讓其他開發人員將新的框架版本及其Hibernate引入了他們的項目,沒有進行完整的重新測試,並且發現了HQL使用的是早期版本所允許的非法語法**。這件事發生在很多年前,所以我沒有完整的詳細信息(而且我不知道是否可以分享這些信息)
結論3仍適用IMO。**如果**您的系統正在運行並且發現了一個漏洞,則如果您確定賠率和損失都非常低,則應首先對其進行“分類”,而不要“匆忙”修復它。高影響力/可利用性漏洞應該*仍*盡快修補,這是沒有討論的
如果與其他錯誤結合使用,則競賽條件錯誤可能會更加嚴重。例如。如果存在嚴重降低系統響應時間的錯誤,則可能會將完全不太可能發生的競爭狀況事件處理為非常非常可能的情況。根據James Reason的瑞士奶酪模型:不要等到奶酪片對齊並證明您的“修補[..]的努力比潛在的損害更昂貴”時-假定錯誤:修復錯誤!
如果不認可要點,則您的帖子可能會通過與人身安全進行比較來更好地說明您的觀點,例如有人可能會跳籬笆,從窗戶爬進去,或讓他們的好友從某個側門進入。或與其他風險進行比較,例如警衛人員/場地失去與中央數據庫的連接。修復錯誤並減少漏洞是一件好事,但是我寧願看到花費在例如尚未計劃到中央數據庫的網絡連接丟失的應急計劃。
Cyrbil
2019-07-31 22:06:57 UTC
view on stackexchange narkive permalink

這裡已經有很好的答案,涵蓋了數據庫的大部分漏洞利用。但是,我想添加我的現實生活經驗,在活動(露天節)領域工作並設計票證驗證系統和應用程序。

最大的挑戰之一是網絡穩定性,因為所有掃描設備一直都具有網絡功能的假設是完全錯誤的。在掃描過程中隨時可能會出現延遲,中斷或不可用的情況。流程,並且不應延遲客戶進入事件(至少從我們的角度出發,其他事件可能需要更嚴格的驗證)。

在我們的應用程序中,票證是使用簽名驗證的,但是僅在網絡啟動時才同步並提交到數據庫。該應用程序將經過驗證/待提交的票證存儲在存儲桶中,並在網絡可用後嘗試提交盡可能多的票證。這也避免了對每張票執行一次 INSERT

在事件條目的最遠位置,wifi根本無法到達。為了節省僅覆蓋10米的覆蓋範圍的另一台路由器的成本,掃描設備可以在它們之間進行通信並共享其連接。這意味著,如果僅其中一台設備可以訪問wifi,則其他設備可以從理論上將其存儲桶發送到該設備,或者發送到可以轉發該設備的範圍內的最近設備。掃描設備每分鐘至少失去一次連接。

從理論上講,一張票可以被任意數量的未連接設備掃描,但每個設備只能掃描一次。這是一個種族條件,但是要利用其他答案提到的方法卻不那麼容易。


如何防止這種情況出現?

您可以通過刪除並行操作來防止爭用情況,這是鎖的作用。由於您基本上會降低數據庫接受並發寫入的能力,因此它將引入延遲。

然後問題變成了,值得嗎?我們可以接受更多的延遲,直到為了確保正確身份驗證而進行驗證為止?這會阻止人們發現敞開的門或跳柵欄嗎?

+1體驗真實生活
如果您必須依靠不良的網絡連接,則可以為網狀網絡/閒話協議想法+1
paolord
2019-07-31 08:06:54 UTC
view on stackexchange narkive permalink

一個好的解決方案可能是使用消息隊列而不是設置延遲。掃描的票證不會立即得到處理,它會一直等到所有已發送的票證才被處理。除非票證已離開隊列並已正確處理,否則係統不會返迴響應。有人認為這可能會很慢,因為每個人都必須等待其他人完成。但您可以在邏輯上分片票證ID,例如:2個隊列,1個用於奇數票證,1個用於偶數票證。或者只是簡單地將組號放在id本身中。

對於具有1,000,000張票證的事件,即使是普通的台式PC也可以運行一個程序,該程序以每秒超過一百萬張票證的速度在內存中建立掃描票證列表並檢查重複項。甚至在網絡瀏覽器中一個相當簡單的Javascript程序也可以接近該速度(我剛剛在瀏覽器中生成了100萬張隨機票證,並在1.2秒內統計了999,870個唯一票證)。即使有一萬個登機口的人都在掃描票,最壞情況下的延遲也只有一秒鐘的時間。
@supercat門需要相互通信。通常不為特定門保留門票。您的簡單javascript程序將允許接受一萬份相同的票證。像往常一樣,限制因素不是CPU,而是IO。記住,我研究的票務系統必須能夠容忍連接失敗,因此它們總是不得不犯下“寧可讓沒有票的人進入,也要讓有票的人拒之門外”。
@Luaan:不,我是說所有的門程序都通過一台PC發送請求。如果一台PC花費不到兩微秒的時間來處理每個請求(在瀏覽器中用簡單的Javascript即可輕鬆實現的性能水平),或者即使花費十倍的時間,那麼即使一次從所有10,000個門接收到請求,服務所有這些請求將花費不到四分之一秒的時間。
cjs
2019-07-31 09:44:38 UTC
view on stackexchange narkive permalink

這是一個典型的問題,是由於您的會計系統的設計(此處的“會計”是在跟踪任何資產或項目的使用,而不僅僅是金錢)與您要跟踪的真實世界信息之間的不匹配引起的。 p p>

您已經說明,在現實世界中,很可能對代碼進行兩次掃描,並且您希望跟踪這種情況,但是您的數據庫僅存儲“已被掃描”。就目前而言,這是不正確的: SET is_scanned = TRUE WHERE ticket_id =?確實是正確的(真實世界之前的狀態是“ ticket_id?已被掃描”,也就是說

而不是保持狀態,而是保留一個分類帳:標題為 ticket_id,scanned_timestamp的表 scan ,scanner_id ,鍵是這三個字段的所有 。¹因此,您不僅要記錄已掃描票證,還要記錄記錄的每次掃描。票。如果沒有包含 ticket_id =?的行,則您將知道從未對票證進行過掃描,否則,您將獲得掃描票證的所有時間(以及位置)的列表。

現在,您的數據庫能夠更好地模擬現實世界的信息,您可以決定要對此做些什麼。您的特定DBMS可能會提供一些解決此爭用條件的方法( INSERT ticket_id,scanner_id INTO掃描WHERE ticket_id NOT IN掃描或其他任何方法),在這種情況下,您肯定要防止同一ticket_id出現兩個條目。或者,您可以簡單地讓兩個人都進入,然後再分析您的記錄,以弄清您似乎擁有兩張具有相同ticket_id的票證的原因。

您想走的路實際上是業務決策,而不是技術決策。有很多原因可能會導致您錯誤地發行兩張具有相同ticket_id的有效票證(例如,與企圖退貨有關的錯誤),或者同一人需要使用兩次票證(忘記在出口處掃描)。如果嘗試欺騙系統的人數低於一定水平,那麼您的企業最好吃一些欺騙者的費用,而不是支付拒絕有效票證持有人的費用(聲譽,訴訟或其他方式)

簡而言之:使用分類帳而不是說明適用的位置,使數據庫設計與實際的實際情況相匹配,並讓企業製定有關如何應對這些實際情況的業務決策。


¹我在這里大肆揮舞著細節,以使答案簡短,只表達一般性想法。
²在這裡,我假設採取了一種具有相同效果的適當一致性策略將這些INSERT請求序列化到此表中。

OP不會要求“跟踪”此問題,而是要防止它。您已經在機制上“揮了揮手臂”以防止出現比賽情況,這將是答案。您的新餐桌創意有相同的比賽條件問題。
這種“插入”策略也容易出現競爭狀況,不過,在“ scans(ticket_id)”上添加唯一鍵應該足以解決該問題。
-1
@schroeder我的觀點是,該問題僅在某些商業條件下有效,發布者應在解決該問題之前明確考慮他的想法並明確做出該商業決定,否則就不存在。通常,我發現大多數問這個問題的人都沒有這樣做。
我同意其他評論者的觀點,即該策略仍然存在競爭條件。除了以“您的特定DBMS可能...”開頭的單句外,答案中沒有討論阻止此問題的“正確一致性模型”。如果您都在防止_and_日誌記錄,則可以使用其他答案之一,並創建“嘗試掃描”日誌,稍後可以對其進行分析以查看有多少人被拒之門外。因此,此答案中的技術細節有些令人分心:這實際上是問題的“挑戰”,詢問是否確實需要任何解決方案。
Džuris
2019-07-30 02:06:55 UTC
view on stackexchange narkive permalink

在Clojure中,它們具有稱為“原子”的功能來解決此類問題。

有兩個線程進入。它們都讀取值 is_scanned = false 並繼續進行。處理完他們的東西後,他們返回 is_scanned = true 來更新atom。但是,一個線程很快就會出現。

稍後出現的線程將更改原子,因此該函數將無法更新原子。相反,它將被告知使用現在更新的 is_scanned = true 值重新運行。現在返回的是另一張票不再有效。

這裡的視頻解釋了clojure的這一功能。

我不太確定這對問這個問題的人有什麼幫助,因為他們強烈暗示他們正在使用關係數據庫,並希望將其解決方案放在這裡。我也看不出這對總體上如何幫助開發人員系統更加安全,因為比賽條件並非Clojure(或任何一種語言)所獨有,並且Clojure也不壟斷如何處理比賽條件。
而且,即使您的軟件是用Clojure編寫的,如果您有如此多的請求,決定決定橫向擴展應用程序並運行多個節點,該怎麼辦?內存中的任何解決方案都行不通。您需要在數據庫中解決它。


該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 4.0許可。
Loading...