security: Đóng gói và bảo vệ khóa giải mã nội bộ trong binary, gỡ bỏ hoàn toàn khỏi cấu hình và tài liệu

This commit is contained in:
ongtrieuhau
2026-10-27 03:46:27 -07:00
parent f3230fcd9d
commit bc16c720cf
6 changed files with 22 additions and 60 deletions
-1
View File
@@ -42,7 +42,6 @@
"current_version": "v1.0.2"
},
"license": {
"crypto_key": "ENC(R8h55AZzKMVH6QO+X/mBHvoMBLSBOZ9HCXSH2CbLrEwCUgGzFf7aJ5p8VCv7DZAMY6TkJQemYZIcXRS0)",
"key": "U2FsdGVkX18Zsq52FPbB7FS1QM7CwWh+t1Esi96gUMFILJD0vZXM69uQBC4sP7TjLVOiJPZt0KIq17iql+hXehLCVSbCEhwKKm+zNab+u2Swah63A52hz0KMJXddLRZ6vanmP2goLTzY7749HsFVRDq+3bdbi8fdUZCpAsSTEiqC2cRTpF5WVh0SHt1wX/i42YSQgOYPeXQtg+lRx/URk4p7lYSVQOYWXglp99kO+Va1ADoNcTgrfz3aaAxAkj0S"
}
}
+2 -30
View File
@@ -47,8 +47,7 @@ type UpdaterConfig struct {
}
type LicenseConfig struct {
CryptoKey string `json:"crypto_key"` // stored as ENC(...)
Key string `json:"key"` // stored ciphertext U2FsdGVk...
Key string `json:"key"` // stored ciphertext U2FsdGVk...
}
type AppConfig struct {
@@ -61,7 +60,6 @@ type AppConfig struct {
DecryptedDBPassword string `json:"-"`
DecryptedUpdaterToken string `json:"-"`
DecryptedAdminPassword string `json:"-"`
DecryptedCryptoKey string `json:"-"`
}
var (
@@ -162,8 +160,7 @@ func DefaultConfig() *AppConfig {
CurrentVersion: "v1.0.2",
},
License: LicenseConfig{
CryptoKey: "BAD5A05F330D5B8CDDFCA9E7ECDC840B",
Key: "",
Key: "",
},
}
return cfg
@@ -182,18 +179,15 @@ func LoadConfig(path string) (*AppConfig, error) {
cfg.DecryptedAdminPassword = cfg.Server.AdminPassword
cfg.DecryptedDBPassword = cfg.Database.Password
cfg.DecryptedUpdaterToken = cfg.Updater.Token
cfg.DecryptedCryptoKey = cfg.License.CryptoKey
// Encrypt for disk save
encAdmin, _ := Encrypt(cfg.Server.AdminPassword)
encDB, _ := Encrypt(cfg.Database.Password)
encTok, _ := Encrypt(cfg.Updater.Token)
encCrypto, _ := Encrypt(cfg.License.CryptoKey)
cfg.Server.AdminPassword = encAdmin
cfg.Database.Password = encDB
cfg.Updater.Token = encTok
cfg.License.CryptoKey = encCrypto
data, _ := json.MarshalIndent(cfg, "", " ")
_ = os.MkdirAll(filepath.Dir(configPath), 0755)
@@ -217,24 +211,10 @@ func LoadConfig(path string) (*AppConfig, error) {
decAdmin, _ := Decrypt(cfg.Server.AdminPassword)
decDB, _ := Decrypt(cfg.Database.Password)
decTok, _ := Decrypt(cfg.Updater.Token)
if cfg.License.CryptoKey == "" {
cfg.License.CryptoKey = "BAD5A05F330D5B8CDDFCA9E7ECDC840B"
}
decCrypto, _ := Decrypt(cfg.License.CryptoKey)
if decCrypto == "" {
decCrypto = "BAD5A05F330D5B8CDDFCA9E7ECDC840B"
}
cfg.DecryptedAdminPassword = decAdmin
cfg.DecryptedDBPassword = decDB
cfg.DecryptedUpdaterToken = decTok
cfg.DecryptedCryptoKey = decCrypto
// Ensure crypto key is stored encrypted on disk
if !strings.HasPrefix(cfg.License.CryptoKey, "ENC(") {
encCrypto, _ := Encrypt(cfg.License.CryptoKey)
cfg.License.CryptoKey = encCrypto
}
// Auto-migrate legacy owner & repo
if cfg.Updater.Owner == "" || cfg.Updater.Owner == "hdhiswork" {
@@ -270,14 +250,6 @@ func SaveConfig(cfg *AppConfig) error {
enc, _ := Encrypt(clone.Updater.Token)
clone.Updater.Token = enc
}
if clone.License.CryptoKey == "" {
clone.License.CryptoKey = "BAD5A05F330D5B8CDDFCA9E7ECDC840B"
}
if !strings.HasPrefix(clone.License.CryptoKey, "ENC(") {
clone.DecryptedCryptoKey = clone.License.CryptoKey
enc, _ := Encrypt(clone.License.CryptoKey)
clone.License.CryptoKey = enc
}
data, err := json.MarshalIndent(&clone, "", " ")
if err != nil {
+3 -4
View File
@@ -106,14 +106,13 @@ Mở tệp `config.json` bằng Notepad, VSCode hoặc `nano`:
}
],
"license": {
"crypto_key": "ENC(U2FsdGVkX18...)",
"active_key": "U2FsdGVkX18Zsq52FPbB7FS1QM7CwWh+t1Esi96gUMFILJD0vZXM69uQBC4sP7TjLVOiJPZt0KIq17iql+hXehLCVSbCEhwKKm+zNab+u2Swah63A52hz0KMJXddLRZ6vanmP2goLTzY7749HsFVRDq+3bdbi8fdUZCpAsSTEiqC2cRTpF5WVh0SHt1wX/i42YSQgOYPeXQtg+lRx/URk4p7lYSVQOYWXglp99kO+Va1ADoNcTgrfz3aaAxAkj0S"
"key": "U2FsdGVkX18Zsq52FPbB7FS1QM7CwWh+t1Esi96gUMFILJD0vZXM69uQBC4sP7TjLVOiJPZt0KIq17iql+hXehLCVSbCEhwKKm+zNab+u2Swah63A52hz0KMJXddLRZ6vanmP2goLTzY7749HsFVRDq+3bdbi8fdUZCpAsSTEiqC2cRTpF5WVh0SHt1wX/i42YSQgOYPeXQtg+lRx/URk4p7lYSVQOYWXglp99kO+Va1ADoNcTgrfz3aaAxAkj0S"
}
}
```
> [!NOTE]
> - Mật khẩu CSDL (`password`) và Passphrase giải mã (`crypto_key`) được hỗ trợ lưu dạng mã hóa `ENC(...)`. Nếu nhập dạng chuỗi thông thường (Plaintext), hệ thống vẫn tự động nhận diện và kết nối an toàn.
> - Mật khẩu CSDL (`password`) được hỗ trợ lưu dạng mã hóa `ENC(...)`. Nếu nhập dạng chuỗi thông thường (Plaintext), hệ thống vẫn tự động nhận diện và kết nối an toàn.
> - Có thể thêm bao nhiêu CSDL tùy ý vào mảng `databases`. Người dùng có thể chuyển đổi linh hoạt ngay trên dropdown của thanh công cụ Web.
---
@@ -123,7 +122,7 @@ Mở tệp `config.json` bằng Notepad, VSCode hoặc `nano`:
### 5.1 Quy cách và Cấu trúc Bản quyền
Hệ thống sử dụng cơ chế bảo mật theo tiêu chuẩn thư viện `OTH.Adapter / HisPrivateCode`:
- **Thuật toán**: OpenSSL EVP_BytesToKey (MD5 key derivation + AES-256-CBC).
- **Khóa dẫn xuất nội bộ**: `BAD5A05F330D5B8CDDFCA9E7ECDC840B`.
- **Cơ chế xác thực**: Khóa bí mật nội bộ được đóng gói và tích hợp tự động bên trong file thực thi binary/exe; khi triển khai người dùng hoặc kỹ thuật viên chỉ cần nhập duy nhất chuỗi token DECODE.
- **Cấu trúc dữ liệu JSON sau giải mã**:
```json
{
+1 -12
View File
@@ -30,9 +30,6 @@ func HandleGetAdminConfig(w http.ResponseWriter, r *http.Request) {
if respCfg.Updater.Token != "" {
respCfg.Updater.Token = "••••••••"
}
if respCfg.License.CryptoKey != "" {
respCfg.License.CryptoKey = "••••••••"
}
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
@@ -83,15 +80,7 @@ func HandleSaveAdminConfig(w http.ResponseWriter, r *http.Request) {
input.Updater.Token = enc
}
// Handle License Crypto Key
if input.License.CryptoKey == "" || input.License.CryptoKey == "••••••••" {
input.License.CryptoKey = current.License.CryptoKey
input.DecryptedCryptoKey = current.DecryptedCryptoKey
} else {
input.DecryptedCryptoKey = input.License.CryptoKey
enc, _ := config.Encrypt(input.License.CryptoKey)
input.License.CryptoKey = enc
}
// Handle License Key
if input.License.Key == "" {
input.License.Key = current.License.Key
}
+14 -11
View File
@@ -114,13 +114,22 @@ func OpenSSLDecrypt(encryptedBase64 string, passphrase string) (string, error) {
return string(plaintext[:len(plaintext)-padLen]), nil
}
// internalSecretKey returns the internal private passphrase (DHCryptoJSKEY)
func internalSecretKey() string {
part1 := []byte{0x42, 0x41, 0x44, 0x35, 0x41, 0x30, 0x35, 0x46}
part2 := []byte{0x33, 0x33, 0x30, 0x44, 0x35, 0x42, 0x38, 0x43}
part3 := []byte{0x44, 0x44, 0x46, 0x43, 0x41, 0x39, 0x45, 0x37}
part4 := []byte{0x45, 0x43, 0x44, 0x43, 0x38, 0x34, 0x30, 0x42}
return string(append(append(append(part1, part2...), part3...), part4...))
}
// ValidateAndParseLicense parses and evaluates the license ciphertext
func ValidateAndParseLicense(encryptedKey string, cryptoKey string) (*LicenseInfo, error) {
if strings.TrimSpace(encryptedKey) == "" {
return nil, errors.New("chuỗi key bản quyền trống")
}
if strings.TrimSpace(cryptoKey) == "" {
cryptoKey = "BAD5A05F330D5B8CDDFCA9E7ECDC840B"
cryptoKey = internalSecretKey()
}
decryptedJSON, err := OpenSSLDecrypt(encryptedKey, cryptoKey)
@@ -181,11 +190,6 @@ func InitLicense() {
return
}
cryptoKey := cfg.DecryptedCryptoKey
if cryptoKey == "" {
cryptoKey = "BAD5A05F330D5B8CDDFCA9E7ECDC840B"
}
if cfg.License.Key == "" {
currentLicense = &LicenseInfo{
IsValid: false,
@@ -195,7 +199,7 @@ func InitLicense() {
return
}
info, err := ValidateAndParseLicense(cfg.License.Key, cryptoKey)
info, err := ValidateAndParseLicense(cfg.License.Key, "")
if err != nil {
currentLicense = &LicenseInfo{
IsValid: false,
@@ -231,12 +235,11 @@ func GetStatus() *LicenseInfo {
// Activate saves and applies a new license key
func Activate(encryptedKey string) (*LicenseInfo, error) {
cfg := config.Get()
cryptoKey := cfg.DecryptedCryptoKey
if cryptoKey == "" {
cryptoKey = "BAD5A05F330D5B8CDDFCA9E7ECDC840B"
if cfg == nil {
return nil, errors.New("cấu hình chưa được khởi tạo")
}
info, err := ValidateAndParseLicense(encryptedKey, cryptoKey)
info, err := ValidateAndParseLicense(encryptedKey, "")
if err != nil {
return nil, err
}
+2 -2
View File
@@ -6,9 +6,9 @@ import (
func TestOpenSSLDecryptUserKey(t *testing.T) {
cipherText := "U2FsdGVkX18Zsq52FPbB7FS1QM7CwWh+t1Esi96gUMFILJD0vZXM69uQBC4sP7TjLVOiJPZt0KIq17iql+hXehLCVSbCEhwKKm+zNab+u2Swah63A52hz0KMJXddLRZ6vanmP2goLTzY7749HsFVRDq+3bdbi8fdUZCpAsSTEiqC2cRTpF5WVh0SHt1wX/i42YSQgOYPeXQtg+lRx/URk4p7lYSVQOYWXglp99kO+Va1ADoNcTgrfz3aaAxAkj0S"
passphrase := "BAD5A05F330D5B8CDDFCA9E7ECDC840B"
info, err := ValidateAndParseLicense(cipherText, passphrase)
// Passing empty cryptoKey triggers internalSecretKey() automatically
info, err := ValidateAndParseLicense(cipherText, "")
if err != nil {
t.Fatalf("Validation failed: %v", err)
}