Существуют ли какие-либо реализации JavaScript SHA-256, которые обычно считаются заслуживающими доверия?

99

Я пишу логин для форума, и мне нужно хешировать пароль на стороне клиента в javascript, прежде чем отправлять его на сервер. Мне сложно понять, какой реализации SHA-256 я могу доверять. Я ожидал, что будет какой-то авторитетный сценарий, который будут использовать все, но я нахожу множество разных проектов со своими собственными реализациями.

Я понимаю, что использование чужой криптовалюты всегда является прыжком веры, если только вы не обладаете достаточной квалификацией, чтобы проверить ее самостоятельно, и что не существует универсального определения термина «заслуживающий доверия», но это кажется чем-то обычным и достаточно важным, что должно быть консенсуса о том, что использовать. Я просто наивен?

Изменить, так как это часто встречается в комментариях: Да, мы снова делаем более строгий хеш на стороне сервера. Хеширование на стороне клиента - это не окончательный результат, который мы сохраняем в базе данных. Хеширование на стороне клиента происходит потому, что его запрашивает человек-клиент. Они не указали конкретную причину, почему, вероятно, они просто любят излишне.

Йоно
источник
9
Чтобы не уйти от темы, а зачем хешировать пароль на стороне клиента?
Стив
5
@ddyer Даже близко. «Не бросайте свой собственный» применимо к изобретению собственного алгоритма, написанию собственной реализации алгоритма, разработке собственного протокола поверх криптоалгоритмов или почти ко всему, что указано выше, с использованием абстракции высокого уровня, насколько это возможно. Если вы думаете, что будете в безопасности, придерживаясь защищенного ядра и пишя только клей-код, вам будет плохо.
Стивен Тусет
26
если вы используете хешированный пароль без протокола запроса / ответа, то хешированный пароль ЯВЛЯЕТСЯ паролем, и это действительно то же самое, что передача пароля в открытом виде.
ddyer
26
@ddyer Есть некоторая ценность в защите пароля пользователя в виде открытого текста для всех других сайтов, на которых они могут его использовать, если не для нашего сайта в частности. Это простое исправление, которое, возможно, не поможет нам, но потенциально может помочь пользователю, если мы где-то облажаемся. И, как я уже сказал, запрос клиента, я ничего не могу с этим поделать, даже если бы захотел.
jono
5
@Anorov Я более чем готов к тому, чтобы изменить свое мнение :) но в данном случае я не совсем понимаю, как можно применить вашу точку зрения. Мы хэшируем пароль дважды: один раз на стороне клиента с помощью простого SHA-256 и один раз на стороне сервера с чем-то более требовательным. Первый для защиты открытого текста в случае MITM или подобного, а второй для грубой защиты. Даже если у вас есть база данных и хэш администратора, вы не сможете использовать их напрямую для проверки входа в систему.
jono

Ответы:

114

УСТАРЕЛО: многие современные браузеры теперь имеют первоклассную поддержку криптографических операций. См . Ответ Виталия Зданевича ниже.


Stanford JS Crypto библиотека содержит реализацию SHA-256. Хотя криптография в JS на самом деле не так хорошо изучена, как другие платформы реализации, эта, по крайней мере, частично разработана и в определенной степени спонсируется Дэном Боне , который является хорошо зарекомендовавшим себя и пользующимся доверием именем в криптографии. , и означает, что над проектом осуществляет контроль кто-то, кто действительно знает, что делает. Проект также поддерживается NSF .

Однако стоит отметить ...
... что если вы хешируете пароль на стороне клиента перед его отправкой, то хеш-код является паролем , а исходный пароль становится неактуальным. Злоумышленнику нужно только перехватить хэш, чтобы выдать себя за пользователя, и если этот хеш хранится на сервере без изменений, то сервер хранит настоящий пароль (хеш) в виде обычного текста .

Таким образом, ваша безопасность теперь хуже, потому что вы решили добавить свои собственные улучшения к ранее проверенной схеме.

Тайлерл
источник
32
Однако, если вы снова хешируете сервер, это вполне законно.
Ник Брант
13
@NickBrunt, если вы хешируете на сервере, то вы не сохраняете переданный пароль, но вы не добавили никакой безопасности, кроме передачи исходного пароля, в отличие от передачи хэша пароля, поскольку в любом случае то, что вы передаете, является настоящий пароль.
Tylerl 03
54
Верно, но это не хуже, чем отправка пароля, который, возможно, используется где-то в Интернете в виде открытого текста.
Ник Брант
15
Я согласен с @NickBrunt. Лучше, если злоумышленник прочитает случайную строку хэша, которая может соответствовать только этому конкретному приложению, а не исходный пароль, который может использоваться в нескольких местах.
Aebsubis
13
Мне нужно использовать хеширование на стороне клиента, потому что я не хочу, чтобы сервер когда-либо видел пароль пользователя в виде открытого текста. Не для дополнительной безопасности услуг, которые я предоставляю, а по отношению к пользователю. Я не только сохраняю хэш пользователя с постоянной солью (константой для каждого пользователя, а не глобально), но и повторно хеширую его со случайной солью сеанса при каждом входе в систему, что ДЕЙСТВИТЕЛЬНО обеспечивает небольшую дополнительную безопасность в сети от прослушивания. Если сервер будет скомпрометирован, игра будет окончена, но в любом случае это верно для всего, кроме p2p.
Hatagashira
56

На https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest я нашел этот фрагмент, который использует внутренний модуль js:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder().encode(message);                    

    // hash the message
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

    // convert ArrayBuffer to Array
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    // convert bytes to hex string                  
    const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
    return hashHex;
}

Обратите внимание, что crypto.subtlein доступно только на httpsили localhost- например, для вашей локальной разработки, python3 -m http.serverвам необходимо добавить эту строку в свой /etc/hosts: 0.0.0.0 localhost

Перезагрузитесь - и можно начинать localhost:8000работать crypto.subtle.

Виталий Зданевич
источник
2
Это круто. Спасибо. Доступен ли сопоставимый API в Node.js?
Con Antonakos
2
Встроенная криптографическая библиотека должна работать нормально: nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho
18

Реализация Forge SHA-256 быстрая и надежная.

Чтобы запустить тесты для нескольких реализаций JavaScript SHA-256, перейдите по адресу http://brillout.github.io/test-javascript-hash-implementations/ .

Результаты на моей машине предполагают, что forge является самой быстрой реализацией, а также значительно быстрее, чем Стэнфордская криптографическая библиотека Javascript (sjcl), упомянутая в принятом ответе.

Forge имеет размер 256 КБ, но извлечение кода, связанного с SHA-256, уменьшает размер до 4,5 КБ, см. Https://github.com/brillout/forge-sha256

блестящий
источник
Мне удалось установить его npm install node-forge, но теперь, как использовать библиотеку для создания хеша из строки foo?
пользователь
17

Для тех, кому интересно, это код для создания хэша SHA-256 с использованием sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
Дэнни Салливан
источник
5
Это должно быть включено в принятый ответ для тех, кто хочет использовать там решение. (Черт возьми, даже в их собственных документах нет такого фрагмента)
MagicLegend
Это лучший ответ. Я пробовал криптовалюту, но у меня это не сработало.
Аугусто Гонсалес,
12

Нет, нет возможности использовать JavaScript в браузере для повышения безопасности паролей. Я очень рекомендую вам прочитать эту статью . В вашем случае самая большая проблема - проблема курица-яйцо:

В чем «проблема куриного яйца» с предоставлением криптографии Javascript?

Если вы не доверяете сети для доставки пароля или, что еще хуже, не доверяете серверу не хранить секреты пользователей, вы не можете доверять им доставку кода безопасности. Тот же злоумышленник, который вынюхивал пароли или читал дневники до того, как вы внедрили криптовалюту, просто захватывает криптокод после того, как вы это сделаете.

[...]

Почему я не могу использовать TLS / SSL для доставки криптокода Javascript?

Вы можете. Это сложнее, чем кажется, но вы безопасно передаете шифрование Javascript браузеру с помощью SSL. Проблема в том, что, установив безопасный канал с SSL, вам больше не нужна криптография Javascript; у вас есть «настоящая» криптография.

Что приводит к следующему:

Проблема с запуском криптографического кода в Javascript заключается в том, что практически любая функция, от которой зависит криптография, может быть незаметно переопределена любым фрагментом контента, используемым для создания страницы хостинга. Криптобезопасность может быть отменена на раннем этапе процесса (путем генерации фиктивных случайных чисел или путем изменения констант и параметров, используемых алгоритмами), или позже (путем передачи ключевого материала обратно злоумышленнику) или --- в наиболее вероятном сценарии --- полностью обходя криптовалюту.

Для какой-либо части кода Javascript нет надежного способа проверить среду его выполнения. Криптографический код Javascript не может спросить: «Я действительно имею дело с генератором случайных чисел или с каким-то его факсимиле, предоставленным злоумышленником?» И он определенно не может утверждать, что «никому не разрешено делать что-либо с этой криптографической тайной, кроме тех способов, которые одобряю я, автор». Это два свойства, которые часто предоставляются в других средах, использующих шифрование, и они невозможны в Javascript.

В основном проблема заключается в следующем:

  • Ваши клиенты не доверяют вашим серверам, поэтому они хотят добавить дополнительный код безопасности.
  • Этот код безопасности доставляется вашими серверами (теми, которым они не доверяют).

Или, альтернативно,

  • Ваши клиенты не доверяют SSL, поэтому они хотят, чтобы вы использовали дополнительный код безопасности.
  • Этот защитный код доставляется через SSL.

Примечание. Кроме того, для этого не подходит SHA-256, так как несоленые неповторяющиеся пароли очень легко перебрать . Если вы все равно решите это сделать, поищите реализацию bcrypt , scrypt или PBKDF2 .

Брендан Лонг
источник
1
Это не так. Безопасность паролей можно повысить за счет хеширования на стороне клиента. Также неверно, что SHA-256 не подходит, если на стороне сервера используется что-то вроде PBKDF2. В-третьих, хотя статья в матасано содержит полезную информацию, суть вопроса неверна, поскольку теперь у нас есть приложения для браузера, которые коренным образом меняют проблему курицы и яйца.
user239558 03
2
Вы правы, что на клиенте нужно использовать PBKDF2. Ярость против шифрования javascript на стороне клиента предполагает, что начальная страница и последующие запросы имеют одинаковые свойства безопасности. Это явно не относится к приложениям браузера, где начальная страница устанавливается отдельно и статична. Это также неверно для любой страницы с семантикой вечного кеширования. В этих случаях это TOFU, который точно такой же, как при установке клиентской программы. Это также может быть верно в более сложных настройках, где обслуживание статических и динамических страниц разделено на сервере.
user239558 03
3
Законно избегать доступа системных администраторов к паролям пользователей, поэтому хеширование на стороне клиента не позволяет администраторам баз данных или другим ИТ-специалистам видеть пароли пользователей и пытаться повторно использовать их для доступа к другим службам / системам, поскольку многие пользователи повторяют свои пароли. .
Yamada
1
@Xenland Все, что вы делаете, это создаете новый пароль "токен + пароль". Все исходные проблемы остаются актуальными. Например, злоумышленник может заменить JavaScript кодом, чтобы отправить ему токен + пароль.
Брендан Лонг,
2
@Xenland Проблема с курицей и яйцом не имеет ничего общего с отправляемыми вами запросами. Проблема в том, что ваш клиент использует код JavaScript, который он загрузил по небезопасному соединению для обеспечения безопасности. Единственный способ безопасно отправить JavaScript в веб-браузер - передать его через TLS, и как только вы это сделаете, вам больше ничего не нужно будет делать, потому что ваше соединение уже безопасно. Неважно, какой у вас протокол, если вы используете код JavaScript, который злоумышленник может просто заменить.
Брендан Лонг,
10

Я нашел эту реализацию очень простой в использовании. Также есть щедрая лицензия в стиле BSD:

jsSHA: https://github.com/Caligatio/jsSHA

Мне нужен был быстрый способ получить шестнадцатеричное представление хэша SHA-256. Потребовалось всего 3 строчки:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
Cobbzilla
источник
1

Помимо Стэнфордской библиотеки, о которой упоминал Тайлерл. Я нашел jsrsasign очень полезным ( репозиторий Github здесь: https://github.com/kjur/jsrsasign ). Я не знаю, насколько он заслуживает доверия, но я использовал его API SHA256, Base64, RSA, x509 и т.д., и он работает очень хорошо. Фактически, он также включает Стэнфордскую библиотеку.

Если все, что вам нужно, это SHA256, jsrsasign может оказаться излишним. Но если у вас есть другие потребности в соответствующей области, я считаю, что это вам подходит.

Далеко
источник
2
Безопаснее и быстрее использовать developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Виталий Зданевич
2
Согласитесь, но, честно говоря, я ответил на этот вопрос в 2015 году, когда спецификация веб-криптографического API
Faraway,