Аутентификация клиентского сертификата Node.js только на некоторых путях

У меня есть веб-приложение на основе node.js, для которого требуется безопасное (https) соединение с клиентом. Что я хочу, так это то, что на некоторых путях требуется аутентификация сертификата клиента, а на других путях нет.

Так например. Если пользователь переходит на https://www.example.com/main, серверу не требуется сертификат клиента. аутентификация (и, таким образом, браузер ничего не спросит). Но если пользователь переходит по адресу https://www.example.com/secure, аутентификация сертификата клиента потребуется (и, таким образом, в браузере появится диалоговое окно для выбора используемого сертификата).

Как я могу это сделать. Я могу принудительно выполнить аутентификацию сертификата клиента, если передам параметры requestCert:true и rejectUnauthorized:true в https.createServer. Проблема с этим подходом заключается в том, что сертификат клиента требуется для каждого пути.


person Jukka    schedule 23.01.2013    source источник
comment
Сертификаты клиента являются частью рукопожатия SSL; Я не уверен, что ты можешь делать то, что хочешь.   -  person Joe    schedule 24.01.2013
comment
@Joe: Вот чего я боялся. Но я видел веб-сайты, которые делают это. Сначала эти приложения отображают страницу, где они просят пользователя вставить смарт-карту (где находится сертификат клиента). Когда вы нажимаете «ОК», приложение перенаправляет пользователя на другой путь в том же приложении, для которого требуется сертификат клиента. В этот момент браузер отображает диалоговое окно для выбора пользователем используемого сертификата. Итак, может быть, есть способ как-то заставить новое рукопожатие в том же соединении?   -  person Jukka    schedule 24.01.2013
comment
У меня есть способ обойти эту проблему, но я не хочу его использовать. Одним из решений является использование двух разных слушателей (разных портов) в одном приложении. Другой требует проверки подлинности сертификата клиента, а другой нет. Проблема с этим подходом заключается в том, что мне приходится использовать нестандартный порт для второго. Это приводит к тому, что некоторым клиентам необходимо перенастроить свой брандмауэр, чтобы разрешить подключения к этому порту.   -  person Jukka    schedule 24.01.2013
comment
@Jukka: Вы нашли альтернативу отдельному слушателю?   -  person Peter Porter    schedule 15.05.2014
comment
Возможно, npmjs.com/package/client-certificate-auth -- хотя я сам не проверял.   -  person Joel Purra    schedule 08.07.2015


Ответы (4)


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

app.use('/api/cert', function(req, res, next){
    //validate that any token passed in the request was generated for a valid
    //cert login, otherwise reject request

    //pseudo-code
    if(isValidCertToken(req.authentication.token)) {
        next(); //this will pass it on to the correct endpoint in the /api/cert/[whatever] chain
    } else {
        req.status(401).send({error: 'invalid login, cert required'});
    }
});

Это потребует, чтобы вы разрешили пользователю аутентифицироваться с использованием сертификатов, установив для флага requestCert значение true, но также разрешите другие методы аутентификации, если пользователь не хочет использовать конечные точки «требуется сертификат», пока установлен rejectUnauthorized в false при настройке соединения https.

person weagle08    schedule 07.10.2015
comment
Это по-прежнему означает, что пользователю предлагается выбрать сертификат для каждого пути. - person DavidM; 18.11.2016
comment
Да, к сожалению. Мы избегаем этого, используя автономный сервер SSO, использующий OAuth 2.0, на который мы перенаправляемся для аутентификации. Последовательность действий: приложение-›[пользователь нажимает кнопку входа]-›направить на сервер OAuth 2.0-›[пользователь запрашивает сертификат]->[пользователь проверен на основе сертификата]->перенаправить в приложение с кодом для запроса токена ИЛИ приложение-›[пользователь клики на вход]-›направить на сервер OAuth 2.0-›[пользователь запрашивает сертификат]-›[пользователь выбирает отмену для отсутствия сертификата]-›отображается экран входа в систему-›[пользователь прошел аутентификацию с помощью UN/PW]-›перенаправить в приложение с кодом запросить токен - person weagle08; 18.11.2016

Я думаю, что эту проблему можно решить, настроив обратный шлюз прокси/API с использованием пакета Node.js http-proxy-middleware, который совместим с экспрессом (пакет веб-сервера, который мы используем).

Как показано в примере на домашней странице пакета Node.js, это можно сделать следующим образом: 1. Определите перенаправления для API UNAUTH и AUTH в соответствии с соглашением, используемым http-proxy-middleware, и используйте его на следующем шаге.

  1. Запустите экспресс-веб-сервер HTTPS на требуемом порту (скажем, 3000) с http-proxy-middleware, настроенным для работы в качестве шлюза обратного прокси-сервера/API. Нет необходимости устанавливать requestCert и rejectUnauthorized в параметрах https.createServer.

  2. Запустите экспресс-веб-сервер HTTPS на другом порту (скажем, 3001) для обслуживания запросов API UNAUTH. Нет необходимости устанавливать requestCert и rejectUnauthorized в параметрах https.createServer.

  3. Запустите экспресс-веб-сервер HTTPS на другом порту (скажем, 3002) для обслуживания запросов AUTH API. Установите requestCert:true и rejectUnauthorized:true в качестве параметров https.createServer.

Запросы UNAUTH будут перенаправляться туда и обратно с 3000 на 3001, а запросы AUTH будут перенаправляться туда и обратно с 3000 на 3002.

person shashi    schedule 17.01.2020

Я не уверен, правильно ли я понимаю, чего вы хотите.. но, насколько я понимаю, если вы используете экспресс, вы можете управлять этой вещью с помощью простого промежуточного программного обеспечения. В этом промежуточном программном обеспечении вы можете отслеживать любой запрос, поступающий с http или https через это

HTTPS-соединение имеет req.connection.encrypted (объект с информацией о SSL-соединении). HTTP-соединение не имеет req.connection.encrypted.

Также (из документов):

При поддержке HTTPS используйте request.connection.verifyPeer() и request.connection.getPeerCertificate() для получения данных аутентификации клиента.

Надеюсь, это поможет..

person khurrum qureshi    schedule 23.01.2013
comment
Я использую платформу Express со своим приложением. Все подключения уже https. Я хочу, чтобы некоторые пути требовали аутентификации клиента с помощью сертификата, а другие пути - нет. Я использую узел 0.8.18, и, похоже, у него нет подключенного метода verifyPeer. Есть метод getPeerCertificate, и он работает как положено (но не требует аутентификации клиентского сертификата). - person Jukka; 24.01.2013

Вы можете добиться этого путем повторного согласования TLS, но, к сожалению, это не поддерживается TLS 1.3, поэтому вам нужно отключить его:

https
    .createServer(
        {
            // configure key, cert, ca,
            secureOptions: require('constants').SSL_OP_NO_TLSv1_3,
        },
        function (req, res) {
            if (req.url.startsWith('/secure')) {
                req.socket.renegotiate(
                    {
                        requestCert: true,
                        rejectUnauthorized: true,
                    },
                    err => {
                        if (err) {
                            console.log('Renegotiation failed', err);
                            res.writeHead(500);
                            res.end('Internal Server Error');

                            return;
                        }

                        // handle secure content
                    },
                );
            } else {
                // handle main content
            }
        },
    )
    .listen(443);
person timukasr    schedule 12.01.2021