簡體   English   中英

是否有必要清理堆棧內容?

[英]Is it necessary to clean up stack contents?

我們正在獲得PCI PA-DSS認證,其中一項要求是避免將干凈的PAN(卡號)寫入磁盤。 應用程序不會將此類信息寫入磁盤,但如果操作系統(在本例中為Windows)需要交換,則將內存內容寫入頁面文件。 因此,應用程序必須清理內存以防止RAM捕獲器服務讀取敏感數據。

有三種情況需要處理:

  • 堆分配(malloc):在釋放內存之前,可以使用memset清理該區域
  • 靜態或全局數據:使用后,可以使用memset清理該區域
  • 本地數據(函數成員):數據放在堆棧上,在函數完成后無法訪問

例如:

void test()
{
  char card_number[17];

  strcpy(card_number, "4000000000000000");
}

在執行測試之后,存儲器仍然包含card_number信息。

一條指令可以在測試結束時將變量card_number歸零,但這應該適用於程序中的所有函數。

memset(card_number, 0, sizeof(card_number));

有沒有辦法在某個時刻清理堆棧,就像程序結束前一樣?

程序完成后立即清理堆棧可能為時已晚,它可能已經在運行時的任何時候被換出。 您應該僅將您的已發送數據保存在使用VirtualLock鎖定的內存中,這樣它就不會被換出。 這必須讀取所述敏感數據之前發生。

你可以鎖定這么多的內存有一個小的限制,所以你可以沒有鎖定整個堆棧,應該避免將敏感數據存儲在堆棧上。

我假設你想擺脫下面這種情況:

#include <iostream>

using namespace std;

void test()
{
    char card_number[17];
    strcpy(card_number, "1234567890123456");
    cout << "test() -> " << card_number << endl;
}

void test_trash()
{
    // don't initialize, so get the trash from previous call to test()
    char card_number[17];
    cout << "trash from previous function -> " << card_number << endl;
}

int main(int argc, const char * argv[])
{
    test();
    test_trash();
    return 0;
}

輸出:

test() -> 1234567890123456
trash from previous function -> 1234567890123456

你可以做這樣的事情:

#include <iostream>

using namespace std;

class CardNumber
{
    char card_number[17];

public:
    CardNumber(const char * value)
    {
        strncpy(card_number, value, sizeof(card_number));
    }

    virtual ~CardNumber()
    {
        // as suggested by @piedar, memset_s(), so the compiler
        // doesn't optimize it away.
        memset_s(card_number, sizeof(card_number), 0, sizeof(card_number));
    }

    const char * operator()()
    {
        return card_number;
    }
};

void test()
{
    CardNumber cardNumber("1234567890123456");
    cout << "test() -> " << cardNumber() << endl;
}

void test_trash()
{
    // don't initialize, so get the trash from previous call to test()
    char card_number[17];
    cout << "trash from previous function -> " << card_number << endl;
}

int main(int argc, const char * argv[])
{
    test();
    test_trash();
    return 0;
}

輸出:

test() -> 1234567890123456
trash from previous function ->

您可以執行類似清理堆上的內存或靜態變量的操作。 顯然,我們假設卡號來自動態源而不是硬編碼的東西......

和是:明確回答你問題的標題:堆棧不會自動清理......你必須自己清理它。

我認為這是必要的,但這只是問題的一半。

這里有兩個問題:

  1. 原則上,在您仍在使用數據時,沒有什么可以阻止操作系統交換數據。 正如在另一個答案中指出的那樣,你想在Linux上使用VirtualLock並在linux上使用mlock

  2. 您需要阻止優化器優化memset 這也適用於全局和動態分配的內存。 我強烈建議你看看cryptopp SecureWipeBuffer

通常,您應該避免手動執行,因為這是一個容易出錯的過程。 相反,請考慮使用自定義分配器或自定義類模板來獲取可在析構函數中釋放的安全數據。

通過移動堆棧指針來清理堆棧,而不是通過實際彈出堆棧指針來清除堆棧。 唯一的機制是將返回彈出到適當的寄存器中。 您必須手動完成所有操作。 另外 - volatile可以幫助您避免基於每個變量的優化。 你可以手動彈出堆棧干凈,但是 - 你需要匯編程序來做 - 並且開始操作堆棧並不是那么簡單 - 它實際上不是你的資源 - 編譯器擁有它就你而言。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM