feat: add security system with system-wide proxy, DNS protection and encryption

- System-wide proxy: automatic Windows proxy configuration for all apps
- DNS leak protection: force all DNS queries through VPN
- Config encryption: AES-256-GCM encryption for all config files
- File protection: strict access permissions for config directory
- Leak detection: built-in security check system
- Kill Switch: temporarily disabled (will be improved in next version)

Security features:
✓ Automatic system proxy setup
✓ DNS leak protection (optional)
✓ AES-256-GCM config encryption
✓ File and directory protection
✓ Security leak checker
⚠ Kill Switch disabled (caused internet blocking issues)

Emergency recovery scripts included:
- ОТКЛЮЧИТЬ_KILLSWITCH.bat
- EMERGENCY_FIX_INTERNET.bat
- ЕСЛИ_СЛОМАЛСЯ_ИНТЕРНЕТ.txt

Documentation:
- Markdown/SECURITY_GUIDE.md - full security guide
- БЕЗОПАСНОСТЬ_БЫСТРЫЙ_СТАРТ.md - quick start guide
- CHANGELOG_SECURITY.md - detailed changelog
This commit is contained in:
2026-04-12 19:01:24 +06:00
parent 20d24a3639
commit b809e84220
18 changed files with 2063 additions and 31 deletions

View File

@@ -0,0 +1,212 @@
package security
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"os"
"path/filepath"
"golang.org/x/crypto/pbkdf2"
)
const (
saltSize = 32
iterations = 100000
keySize = 32
)
// GetMachineKey генерирует ключ на основе уникальных характеристик машины
func GetMachineKey() ([]byte, error) {
// Используем hostname и другие системные параметры
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
// Добавляем путь к исполняемому файлу для уникальности
exePath, err := os.Executable()
if err != nil {
return nil, err
}
// Комбинируем для создания уникального ключа
combined := hostname + exePath
hash := sha256.Sum256([]byte(combined))
return hash[:], nil
}
// Encrypt шифрует данные с использованием AES-256-GCM
func Encrypt(plaintext []byte, password string) (string, error) {
// Генерируем соль
salt := make([]byte, saltSize)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
return "", err
}
// Получаем машинный ключ
machineKey, err := GetMachineKey()
if err != nil {
return "", err
}
// Комбинируем пароль с машинным ключом
combinedPassword := append([]byte(password), machineKey...)
// Генерируем ключ шифрования
key := pbkdf2.Key(combinedPassword, salt, iterations, keySize, sha256.New)
// Создаем AES cipher
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// Используем GCM для аутентифицированного шифрования
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
// Генерируем nonce
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
// Шифруем
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
// Комбинируем соль и зашифрованные данные
result := append(salt, ciphertext...)
return base64.StdEncoding.EncodeToString(result), nil
}
// Decrypt расшифровывает данные
func Decrypt(encryptedData string, password string) ([]byte, error) {
// Декодируем из base64
data, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return nil, err
}
if len(data) < saltSize {
return nil, fmt.Errorf("неверный формат зашифрованных данных")
}
// Извлекаем соль
salt := data[:saltSize]
ciphertext := data[saltSize:]
// Получаем машинный ключ
machineKey, err := GetMachineKey()
if err != nil {
return nil, err
}
// Комбинируем пароль с машинным ключом
combinedPassword := append([]byte(password), machineKey...)
// Генерируем ключ
key := pbkdf2.Key(combinedPassword, salt, iterations, keySize, sha256.New)
// Создаем AES cipher
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// Используем GCM
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, fmt.Errorf("неверный размер зашифрованных данных")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
// Расшифровываем
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("ошибка расшифровки: неверный пароль или поврежденные данные")
}
return plaintext, nil
}
// SecureDelete безопасно удаляет файл (перезаписывает случайными данными)
func SecureDelete(filepath string) error {
// Получаем размер файла
info, err := os.Stat(filepath)
if err != nil {
return err
}
size := info.Size()
// Открываем файл для записи
file, err := os.OpenFile(filepath, os.O_WRONLY, 0)
if err != nil {
return err
}
defer file.Close()
// Перезаписываем случайными данными 3 раза
for i := 0; i < 3; i++ {
randomData := make([]byte, size)
if _, err := rand.Read(randomData); err != nil {
return err
}
if _, err := file.WriteAt(randomData, 0); err != nil {
return err
}
if err := file.Sync(); err != nil {
return err
}
}
// Удаляем файл
return os.Remove(filepath)
}
// SetFilePermissions устанавливает строгие права доступа к файлу
func SetFilePermissions(path string) error {
// Только владелец может читать и писать
return os.Chmod(path, 0600)
}
// ProtectDirectory защищает директорию и все файлы в ней
func ProtectDirectory(dirPath string) error {
// Устанавливаем права на директорию
if err := os.Chmod(dirPath, 0700); err != nil {
return err
}
// Защищаем все файлы в директории
entries, err := os.ReadDir(dirPath)
if err != nil {
return err
}
for _, entry := range entries {
if !entry.IsDir() {
filePath := filepath.Join(dirPath, entry.Name())
if err := SetFilePermissions(filePath); err != nil {
return err
}
}
}
return nil
}