Jak ważne i poufne informacje znajdują się w pliku konfiguracyjnym aplikacji (app.config lub web.config), każdy chyba wie. O szyfrowaniu tychże informacji z poziomu konsoli wspominałem tutaj. W tym artykule zaprezentować chciałbym jak szyfrować/deszyfrować sekcje z poziomu kodu. Zapraszam.
W pierwszej kolejności wydzielimy sekcję do oddzielnego pliku (ja działam na sekcji connectionStrings). Jest to praktyka, do której zachęca Microsoft, ponieważ edycja “zewnętrznych” plików nie powoduje restartu aplikacji. I tak też sekcja, która wyglądała następująco:
<connectionStrings> <clear /> <add name="myConnection" connectionString="Valid connection string" /> </connectionStrings>
wygląda tak:
<connectionStrings configSource="connectionStrings.config" />
a plik connectionStrings.config tak:
<?xml version="1.0"?> <connectionStrings> <clear /> <add name="myConnection" connectionString="Valid connection string" /> </connectionStrings>
Następnie definiujemy własnego providera, który odpowiedzialny będzie za szyfrowanie sekcji. Podajemy w nim dwa istotne atrybuty: keyContainerName oraz useMachineContainer. Pierwszy z nich wyznacza nam nazwę klucza RSA. Drugi natomiast decyduje czy klucz znajduje się w kontenerze na poziomie maszyny, czy też na poziome użytkownika (wyjaśnienie różnicy pomiędzy poziomami można znaleźć tutaj). Nasz provider prezentuje się jak poniżej.
<configProtectedData> <providers> <add name="MyProvider" type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" keyContainerName="MySampleKey" useMachineContainer="true"/> </providers> </configProtectedData>
Jak widać w powyższym przykładzie, wykorzystujemy klucz RSA, który zapewne nie znajduję się na naszej maszynie, a co za tym idzie należy go utworzyć. Służy do tego poniższa komenda uruchamiana z wiersza poleceń Visual Studio:
aspnet_regiis -pc "MySampleKey" -exp
Od tego momentu nasza maszyna zawiera klucz o zadanej nazwie. W tym miejscu warto wspomnieć, iż po zaszyfrowaniu informacji odkodować je będzie można tylko i wyłącznie na maszynie posiadającej podany klucz. Jeśli maszyna nie będzie zawierać klucza otrzymamy poniższy błąd:
Do eksportu i importu klucza służą następujące komendy (więcej szczegółów tutaj):
aspnet_regiis -px "MySampleKey" keys.xml -pri
aspnet_regiis -pi "MySampleKey" keys.xml
Na koniec pozostaje nam tylko obsługa od strony kodu. Do testu przygotowałem prostą stronkę z trzema przyciskami oraz kontrolką typu Literal. Poniżej kod akcji tychże przycisków:
protected void bDecrypt_Click(object sender, EventArgs e) { var config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~"); var section = config.GetSection("connectionStrings") as System.Configuration.ConnectionStringsSection; if (section.SectionInformation.IsProtected) { section.SectionInformation.UnprotectSection(); } config.Save(System.Configuration.ConfigurationSaveMode.Minimal); } protected void bEncrypt_Click(object sender, EventArgs e) { var config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~"); var section = config.GetSection("connectionStrings") as System.Configuration.ConnectionStringsSection; if (!section.SectionInformation.IsProtected) { section.SectionInformation.ProtectSection("MyProvider"); } config.Save(System.Configuration.ConfigurationSaveMode.Minimal); } protected void bLoad_Click(object sender, EventArgs e) { litConnections.Text = string.Empty; foreach (ConnectionStringSettings item in ConfigurationManager.ConnectionStrings) { litConnections.Text += string.Format("{0}: {1} ", item.Name, item.ConnectionString); } }
Scenariusz moich testów wygląda następująco. Klikam w przycisk pobrania i wypisania na stronie zawartości sekcji connectionStrings. Wpisy pokazują się poprawnie. Następnie kolejno w przycisk szyfrujący i znowu pobierający informacje. Ponownie zawartość wyświetla się poprawnie. Natomiast w momencie, gdy podglądniemy zawartość pliku connectionStrings.config wygląda ona następująco:
<connectionStrings configProtectionProvider="MyProvider"> <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <KeyName>Rsa Key</KeyName> </KeyInfo> <CipherData> <CipherValue>X2wpUxf/x3TD+EAHO43Zp3I+bb+Bzhnzg47F91VMhksTPy7KFY38MZ06/Cf6LA4cw+TvaF9se5W7NUxtU66pJuZVxzlqBmy/NIQZ0PtQdNvPw5Je5Z3GpsbLvTRjaBhyHzXSH62O8BHVUoJ/220gtZNTIG6xEc1s4gzRp19L+pg=</CipherValue> </CipherData> </EncryptedKey> </KeyInfo> <CipherData> <CipherValue>FfkYyyj7F+ZKaxVfffKoF6TZPkhckmt8c5adQmUTjo30GpgUpa0dQqU07GTkBOp508E6uo0Lp4QjABOnoz/1ldyYx2KZhh9hDaTBrIEBTcxFoEghkd5b6Pd59ndvwyLLZlvbrQhcY+ew6PS4zJPeAVKzEE5iO/r7kEOP/QSVHF+FeovtGxxQAs73cYAQfcS+</CipherValue> </CipherData> </EncryptedData> </connectionStrings>
Jak widać, po zaszyfrowaniu aplikacja sama “w locie” odszyfrowała informacje oraz wyświetliła prawdziwą zawartość sekcji. Tym samym nie musimy się martwić o obsługę odkodowywania informacji podczas próby ich wykorzystania.
Kliknięcie w przycisk odszyfrowania skutkuje powrotem seksji do swojej pierwotnej postaci.
Szyfrowanie newralgicznych informacji aplikacji przed niepowołanym dostępem nie jest trudne ani pracochłonne. Dlatego też chciałbym wszystkich zachęcić do stosowania któregokolwiek z rozwiązań, gdyż mają one na celu zwiększenie bezpieczeństwa naszych jakże wysoce poufnych informacji.
Właściwie wszystko działa jak należy. Jednak w pliku exe (dla aplikacji Windows Form) jest tak czy inaczej jest wkompilowany niezaszyfrowany string.
Exe’ca nawet nie trzeba dekompilować itp. wystarcza podgląd z notatnika.
Rydzu,
Szczerze powiedziawszy, to nigdy nie próbowałem tego z aplikacjami desktopowymi ;)