Coding4Fun - 從比對原始資料改為比對 SHA256 會慢多少?
0 |
開會討論到一個系統需求,要從大量資料標註出特定類別,例如:呼叫端提交一百萬筆帳號,系統由資料庫查詢屬於該類別的帳號清單(假設有五萬筆),API 服務要在標記出這五萬筆並傳回結果。
由於帳號具敏感性,為進一步降低資訊外洩風險,既然只要比對相符,我提議傳入帳號雜湊值取代帳號本身,感覺一定比傳明碼安全。不過,馬上被問了一句:「這樣速度會變慢很多嗎?」
依前陣子玩 SHA 暴力破解的經驗,SHA256 甚至 SHA512 的運算速度遠比想像快,執行速度上不用擔心。但沒有數據佐證難以服人,提議之人有舉證之責,就來做個實驗吧。
我設計一個實驗如下,用迴圈產生一百萬筆來源資料,帳號文字用 string GenSimuValue(int i) => $"{i:X4}{i:000000}";
模擬,第 9999 筆為 270F009999,再用 270F 當資料序號,其檔案範例如下:
若轉為 SHA256,會像這樣:
第一個效能問題是資料量變大,傳輸速度變慢,一百萬筆明文版為 18,870,272 Bytes、SHA256 版為 51,937,280 Bytes,增加 33MB 左右,若未壓縮傳輸,以內網 100Mbps 傳送將增加 2.5 秒。若經過壓縮,差距縮小為 19MB (模擬資料用流水號,明文碼壓縮比較高,實際資料的差異會更小一點),增加的傳輸資料時間抓 1.6 秒。(樂觀值)
接下來我用一段程式測試一百萬筆明文版跟 SHA256 版雜湊版的資料產生與比對效能:(測速部分用之前發明的極簡風格 .NET Stopwatch 計時法)
using System.Security.Cryptography;
using System.Text;
const int DATA_COUNT = 1_000_000;
const int TARGET_COUNT = 50_000;
var sha = SHA256.Create();
//https://blog.darkthread.net/blog/handy-stopwatch-library/
using (var ts = new TimeMeasureScope("GenRawData")) {
GenRaw("data", DATA_COUNT);
}
using (var ts = new TimeMeasureScope("GenRawTarget")) {
GenRaw("target", TARGET_COUNT);
}
using (var ts = new TimeMeasureScope("FindRaw"))
{
var data = File.ReadAllLines(Path.Combine("data", "raw.txt"))
.Select(x => x.Split(','))
.Select(o => new { SeqNo = o[0], Data = o[1]});
var target = File.ReadAllLines(Path.Combine("target", "raw.txt"))
.Select(x => x.Split(',').Last())
.ToHashSet<string>();
using var sw = new StreamWriter(Path.Combine("data", "result-raw.txt"));
foreach (var item in data)
{
sw.WriteLine($"{item.SeqNo},{(target.Contains(item.Data) ? "Y" : "N")}");
}
}
using (var ts = new TimeMeasureScope("GenHashData")) {
GenHash("data", DATA_COUNT);
}
using (var ts = new TimeMeasureScope("FindHash")) {
var data = File.ReadAllLines(Path.Combine("data", "hash.txt"))
.Select(x => x.Split(','))
.Select(o => new { SeqNo = o[0], Hash = o[1]});
var target = File.ReadAllLines(Path.Combine("target", "raw.txt"))
.Select(x => x.Split(',').Last())
.Select(x => GenShaHash(x))
.ToHashSet<string>();
using var sw = new StreamWriter(Path.Combine("data", "result-hash.txt"));
foreach (var item in data)
{
sw.WriteLine($"{item.SeqNo},{(target.Contains(item.Hash) ? "Y" : "N")}");
}
}
//比較結果
var hashResult = File.ReadAllLines(Path.Combine("data", "result-hash.txt"));
var rawResult = File.ReadAllLines(Path.Combine("data", "result-raw.txt"));
if (hashResult.Length == rawResult.Length)
{
var diff = hashResult.Zip(rawResult,
(h, r) => h == r ? null : $"{h} != {r}")
.Where(x => x != null).ToArray();
if (diff.Any())
{
Console.WriteLine("資料不一致");
foreach (var item in diff) Console.WriteLine(item);
}
else
Console.WriteLine("資料完全相同");
}
else
Console.WriteLine("資料筆數不同");
//TODO: 若要提高安全強度,每次執行雙方可約定隨機產生的 Salt 串接明文字串,讓相同值每次產生的雜湊結果不同
string GenShaHash(string s) =>
Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(s)));
string GenSimuValue(int i) => $"{i:X4}{i:000000}";
void GenHash(string folder, int count)
{
using var sw = new StreamWriter(Path.Combine(folder, "hash.txt"));
foreach (int i in Enumerable.Range(0, count))
sw.WriteLine($"{i:X4},{GenShaHash(GenSimuValue(i))}");
}
void GenRaw(string folder, int count)
{
using var sw = new StreamWriter(Path.Combine(folder, "raw.txt"));
foreach (int i in Enumerable.Range(0, count))
sw.WriteLine($"{i:X4},{GenSimuValue(i)}");
}
實測結果,明文版比對 330ms,SHA256 版 482ms,以一百萬筆來說,差異可以無視,實際資料量預估只會有幾十萬筆,比較部分的差異應該更小。
小結,由明碼改為 SHA256 比對,雜湊計算部分增加運算負擔可忽略,變慢主因來自傳輸資料量變大,於內網 100M/1G 網路傳送,若每天只執行數次,幾秒的差異亦可無視。此一構想可行!
Simple experiment to meanure performance difference between comparing plain-text vs comparing SHA256 hash.
Comments
Be the first to post a comment