Если вам уже доводилось писать приложения на Node/Express, то вы не понаслышке знаете, что такие приложения обычно устанавливаются с HTTPS
и сертификатом сервера. Однако в рабочей среде Node.js HTTPS
, как правило, не требуется, поскольку Node-приложения прикрыты обратным прокси-сервером (например, Nginx
), который и обслуживает сертификаты.
Скорее всего, ваше Node-приложение будет работать как клиент, и ему придется вызывать серверные службы, защищенные HTTPS
. По умолчанию Node.js создается с набором часто используемых корневых сертификатов центров сертификации (СА). Но и здесь мы не застрахованы от ошибок в случаях, если Node-приложение осуществляет HTTPS
API-вызов серверных служб с самоподписанными сертификатами (например, частных корпоративных СА). Примеры ошибок:
UNABLE_TO_GET_ISSUER_CERT_LOCALLY
UNABLE_TO_VERIFY_LEAF_SIGNATURE
DEPTH_ZERO_SELF_SIGNED_CERT
Для устранения этих ошибок потребуется найти их источник.
Такие ошибки возникают в процессе SSL
-рукопожатия. Клиент пытается установить соединение с сервером через TLS
-рукопожатие. TLS
-рукопожатие — это обмен сообщениями между клиентом и сервером, в ходе которого они договариваются о версии TLS
и комбинации шифров, подтверждают подлинность сервера, а также генерируют ключи сессии.
В шаге 2 сервер отправляет сообщение со своим SSL
-сертификатом, а клиент проверяет его через СА, выпустившего данный сертификат. Так подтверждается подлинность сервера, и клиент взаимодействует с истинным владельцем домена.
Эти ошибки возникают в случаях, когда клиент не может проверить самоподписанный сертификат сервера из шага 2.
Простейший способ по устранению подобных ошибок заключается в использовании rejectUnauthorized
.
https.request({
....,
rejectUnauthorized: false,
},
...)
Вы также можете задать ее в качестве переменной среды:
NODE_TLS_REJECT_UNAUTHORIZED=0К сожалению, данный метод не безопасен, поскольку отключает проверку сертификата сервера, из-за чего Node-приложение становится уязвимым для так называемой “атаки посредника”. Получается, что данный способ допустим только в среде разработки, а не в рабочей среде.
Более безопасным способом будет указать, какой именно CA-сертификат ожидается от сервера. Иначе говоря, название сертификата должно совпадать с сертификатом сервера.
request({
ca: [fs.readFileSync([certificate path])],
rejectUnauthorized: true,
}
Как вы могли заметить, ca
— это массив, в котором при желании можно прописать несколько файлов сертификатов. Минус данного решения в том, что сертификат прописывается в самом в коде. Это весьма чревато в случае, когда нам нужно управлять несколькими версиями сертификатов в разных средах.
В Node версии 7.3.0 и выше появилась переменная среды NODE_EXTRA_CA_CERTS
, которая передается в файл СА-сертификата. Это позволяет дополнять «корневые» CA другими сертификатами. Сам файл сертификата должен состоять как минимум из одного доверенного сертификата в PEM
-формате.
Обратите внимание, что дополнительные сертификаты не имеют никакой силы, если в HTTPS
-клиенте или сервере явно прописано свойство ca
.
Чтобы избавиться от ошибок сертификатов, следует пользоваться свойством ca
или NODE_EXTRA_CA_CERTS
. Но появление тех же сообщений об ошибках все еще возможно. Как правило, это вызвано использованием неправильного сертификата. Для каждого из представленных вариантов вы должны получать полную цепочку сертификатов либо иметь как минимум один корневой сертификат CA.
Получить полную цепочку сертификатов можно через OpenSSL:
openssl s_client -connect ${REMHOST}:${REMPORT}Пример цепочки сертификатов:
Цепочка сертификатов Google CAВнимание: команда showcerts
не всегда работает при выполнении ее с прокси-сервера либо в случаях, когда удаленный сервер использует SNI
.
Перевод статьи Sunny Sun: How to Resolve Certificate Errors in a Node.js App with SSL Calls
Комментарии