← Назад к вопросам

Можно ли имея хэш получить данные по которым он был сделан?

1.8 Middle🔥 141 комментариев
#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Можно ли получить данные по хешу?

Коротко: нет, это невозможно. Хеширование — односторонняя функция. Но есть практические способы "восстановления" в специфических случаях.

Математическая невозможность

Хеш-функция — это математическое преобразование, которое берёт данные любого размера и выпускает строку фиксированного размера:

Данные (1 Мб, 1 Гб, ∞) → Hash Function → Хеш (256 бит)

Это необратимо по определению:

// Примеры хеш-функций
String hash1 = MessageDigest.getInstance("SHA-256")
    .digest("password123".getBytes());
// Результат: a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3

String hash2 = MessageDigest.getInstance("SHA-256")
    .digest("Password123".getBytes());
// Результат: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
// Полностью другой хеш!

Почему это невозможно?

1. Множественные источники → один результат Миллиарды разных данных имеют один и тот же хеш (коллизии). Обратная функция не определена:

// Хеш 256 бит может быть результатом 2^∞ разных входов
// Невозможно выбрать правильный вариант
public class HashCollisions {
    // MD5("a") = 0cc175b9c0f1b6a831c399e269772661
    // MD5("b") = 92eb5ffee6ae2fec3ad71c777531578f
    // MD5("...1000 других строк...") могут дать тот же результат
}

2. Информационно неполно Хеш содержит намного меньше информации, чем исходные данные. Обратного пути не существует:

Исходные данные: "The quick brown fox jumps over the lazy dog" (44 символа)
SHA-256 хеш: d7a8fbb307d7809469ca9abdcbed1b1467a5f53d  (только 40 символов)

Всё нижнее невозможно восстановить из верхнего

Практические атаки на хеши

Хотя восстановление невозможно, существуют способы угадать исходные данные:

1. Brute Force атака

Перебор всех возможных значений:

public class BruteForceAttack {
    public String findPassword(String targetHash) {
        // ❌ Очень медленно
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            String candidate = String.valueOf(i);
            String hash = SHA256(candidate);
            if (hash.equals(targetHash)) {
                return candidate;
            }
        }
        return null;
    }
}

Время: для пароля из 8 символов — миллиарды операций.

2. Rainbow Table атаки

Предварительные таблицы хеш → данные:

// Готовая таблица размером 500 Гб содержит:
// MD5("password") → "password"
// MD5("123456") → "123456"
// MD5("admin") → "admin"
// ...

// Поиск: O(1), находим мгновенно
Map<String, String> rainbowTable = new HashMap<>();
rainbowTable.put("5f4dcc3b5aa765d61d8327deb882cf99", "123456");
String originalPassword = rainbowTable.get(hash);

Защита: соль (salt)

public class SecurePasswordHashing {
    public String hashPassword(String password) {
        String salt = generateRandomSalt();
        String hash = PBKDF2(password + salt);
        return salt + ":" + hash;  // Сохраняем обе части
    }
    
    // Rainbow таблица становится бесполезной — миллиарды вариантов
}

3. Dictionary атака

Перебор известных слов и паролей:

public class DictionaryAttack {
    public String findPassword(String targetHash) {
        List<String> commonPasswords = Arrays.asList(
            "password", "123456", "admin", "qwerty", "admin123"
        );
        
        for (String guess : commonPasswords) {
            if (SHA256(guess).equals(targetHash)) {
                return guess;  // Найдено!
            }
        }
        return null;
    }
}

Защита: strong пароли, которые не в словарях.

Технология Bcrypt/Argon2

Модерные алгоритмы замедляют перебор:

public class ModernHashing {
    public static void main(String[] args) throws Exception {
        String password = "securePassword";
        
        // Bcrypt: deliberate slowness
        String bcryptHash = BCrypt.hashpw(password, BCrypt.gensalt(12));
        // Один хеш занимает 100ms (вместо 1мкс у MD5)
        // Brute force становится невозможен: 10^15 попыток = 3000+ лет
        
        // Проверка
        boolean matches = BCrypt.checkpw(password, bcryptHash);
    }
}

Хеши используемые для проверки целостности

Для таких задач хеши отлично работают:

public class FileIntegrity {
    public void verifyDownload(File file, String expectedHash) {
        String actualHash = SHA256(file);
        
        // Мы не можем восстановить файл из хеша
        // Но можем проверить, что он не повреждён
        assert actualHash.equals(expectedHash) : "File corrupted";
    }
}

public class SignatureVerification {
    // Git коммиты, цепочки блокчейна
    // Проверяем целостность, не восстанавливая данные
}

Почему это хорошо?

Безопасность: пароли не могут быть украдены в исходном виде ✅ Компактность: 500 Мб файл → 32 байта хеша ✅ Детерминированность: одни данные = один хеш (полезно для проверок) ✅ Универсальность: работает для любого размера данных

Итог

Прямое восстановление: невозможно (математически доказано)

"Угадывание": возможно только для:

  • Слабых паролей (брутфорс через rainbow table)
  • Частых слов из словаря
  • Слишком быстрых хеш-функций без соли

Защита:

  • Используй Bcrypt/Argon2 вместо MD5/SHA1
  • Добавляй соль к каждому паролю
  • Выбирай сложные пароли (не из словаря)
  • Никогда не логируй хеши в открытом виде