題:
在刪除變量之前覆蓋“敏感”變量是否是一種好的安全編程習慣?
Jonathan
2014-12-04 22:18:15 UTC
view on stackexchange narkive permalink

在刪除(或超出範圍)之前覆蓋存儲在變量中的敏感數據是否是一種良好的安全編程習慣?我的想法是,這將阻止黑客由於數據殘留而無法讀取RAM中的任何潛在數據。覆蓋幾次會增加安全性嗎?這是我在C ++中談論的一個小例子(帶有註釋)。

  void doSecret(){//您想要保護的秘密(最好不要使用)這樣硬編碼)int mySecret = 12345; //用數字做任何事情... // **通過覆蓋數字清除mySecret的內存** mySecret = 111111; mySecret = 0; //也許會在一個循環中重複幾次}  

一個想法是,如果這確實增加了安全性,那麼編譯器自動添加指令來執行此操作就很好(也許通過默認值,或者也許告訴編譯器在刪除變量時執行此操作。)


此問題被稱為 本周信息安全問題
閱讀2014年12月12日的 博客條目 以獲取更多詳細信息,或 提交自己的“本週問題” a> 。

另請參見:[操作系統級別視圖](http://security.stackexchange.com/questions/74288/is-data-remanence-a-concern-in-ram)
請注意,根據[語言]( http://stackoverflow.com/questions/3785582/how-to-write-a-password-safe-class)。 (請參閱兩個鏈接的問題,尤其是它們的答案)
還要注意,java中的字符串是不可變的,因此覆蓋(將新值分配給引用變量)將無效。
這不僅是一個好主意,而且您可能要採取的其他步驟是:“鎖定”內存(以確保不被寫入以進行交換),“ mprotect”頁面,以便在機密數據被保存後變為只讀狀態。初始化(還可能將頁面標記為“根本不可訪問”,除非您打算訪問該頁面的小窗口中的所有窗口),以便在機密信息之後立即將“ canary”值寫入內存,以便在dealloc期間檢測是否被溢出所覆蓋,並在發生溢出和下溢時向SEGV分配機密之前和之後的額外不可訪問保護頁。
請參閱libsodium的[`sodium_malloc`](https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/utils.c#L392)作為該實現的實現。
-1
除非您將變量聲明為“ volatile”,否則C / C ++編譯器實際上可以自由地優化覆蓋,因為它們不會改變執行代碼的結果(該值在任何地方都不會使用)。
NIST的[FIPS 140-2](http://csrc.nist.gov/groups/STM/cmvp/standards.html)要求進行零位化,即使在1級驗證時也是如此。攻擊者確實會使用內存,而不必是本地的。例如,我們知道[NSA會將Windows錯誤報告記錄到其XKeyscore系統中,以幫助獲得未經授權的訪問](http://en.wikipedia.org/wiki/Tailored_Access_Operations)。
儘管大多數答案都提到了覆蓋數據(如果可能)的好處,但我看不到問題中建議的“循環幾次”的好處,但是當前的AFAICS答案並未涉及到。除非將此數據寫入磁介質並且攻擊者無法訪問系統,否則我會認為一次覆蓋就足夠了?還是多次寫入仍然有益?
十 答案:
goodguys_activate
2014-12-04 22:26:55 UTC
view on stackexchange narkive permalink

是的,最好先覆蓋然後刪除/釋放該值。 不要假設,您要做的就是“覆蓋數據”或讓其超出GC的處理範圍,因為每種語言與硬件的交互方式都不相同。

在保護變量時,您可能需要考慮以下問題:

  • 加密(在內存轉儲或頁面緩存的情況下)
  • 固定在內存中
  • 將標記為只讀(以防止進一步修改)的能力
  • 通過不允許在優化編譯器中傳遞常量字符串來保證安全構造(請參見
  • 鏈接文章中的註釋 re:ZeroMemory宏)

“擦除”的實際實現取決於語言和平台。研究您使用的語言,並查看是否有可能進行安全編碼。

為什麼這是個好主意?故障轉儲以及包含堆的任何內容都可能包含您的敏感數據。保護內存中數據時請考慮使用以下內容

有關每種語言的實施指南,請參考StackOverflow。

即使使用供應商,也應注意指南(在這種情況下為MSFT)仍然可以轉儲SecureString的內容,並且可能具有針對高安全性場景的特定使用指南。

問題是先覆蓋然後刪除。您可以添加到答案中嗎?
雖然也可以在程序運行期間查看程序存儲空間的內容。刪除內存只會刪除指向內存的指針(就像刪除文件會從文件系統刪除指向文件的文件指針一樣)。覆蓋它有助於保持其“安全”。
假定覆蓋實際上是覆蓋它,並且不只是將指針移動到具有新值的內存的某個新部分(這意味著舊數據會一直存在,直到重新使用內存為止,就像刪除它一樣)。
@lawtonfogle指針通常是內存地址,因此您通過指針寫入的任何數據都將進入該地址空間。雖然這是一個有趣的想法。下文所述的方法與使用指針不太一樣。
@Desthro:除非您在實模式下工作(即,您正在編寫OS內核),否則指針通常只是虛擬內存地址;只有內核有權訪問實存儲器地址。當內存被覆蓋時,操作系統當然有可能將兩個內存頁面移至不同的實際地址,這可以在不更改虛擬地址的情況下完成。例如,這發生在交換期間。
@LieRyan顯示了我的編程知識有多老;)
我認為您絕對希望volatile關鍵字確保發生的事情實際上是覆蓋
@raptortech97是否確實確保“ volatile”呢?假設在敏感變量可以被覆蓋之前,進程已被掛起並且其內存已被調出,然後將其恢復。無法保證虛擬內存將被分頁回物理內存的同一部分,是嗎?在這種情況下,“ volatile”不能保證覆蓋。我認為。老實說,我不確定。 (`volatile`阻止了某些編譯器優化,我明白了,但似乎您在說它的作用遠不止於此)
我覺得我們在這些評論中確定的是,計算機很難“智勝”
Lawtonfogle
2014-12-04 22:45:14 UTC
view on stackexchange narkive permalink

存儲不再使用的值?似乎可以對其進行優化,無論它可能帶來什麼好處。

此外,您可能實際上並不會根據語言本身的工作方式覆蓋內存中的數據。例如,在使用垃圾收集器的語言中,不會立即將其刪除(這是假設您沒有留下任何其他引用)。

例如,在C#中,我認為

  string secret =“我的秘密數據”; ...大量工作... string secret =“ blahblahblah”;  

“我的秘密數據” 一直掛到垃圾被收集,因為它是不可變的。最後一行實際上是在創建一個新字符串並對其進行秘密指向。它不能加快刪除實際機密數據的速度。

有好處嗎?假設我們以彙編語言或低杠桿語言編寫了數據,那麼我們可以確保覆蓋數據,並讓計算機進入睡眠狀態或在應用程序運行時將其保留,並且我們的RAM被一個邪惡的女僕刮掉了,邪惡的女僕在機密被覆蓋之後但在被刪除之前(可能是很小的空間)就獲得了我們的RAM數據,而RAM或硬盤驅動器上的其他任何內容都不會洩露此秘密...然後我看到了可能的增加

但是,成本與收益的關係似乎使得此安全​​性優化在我們的優化列表中非常低(並且通常在大多數應用程序中都低於“有價值”的水平)。

我可能會看到這種在特殊芯片中的使用受到限制,意味著可以在短時間內保存秘密,以確保它們在盡可能短的時間內保存下來,但是即使如此,我仍不確定成本會帶來什麼好處。

“可能是一個很小的空間”-提出一個很小的空間至少是該過程的剩餘生命週期的案例可能並不難。也就是說,可能存在由於各種原因(而且我不僅僅是說內存洩漏)而可能會長時間閒置的未使用內存。畢竟,邪惡的女僕會掃描我們所有的記憶,尋找任何可能有用的東西。因此,在不使用它時覆蓋它會關閉一個窗口。但是就像你說的那樣,我們仍然無法指望`mySecret = 0;帶來的任何安全利益。 mySecret = -1;`。
如果您擔心分配會被優化,只需將變量聲明為volatile(在C / C ++中)。這樣就可以了。如果該值為字符串,則覆蓋字符串的* content *而不是使用簡單的賦值。
許多與語言相關的技巧不會傳播。您使用的任何存儲機密數據的庫都需要逐行檢查以確保它們能夠執行此操作。如果沒有,您是否重新實現數學/密碼庫?每個資源花費的安全性增加比率是如此之低,並且(通過庫重寫)降低安全性的機會也很高,以至於我認為普通開發人員沒有合理的用例。
Cort Ammon
2014-12-06 22:45:52 UTC
view on stackexchange narkive permalink

您需要一個威脅模型

您甚至不應該開始考慮覆蓋安全變量,除非您有一個威脅模型來描述要阻止的黑客種類。 安全性總是要付出代價的。在這種情況下,代價就是教導開發人員維護所有這些額外代碼以保護數據的開發成本。 這筆費用意味著您的開發人員很可能會犯錯誤,並且這些錯誤更可能是洩漏的根源而不是內存問題。

  • 可以攻擊者訪問您的記憶?如果是這樣,您是否有理由認為他們不能/不會只是在改寫之前先嗅探值?攻擊者可以在什麼時間範圍內訪問您的內存
  • 攻擊者可以訪問核心轉儲嗎?您是否介意它們是否可以訪問敏感數據以換取足夠高的噪聲以首先引起核心轉儲?
  • 這是開放源還是封閉源?如果它是開源的,則您必須擔心多個編譯器,因為編譯器始終優化諸如覆蓋數據之類的東西。他們的工作不是提供安全性。 (對於一個真實的例子,Schneier的PasswordSafe具有專門的類來保護未加密的密碼數據。為此,他使用Windows API函數來鎖定內存,並強制其被正確覆蓋,而不是使用編譯器。為他做
  • 這是垃圾收集的語言嗎?您知道如何迫使您的特定垃圾收集器的特定版本實際刪除您的數據嗎?
  • 在您注意到並用其他方法切斷攻擊者之前,攻擊者可以進行幾次嘗試來獲取敏感數據(例如防火牆)?
  • 這是在虛擬機中運行嗎?您如何確定Hypervisor的安全性?
  • 攻擊者是否具有物理訪問權限?例如,Windows非常樂意使用閃存驅動器來緩存虛擬內存。攻擊者所需要做的就是說服Windows將其推入閃存驅動器。發生這種情況時,真的很難解決。實際上,是如此的困難,以至於沒有可靠的方法可以從閃存驅動器中可靠地清除數據。

在考慮覆蓋敏感數據之前,需要解決這些問題。 嘗試覆蓋數據而不解決線程模型是一種錯誤的安全感。

另一個代價是需要更多的代碼和更多的指令來執行計算機(速度較慢),但是是的,考慮威脅的好點。我認為黑客在程序運行時(甚至在此之後)從RAM訪問機密數據將是最大的威脅之一。我認為,如果您可以告訴編譯器(或使用您所使用的語言的任何構建/運行語言)在完成時將數據歸零,那將是很好的。
*“攻擊者可以訪問您的內存” *-好吧,我們知道攻擊者將使用該內存(如果有)。而且它不必在本地計算機上。例如,我們知道[NSA會將Windows錯誤報告記錄到其XKeyscore系統中,以幫助獲得未經授權的訪問](http://en.wikipedia.org/wiki/Tailored_Access_Operations)。無論您的威脅模型是否包括它,都會發生這種情況:)
它是否發生與您是否真的想對此做任何事情有很大的不同。用凱文·米特尼克(Kevin Mitnick)的話說:“唯一真正安全的計算機是斷開互聯網連接,拔下電源,存儲在地下水泥倉中並由武裝警衛控制的計算機,即使如此,我也會不時檢查一下。如果您確實想確保程序NSA的安全,那麼除了StackExchange以外,您還需要更多的幫助;-)
Gilles 'SO- stop being evil'
2014-12-05 00:02:43 UTC
view on stackexchange narkive permalink

是的,在安全性方面,優良作法是在不再需要數據時覆蓋特別敏感的數據,即作為對象析構函數(由語言提供的顯式析構函數或程序採取的操作)的一部分在取消分配對象之前)。甚至最好的做法是覆蓋本身不敏感的數據,例如將不再使用的數據結構中的指針字段清零,並且當指向的對像被釋放時將指針清零,即使您知道您將不再使用該字段。

這樣做的一個原因是,如果數據因外部因素(例如暴露的核心轉儲,被盜的休眠映像,受損害的服務器允許內存)洩漏而洩漏攻擊者提取RAM棒並利用數據保留能力的物理攻擊很少引起關注,除非在便攜式計算機上,也許在移動設備(例如電話)上(由於RAM被焊接,所以閾值較高),甚至在大多數情況下也只針對目標場景。改寫值的剩餘性不是問題:要在RAM芯片內部進行探測以檢測任何可能受改寫值影響的揮之不去的微小電壓差,將需要非常昂貴的硬件。如果您擔心對RAM的物理攻擊,那麼更大的問題就是確保將數據寫在RAM中,而不僅僅是在CPU緩存中。但這又通常是一個很小的問題。

覆蓋陳舊數據的最重要原因是為了防禦導致使用未初始化內存的程序錯誤,例如臭名昭​​著的 Heartbleed。這超出了敏感數據的範圍,因為風險不僅限於數據洩漏:如果存在某個軟件錯誤導致指針字段在未初始化的情況下被取消引用,則該錯誤不那麼容易被利用,並且如果

請注意,如果好的編譯器檢測到不再使用該值,則該字段將全部清零。

請注意,好的編譯器會優化清零。您可能需要使用一些特定於編譯器的技巧,以使編譯器相信該值仍在使用,從而生成清零代碼。

在許多具有自動管理功能的語言中,對象可以在不移動的情況下移動到內存中注意。這意味著很難控制陳舊數據的洩漏,除非內存管理器本身會擦除未使用的內存(出於性能目的通常不會這樣做)。從好的方面來說,外部洩漏通常是您所要擔心的,因為高級語言往往會阻止使用未初始化的內存(請注意具有可變字符串的語言中的字符串創建函數)。

順便說一句,我在上面寫了“歸零”。您可以使用除全零以外的位模式。全零的優點是在大多數環境中它都是無效的指針。您知道的位模式是無效的指針,但是更具特色的位模式可以幫助調試。

許多安全標準要求擦除敏感數據(例如密鑰)。例如,用於加密模塊的 FIPS 140-2標準甚至在最低保證級別上也要求它,除此之外,它僅要求功能合規且不需要抵抗攻擊。

通常,秘密不會直接作用。使用了一個加密庫(因為實現一種算法,即使是一種好的算法也很困難,而且實現錯誤會使您的安全性失效)。那麼,是否有人將自己限制為僅在刪除數據之前才更改數據的庫(可能會刪除最適合該工作的庫)?還是使用一種允許數據一直掛起直到刪除的庫,從而失去了將其應用於自己的代碼的好處?
@Lawtonfogle密碼庫通常以這樣的方式編寫:在使用後至少擦除密鑰。在加密實施者中,這被認為是良好的衛生習慣,而且我期望(儘管我沒有關於該主題的可靠數據)與合理的質量度量相關。即使該庫沒有執行此操作,您通常也可以從應用程序控制該庫的內存管理。
我不得不承認,編寫加密庫的安全專家似乎也將納入其他安全實踐,即使它們與加密本身並不直接相關。但是您希望確保它是開源的(如果僅出於驗證其加密完整性的其他原因)。
“如果您擔心對RAM的物理攻擊,那麼更大的擔憂將是確保將數據寫在RAM中,而不僅僅是在CPU緩存中。” -您可以強制執行內存屏障(最低級別作為CPU指令實現)以強制將CPU緩存刷新到RAM。如果編程語言是高級語言,一種實現方法是採用一些內存共享原語,例如互斥鎖(鎖定,擦除,解鎖),這些互斥量使用內存屏障作為實現的一部分。
user10211
2014-12-16 18:48:16 UTC
view on stackexchange narkive permalink

對於其餘答案的補充(希望很有趣),許多人低估了用C正確覆蓋內存的難度。我將在Colin Percival的博客文章“ 如何將a歸零”中大量引用。緩衝區”。

天真的嘗試覆蓋C語言中的內存所面臨的主要問題是編譯器優化。大多數現代編譯器都非常“聰明”,足以識別出用於覆蓋內存的通用對象實際上並不會改變程序的可觀察行為,因此可以對其進行優化。不幸的是,這完全破壞了我們想要實現的目標。上面鏈接的博客文章中介紹了一些常見的技巧。更糟糕的是,適用於一個版本的編譯器的技巧可能不一定與另一版本的編譯器甚至同一版本的其他版本一起使用。除非您僅分配二進製文件,否則這將是一個問題。

可靠地覆蓋C語言中我所知的唯一方法是 memset_s功能。可悲的是,這僅在C11中可用,因此為較早版本的C編寫的程序不走運。

memset_s函數將c的值(轉換為無符號字符)複製到每個s指向的對象的​​前n個字符。與memset不同,對memset_s的任何調用都必須嚴格按照抽像機的規則進行評估,如5.1.2.3中所述。也就是說,對memset_s的任何調用均應假定s和n所指示的內存將來可能可訪問,因此必須包含c所指示的值。

僅覆蓋內存是不夠的。他在標題為“ 歸零緩衝區不足”的後續博客文章中指出

稍加註意,使用協作編譯器,我們可以將緩衝區歸零-但這不是我們所需要的。我們需要做的是將可能存儲敏感數據的每個位置都設為零。請記住,我們之所以首先將敏感信息存儲在內存中,是因為我們可以使用它。並且這種用法幾乎肯定會導致敏感數據被複製到堆棧和寄存器中。

他繼續介紹了使用x86平台上的AESNI指令集洩漏數據的AES實現示例。

他聲稱,

不可能在C中安全地實現任何提供前向保密性的密碼系統。

A確實令人不安。

感謝您的見解!如果C和C ++編譯器包括一個超出範圍的內存清零選項(或者一種告訴編譯器要安全刪除哪些變量的方法),那肯定是很好的。
Ari Trachtenberg
2014-12-05 09:37:45 UTC
view on stackexchange narkive permalink

重要的是在需要後立即覆蓋,這很重要,因為,否則,

  • 數據會一直保留在堆棧上直到被覆蓋,並且可以通過
  • 數據容易受到內存刮擦。

實際上,如果您查看安全敏感型應用程序的源代碼(例如, openssh),您會發現它在使用後會仔細將敏感數據清零。

編譯器可能會嘗試優化覆蓋,即使沒有這樣做,也確實如此。重要的是要知道數據的物理存儲方式(例如,如果機密存儲在SSD上,則由於損耗平衡而覆蓋它可能不會擦除舊內容)。

Andy Dent
2014-12-07 11:49:37 UTC
view on stackexchange narkive permalink

原始示例顯示了一個堆棧變量,因為它是本機int類型。

覆蓋它是一個好主意,否則它會一直停留在堆棧上,直到被其他東西覆蓋為止。

我懷疑如果您是在C ++中通過指針和malloc分配了堆對像或C本機類型,那麼

  1. 使用 volatile
  2. 使用編譯指示符使用該變量將代碼括起來並禁用優化。
  3. 如果可能,僅將機密彙編成中間值而不是任何命名變量,因此它僅存在在計算過程中。
  4. ol>

    在JVM或C#下,我認為所有選擇都沒有。

*“如果可能的話,只將秘密組合在中間值中,而不是任何命名變量中”。*這沒有多大意義,因為大多數發行版編譯的二進製文件都不包含命名信息。如果我反彙編一個C ++編譯的應用程序,我將能夠說出它執行什麼指令,但是幾乎可以肯定,我不能說出任何變量名。
我可能是錯的,但是我在考慮中間值存儲在何處,而不是分配給變量的任何單個值。如果在代碼中放置一個常量並將其分配給變量,則該常量有時會鏈接到靜態塊中的值。中間值存在於寄存器中或內部在芯片高速緩存中。如果您要計算某種鍵作為中間值,然後將該結果與服務中的值進行比較,則整個過程將更加短暫。
ratchet freak
2014-12-05 18:00:31 UTC
view on stackexchange narkive permalink

有可能在您刪除並刪除內存之前,已將頁面中的內存分頁,這取決於頁面文件中的使用情況分佈如何播放數據,從而可能永遠存在於其中。

因此,要成功從計算機中刪除機密,必須首先通過固定頁面來確保數據永遠不會到達持久性內存。然後確保編譯器沒有優化對要刪除的內存的寫操作。

PaulOverflow
2014-12-17 13:20:34 UTC
view on stackexchange narkive permalink

實際上答案是肯定的,您可能應該在刪除它之前將其覆蓋。如果您是網絡應用程序開發人員,那麼您可能應該在使用 delete free 函數之前覆蓋所有數據。

此錯誤的示例被利用:

用戶可以在輸入字段中插入一些惡意數據。您繼續處理數據,然後調用已分配內存的 free 函數而不會覆蓋它,因此數據將保留在內存中。然後,用戶使您的Web應用程序崩潰(例如,通過上傳非常大的圖片或類似的東西),UNIX系統將把核心內存轉儲到 core core中。 <pid> 文件。然後,如果用戶可以蠻破解 <pid> (這不會花費太長時間),並且核心轉儲文件將被解釋為Web Shell,因為它包含用戶的惡意數據。

mincewind
2014-12-17 13:52:09 UTC
view on stackexchange narkive permalink

int secret = 13123在本地範圍內不是一個常量嗎?

您不應太在意所寫的東西,而該書幾乎值得一讀。實際上,在一些相反的建議下,我建議您故意讓它保持可讀性。而且,用時間相關的字符串隨機填充它,這些字符串不是以標準方式調用的。

這樣,如果您查看黑暗的網站以發現自己是否受到攻擊,則可以準確地知道它是如何完成的。由於數據庫轉儲與刮板轉儲是分開的。



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