昨天建立了AP到DB間的安全傳輸通道,算是建立了一條安全回家的航路,今天我們開始把視角移動到AP Server上的應用程式(Application)。
在軟體開發生命週期(SDLC)中,工程師常感受到軟體品質的重要性順序:
通常安全性是最晚被提起的,在大部分急急如律令的專案裡,融入資訊安全在軟體開發生命週期(S
SDLC,Security
Software
Development Lifecycle)裡,是一種挑戰,但身為工程師的我們也理解越後期進行程式修補,開發成本越高,越早從需求、設計面就準備好安全性需求,就能減少專案後期投入更多的人力與資源。
安全軟體開發的方法論可以從微軟的SDL
(Security Deployment Liftcycle)教學文件或是OWASP的CLASP
念起,哈!這個題目太大,見面會的工程師就從小小世界作起。
MS SDL:
圖片來源:https://www.microsoft.com/en-us/SDL/process/training.aspx
OWASP的CLASP:
圖片來源:https://www.owasp.org/index.php/CLASP_Concepts
好!今天先來保護連接字串。
因為史丹利平時是一個.NET工程師,所以需要寫碼的部分會以C#作範例。
連接字串,相信寫程式的攻城獅都不陌生,我們可以用許多種方式變出來:
也許我們都注意到一件很可疑的事情,但總是把她自動跳過,該是時候面對了:
連接字串中好像有著連結資料庫的帳號及密碼。(驚!)
連接字串資訊:
DB帳號
+DB密碼
為了確保密碼的機密性(Confidentiality),防範密碼資料不洩露也是工程師的重要工作,我們來看幾個.NET C#的程式範例:
也許這樣的程式碼我們可能碰過面:
public void TestSqlConnectionString()
{
//簡單連接資料庫查詢SQL Server版本資料
using (SqlConnection conn = new SqlConnection("Data Source=資料庫IP;Initial Catalog=SecurityDB;User ID=資料庫帳號;Password=資料庫密碼;Application Name=testAP"))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("select @@version", conn))
{
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
DataTable table = new DataTable();
adapter.Fill(table);
Console.WriteLine($"{table.Rows[0][0]}");
}
}
}
程式裡面寫密碼,犯了安全軟體開發的天條,馬上被活逮。
另外一般專案的程式碼編譯後通常不會使用混淆器(obfuscator)混淆程式碼(Obfuscated code),有心人士透過反組譯(Decomplier)後可能讓DB密碼外流。
也許這樣的組態和程式碼我們都可能碰過面,而且還蠻熟的!
組態檔案:
app.config或是web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="Ironman" connectionString="Data Source=資料庫ip;Initial Catalog=SecurityDB;User ID=帳號;Password=密碼;Application Name=testAP;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup>
</configuration>
C#程式
public void TestSqlConnectionStringFromConfig()
{
//簡單連線資料庫查詢SQL Server版本資料
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ironman"].ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("select @@version", conn))
{
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
DataTable table = new DataTable();
adapter.Fill(table);
Console.WriteLine($"{table.Rows[0][0]}");
}
}
}
config裡面寫密碼,又犯了源碼檢測的Rules:CWE-260
: Password in Configuration File
在組態設定檔案中儲存純文字密碼可能會危及系統安全。
呼!好,於是有了一種把密碼去模糊化或是加密的做法。
舉例像是將連結字串中的密碼加密
1.先準備一支AES256
加密程式
public string Encryption(string PlainText)
{
using (Aes aesAlg = Aes.Create())
{
//加密金鑰(32 Byte)
aesAlg.Key = Encoding.Unicode.GetBytes("我是金鑰我是機密別和人說我是金鑰");
//初始向量(Initial Vector, iv) 類似雜湊演算法中的加密鹽(16 Byte)
aesAlg.IV = Encoding.Unicode.GetBytes("台塩高級精鹽加碘");
//加密器
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
//執行加密
byte[] encrypted = encryptor.TransformFinalBlock(Encoding.Unicode.GetBytes(PlainText), 0,
Encoding.Unicode.GetBytes(PlainText).Length);
return Convert.ToBase64String(encrypted);
}
}
2.輸入DB連線的密碼
string PlainText = "我是密碼";
Console.WriteLine($"明文:{PlainText}");
加密後的密碼是4CGcm2J1K9Ur9TyLj4r1dw==
3.貼到組態檔案(app.config或是web.config)中
4.撰寫AES256
解密程式
public string Decryption(string CipherText)
{
using (Aes aesAlg = Aes.Create())
{
//加密金鑰(32 Byte)
aesAlg.Key = Encoding.Unicode.GetBytes("我是金鑰我是機密別和人說我是金鑰");
//初始向量(Initial Vector, iv) 類似雜湊演算法中的加密鹽(16 Byte)
aesAlg.IV = Encoding.Unicode.GetBytes("台塩高級精鹽加碘");
//加密器
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
//執行加密
byte[] decrypted = decryptor.TransformFinalBlock(Convert.FromBase64String(CipherText), 0, Convert.FromBase64String(CipherText).Length);
return Encoding.Unicode.GetString(decrypted);
}
}
5.執行讀取DB程式
資料庫其他連接資訊還是透過組態檔取得,但密碼呼叫步驟4的Decryption()方法解密。
public void TestSqlConnectionStringFromConfigAndPasswordEncrpted()
{
//連接字串產生器
SqlConnectionStringBuilder sqlsb = new SqlConnectionStringBuilder(ConfigurationManager.ConnectionStrings["ironman"].ConnectionString);
//資料庫使用者密碼解密
sqlsb.Password = Decryption(sqlsb.Password);
//簡單連線資料庫查詢SQL Server版本資料
using (SqlConnection conn = new SqlConnection(sqlsb.ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("select @@version", conn))
{
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
DataTable table = new DataTable();
adapter.Fill(table);
Console.WriteLine($"{table.Rows[0][0]}");
}
}
}
安全性雖然不高,但好像是暫時可以接受的方法。
加解密的金鑰、金鑰初始向量及加密演算法的選擇都放在程式中,這也是一種潛在風險,而且還是通過不了源碼檢測的盤查。
有一種HSM(Hardware Security Module)
硬體安全模組解決方案可以針對我們AP的保管金鑰弱點強化,可以將我們使用到的各種金鑰與密碼演算法都保護在安全性硬體設備上,AP使用時只要呼叫HSM廠商寫好的API,實際加解密的運算過程和金鑰都保護在一台小機器上,資料庫連線密碼的機密性也可以受到保護。
不過效能和安全性選擇也許是我們要權衡的課題。
哈!今晚要加班幫同事寫程式,明天續!
連接字串大平台The Connection Strings Reference
西班牙到英屬直布羅陀海關
2014-12攝於Gibraltar
國家格言:Montis Insignia Calpe
Nulli expugnabilis hosti
「直布羅陀巨巖的徽章」(拉丁文)
「沒有敵人能攻陷這裡」(拉丁文)