Pytanie jak bezpiecznie usunąć std :: string?


W jaki sposób można przechowywać poufne dane (np. Hasła) w std::string?

Mam aplikację, która monituje użytkownika o hasło i przekazuje je do serwera niższego poziomu podczas konfiguracji połączenia. Chcę bezpiecznie usunąć wartość hasła po nawiązaniu połączenia.

Jeśli przechowuję hasło jako char * tablica, mogę używać API takich jak SecureZeroMemory pozbyć się wrażliwych danych z pamięci procesu. Jednak chcę uniknąć tablic znaków w moim kodzie i szukam czegoś podobnego std::string?


16
2018-04-18 02:37


pochodzenie


Według ten link, std :: string nie są zaprojektowane dla celów bezpieczeństwa. - Marlon
dziękuję Marlonowi, to oznacza, że ​​nie mam innego wyboru, jak zaśmiecać interfejsy moich metod char *buf, size_t len :) - ajd.
@ user34965: To nie jest binarne. Powinieneś zaprojektować class SecureString. Dobrym pomysłem jest skopiowanie interfejsu std::string więc jest to zamiennik. - MSalters


Odpowiedzi:


Na podstawie podanej odpowiedzi tutaj, Napisałem alias do bezpiecznego zerowania pamięci.

#include <string>
#include <windows.h>

namespace secure
{
  template <class T> class allocator : public std::allocator<T>
  {
  public:

    template<class U> struct rebind { typedef allocator<U> other; };
    allocator() throw() {}
    allocator(const allocator &) throw() {}
    template <class U> allocator(const allocator<U>&) throw() {}

    void deallocate(pointer p, size_type num)
    {
      SecureZeroMemory((void *)p, num);
      std::allocator<T>::deallocate(p, num);
    }
  };

  typedef std::basic_string<char, std::char_traits<char>, allocator<char> > string;
}

int main()
{
  {
    secure::string bar("bar");
    secure::string longbar("baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar");
  }
}

Okazuje się jednak, w zależności od sposobu std::string jest zaimplementowane, możliwe jest, że alokator nie jest nawet wywoływany dla małych wartości. W moim kodzie, na przykład deallocate nie otrzymuje nawet wezwania do napisu bar (w Visual Studio).

Odpowiedź brzmi, że nie możemy użyć std :: string do przechowywania poufnych danych. Oczywiście, mamy opcję napisania nowej klasy, która obsługuje przypadek użycia, ale byłem szczególnie zainteresowany użyciem std::string jak zdefiniowano.

Dziękuję wszystkim za pomoc!


11
2018-04-20 19:43



Cóż, jeśli ciąg znaków jest zbyt duży dla SSO, jest dobry. - Deduplicator


std :: string opiera się na char *. Gdzieś za wszystkimi dynamicznymi magiami jak char *. Więc kiedy mówisz, że nie chcesz używać znaku * na kodzie, nadal używasz znaku *, jest on po prostu w tle z całą masą innych śmieci ułożonych na nim.

Nie mam zbyt dużego doświadczenia z pamięcią procesową, ale zawsze możesz przejrzeć każdą postać (po zaszyfrowaniu i zapisaniu hasła w bazie danych) i ustawić ją na inną wartość.

Jest też std :: basic_string, ale nie jestem pewien, jaka pomoc mogłaby ci pomóc.


1
2018-04-18 03:03



ręczne nadpisanie każdego znaku nie zostanie wykonane - ponieważ kompilator może zoptymalizować taki kod, jeśli ciąg ma zostać zniszczony. zobacz pytanie Marlon dołączył do powyższego komentarza. - ajd.
następnie nadpisaj każdy znak, a następnie użyj ciągu;) - Andrew Rasmussen
Główny problem z nadpisaniem czegokolwiek std::string jest to, że nic na świecie nie gwarantuje, że w rzeczywistości przerobi pamięć, w której znajdował się napis, lub że przepisuje całą pamięć, w której był ciąg, ponieważ std::string może przesuwać bazowy bufor wokół. - Jan Hudec
Istnieje kilka instrukcji montażu, które mogą zostać dołączone, aby poinformować system i kompilator, że wszystko może odczytać dane z innego wątku. To uniemożliwiłoby optymalizację, ale byłoby również zależne od platformy. - Sqeaky


Dla potomności zdecydowałem kiedyś zignorować tę radę i mimo to użyć std :: string i napisałem metodę zero () używając c_str () (i odrzucając constness) i volatile. Gdybym był ostrożny i nie powodował ponownego przydzielenia / przeniesienia zawartości, a ja ręcznie wywołałem zero (), gdzie potrzebowałem go w czystości, wszystko wydawało się działać poprawnie. Niestety, odkryłem inną poważną wadę w sposób trudny: std :: string może być również obiektem zliczanym z odwołaniem ... zestrzelenie pamięci w c_str () (lub pamięci, do której odwołuje się obiekt, do którego odwołuje się), nieświadomie wysadzi w powietrze drugi obiekt .


1
2017-10-03 12:22



Implementacja licznika jest niezgodna z C ++ 11. - Baum mit Augen


std::string mystring;
...
std::fill(mystring.begin(), mystring.end(), 0);

lub jeszcze lepiej napisz własną funkcję:

void clear(std::string &v)
{
  std::fill(v.begin(), v.end(), 0);
}

0
2018-04-18 08:43



Nie zadziała. Nie ma gwarancji, że nadpisałeś całą pamięć, w której był napis, ponieważ mogła zostać przeniesiona podczas jakiejś operacji. - Jan Hudec
Odpowiednio inteligentny optymalizator może również wykryć, że nigdy więcej nie użyjesz zer i pominąć wypełnienie. - Bo Persson