在上一篇文章當中,介紹到了通過Silverlight獲取web.config中的值,最後提到了加密的問題,因此首先對該安全問題做一個簡單的描述。
問題描述
1. 下方是我的web.config文件,當中配置這一個媒體文件服務器的IP地址
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<connectionStrings>
<add name="VideoFiles_ConnectionString" connectionString="127.0.0.1"/>
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
</configuration>
2. 當在Silverlight程序中獲取到該config中的值時,我們可以發現,通過查看頁面的Source Code,是可以看到這個IP地址的,它是以明文的方式顯示出來的,這樣會引發安全性問題,我相信沒有人願意將自己的服務器IP地址暴露在外面
解決方法
在Page頁面獲取到web.config後,對該文件當中的內容進行加密,從而有效地解決了明文顯示的問題,這樣,別人就不能通過查看源代碼的方式從前臺獲取你的服務器IP地址或者其他機密信息了,其實現方法如下:
1. 在Page頁面後臺的.cs文件中,加入機密算法,使用Rfc2898DeriveBytes 獲取密碼、salt 值和迭代次數,然後通過調用 GetBytes 方法生成密鑰:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Specialized;
using System.Text;
using System.Configuration;
using System.Security.Cryptography;
using System.IO;
namespace GetWebConfig.Web
{
public partial class HostPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//Clear the Cache
Response.Cache.SetCacheability(HttpCacheability.NoCache);
WriteInitParams();
}
private void WriteInitParams()
{
string strConn = ConfigurationManager.ConnectionStrings["VideoFiles_ConnectionString"].ToString();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("<param name=\"InitParams\" value=\"");
stringBuilder.Append(Encrypt(strConn));
stringBuilder.Append("\"/>");
this.litInitParams.Text = stringBuilder.ToString();
}
/**/
/// <summary>
/// 加密數據
/// </summary>
/// <param name="input">加密前的字符串</param>
/// <returns>加密後的字符串</returns>
public static string Encrypt(string input)
{
// salt值
string saltValue = "saltValue";
// 密碼值
string pwdValue = "xuyue";
byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(input);
byte[] salt = System.Text.UTF8Encoding.UTF8.GetBytes(saltValue);
// AesManaged - 高級加密標準(AES) 對稱算法的管理類
System.Security.Cryptography.AesManaged aes = new System.Security.Cryptography.AesManaged();
// Rfc2898DeriveBytes - 通過使用基於 HMACSHA1 的僞隨機數生成器,實現基於密碼的密鑰派生功能 (PBKDF2 - 一種基於密碼的密鑰派生函數)
// 通過 密碼 和 salt 派生密鑰
System.Security.Cryptography.Rfc2898DeriveBytes rfc = new System.Security.Cryptography.Rfc2898DeriveBytes(pwdValue, salt);
/**/
/*
* AesManaged.BlockSize - 加密操作的塊大小(單位:bit)
* AesManaged.LegalBlockSizes - 對稱算法支持的塊大小(單位:bit)
* AesManaged.KeySize - 對稱算法的密鑰大小(單位:bit)
* AesManaged.LegalKeySizes - 對稱算法支持的密鑰大小(單位:bit)
* AesManaged.Key - 對稱算法的密鑰
* AesManaged.IV - 對稱算法的密鑰大小
* Rfc2898DeriveBytes.GetBytes(int 需要生成的僞隨機密鑰字節數) - 生成密鑰
*/
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
aes.Key = rfc.GetBytes(aes.KeySize / 8);
aes.IV = rfc.GetBytes(aes.BlockSize / 8);
// 用當前的 Key 屬性和初始化向量 IV 創建對稱加密器對象
System.Security.Cryptography.ICryptoTransform encryptTransform = aes.CreateEncryptor();
// 加密後的輸出流
System.IO.MemoryStream encryptStream = new System.IO.MemoryStream();
// 將加密後的目標流(encryptStream)與加密轉換(encryptTransform)相連接
System.Security.Cryptography.CryptoStream encryptor = new System.Security.Cryptography.CryptoStream
(encryptStream, encryptTransform, System.Security.Cryptography.CryptoStreamMode.Write);
// 將一個字節序列寫入當前 CryptoStream (完成加密的過程)
encryptor.Write(data, 0, data.Length);
encryptor.Close();
// 將加密後所得到的流轉換成字節數組,再用Base64編碼將其轉換爲字符串
string encryptedString = Convert.ToBase64String(encryptStream.ToArray());
return encryptedString;
}
}
}
2. 之後,在Silverlight頁面獲取config數據的時候,通過解密算法進行解密,得到真正的IP地址信息:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Security.Cryptography;
using System.IO;
using System.Text;
namespace GetWebConfig
{
public partial class MainPage : UserControl
{
private IDictionary<string, string> _config;
public MainPage()
{
InitializeComponent();
_config = (Application.Current as App).Configurations;
if (_config.Count > 0)
lblWebconfig.Content = "Web.config connectionString: " + Decrypt(_config.ElementAt(0).Key.ToString() + "==");
}
/// <summary>
/// 解密數據
/// </summary>
/// <param name="input">加密後的字符串</param>
/// <returns>加密前的字符串</returns>
public string Decrypt(string input)
{
// salt值(與加密時設置的值一致)
string saltValue = "saltValue";
// 密碼值(與加密時設置的值一致)
string pwdValue = "xuyue";
byte[] encryptBytes = Convert.FromBase64String(input);
byte[] salt = Encoding.UTF8.GetBytes(saltValue);
System.Security.Cryptography.AesManaged aes = new System.Security.Cryptography.AesManaged();
System.Security.Cryptography.Rfc2898DeriveBytes rfc = new System.Security.Cryptography.Rfc2898DeriveBytes(pwdValue, salt);
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
aes.Key = rfc.GetBytes(aes.KeySize / 8);
aes.IV = rfc.GetBytes(aes.BlockSize / 8);
// 用當前的 Key 屬性和初始化向量 IV 創建對稱解密器對象
System.Security.Cryptography.ICryptoTransform decryptTransform = aes.CreateDecryptor();
// 解密後的輸出流
MemoryStream decryptStream = new MemoryStream();
// 將解密後的目標流(decryptStream)與解密轉換(decryptTransform)相連接
System.Security.Cryptography.CryptoStream decryptor = new System.Security.Cryptography.CryptoStream(
decryptStream, decryptTransform, System.Security.Cryptography.CryptoStreamMode.Write);
// 將一個字節序列寫入當前 CryptoStream (完成解密的過程)
decryptor.Write(encryptBytes, 0, encryptBytes.Length);
decryptor.Close();
// 將解密後所得到的流轉換爲字符串
byte[] decryptBytes = decryptStream.ToArray();
string decryptedString = UTF8Encoding.UTF8.GetString(decryptBytes, 0, decryptBytes.Length);
return decryptedString;
}
}
}
最終實現效果如下,這時候我們通過查看源文件看到的則是被加密後的信息,而不是127.0.0.1,只有在Silverlight進行解密之後,才能看到我們想要的數據。