cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.87k stars 3.18k forks source link

Use certificates from smartcards #22076

Open PeerWitthoeft opened 2 years ago

PeerWitthoeft commented 2 years ago

What would you like?

Hello,

i would like to use certificates provided by an secure smartcard.

Why is this needed?

Our Applications uses a secure smartcard with stored certifikate for authentifikation. For login, the user has to select his certificate in the browser. In our test environment this is handled by softcertificates (pfx-files). Due to security this is not allowed in production, where we would like to create some monitoring-like tests with cypress. So we need to use the "real" certificate from a hardware smartcard.

Other

No response

flotwig commented 2 years ago

Hi @PeerWitthoeft, I'm unclear as to if you are reporting a bug, or what specifically you are asking for. What you're proposing sounds like it should "just work", Cypress doesn't do anything special to block the browser process from accessing peripherals. Can you clarify the ask?

PeerWitthoeft commented 2 years ago

Hi @flotwig, sorry to make in more complicated than it is ;) For our tests in production, we need certificates from the microsoft certificate store. These are unavailable with cypress. I assume, cypress uses another (maybe own generated) store for certificates instead. Is it possible to make the use of the default certificate store configurable?

rachelruderman commented 2 years ago

Hi @PeerWitthoeft! Currently we support configuring the certificate authority and client certificates. Does this address your issue?

PeerWitthoeft commented 2 years ago

Hey @rachelruderman , unfortunately this solution is not possible in our environment, as we don't have access to any pfx- or pem-files.

rachelruderman commented 2 years ago

Thanks for the clarification, @PeerWitthoeft! I've consulted with the team and have some follow-ups for ya:

I assume, cypress uses another (maybe own generated) store for certificates instead.

We don't use another certificate store; we do disable SSL verification in the browser, but if I understand correctly, you're not talking about SSL certificates

For our tests in production, we need certificates from the microsoft certificate store. These are unavailable with cypress.

When you say, "These are unavailable with cypress", do you mean regular Chrome has what you need, but Cypress doesn't? Can you share some test code that doesn't work?

Resources that may help:

AtofStryker commented 2 years ago

Unfortunately we have to close this issue due to inactivity. Please comment if there is new information to provide concerning the original issue and we can reopen.

PeerWitthoeft commented 2 years ago

Hello again, sorry for the late answer. We made a few more researches and got the following results:

With Chrome and without Cypress:

With Chrome and Cypress

Sorry for censor almost half of the screenshots. Our company is very strict with security-issues ;) For the same reason, i am not allowed to share test-code here. It consists only of 3 steps:

  1. visit login-site (no, i am not allowed to write which site it is)
  2. select login with client certificate
  3. select confirm button

then the selection for the certificate pops up. I hope this helps to analyze our issue!

tbiethman commented 2 years ago

@PeerWitthoeft @rachelruderman had suggested this in a comment above:

Were you able to give that a try?

PeerWitthoeft commented 2 years ago

@tbiethman Yes, i tried it, but without any changes in behavior

tbiethman commented 2 years ago

@PeerWitthoeft I see, thanks for double checking that. Without more specifics, it is difficult to try and reproduce this issue. Is there any way for you to provide a minimal reproduction that exhibits the issues you're seeing?

For your screenshot above regarding "The browser only uses files in cypress path:":

image

What application is that/how are your viewing that data? I apologize, I'm unfamiliar with Windows certificate management.

PeerWitthoeft commented 2 years ago

@tbiethman I am currently trying to get permission to share some test code here.

For the screenshot: is used the process monitor of sysinternals tool suite, available here: https://docs.microsoft.com/en-us/sysinternals/downloads/procmon. This tool documents almost all activities of all processes running in windows. Filtering the correct entries is quite... lets call it "interesting" ;)

PeerWitthoeft commented 2 years ago

I have the permission to share some of our testcode:

it('should be able to use log in', () => { cy.visit('apps.datev.de/km-kalkulation'); cy.get('#label_secure18').click(); cy.get('#btn_MethodNext').click(); })

The popup for the certificate should show, if the user has an client-certificate in his own certificates in microsoft certificate store

tbiethman commented 2 years ago

Having site under test does help give some context, thank you. Have you explored using Chrome's AutoSelectCertificateForUrls policy? I'm unfamiliar with how dynamic the smartcard cert data/locations are, but it seems like that might be the best way to avoid the certificate popup altogether, which would be necessary for Cypress to test your application. The certificate selection popup is shown by the browser, not by your app, and thus you cannot interact with it using Cypress commands in an automated fashion.

mjhenkes commented 2 years ago

@PeerWitthoeft, This seems like a limitation with cypress today. I've converted this to a feature request.

PeerWitthoeft commented 2 years ago

@tbiethman , i tried the AutoSelectCertificateForUrls policy, but with no other outcome. The browser still won't use any certificates from the store. I know about the fact, that Cypress can't handle browser dialogs, but we have some ideas for this problem.

Arachnatur commented 1 year ago

Hello, it's been some time since the last comment.. any updates on this?

AtofStryker commented 1 year ago

Hi @Arachnatur. No updates yet. I do not see this work being prioritized by the team and might be a good opportunity for an open source contribution if any is up for the task!

meshuggahbobo commented 1 year ago

Totally agree! I also need this feature as the AutoSelectCertificateForUrls policy doesn't work (I perform a task with Selenium inside Cypress project to perform a login for some tests with AutoSelectCertificateForUrls policy and it's working)

meshuggahbobo commented 1 year ago

Right now, if you need a workaround for some of your tests that need login via smartcards, here the steps:

  1. Add a task for login that perform a new selenium-webdriver process for opening the login (I know... but right now is the only way that i found)
  2. Hit the button/link that invoke the login via certificate + pin digit; it's importatnt that the action is invoke via the executeScript
  3. Await the Promise that launch the powershell script that select the certificate and digit the pin

cypress.config.js

on('task', {
          loginSmartCard: async (pin) => {
            driver = new Builder()
              .setChromeOptions(chromeCapabilities)
              .forBrowser('chrome')
              .build()

            await driver.switchTo().window(driver.getWindowHandle())
            await driver.get('https://mywebsite.com/login')

            const button = driver.findElement(By.xpath("//button[contains(text(), 'LOGIN')]"))
            driver.executeScript("arguments[0].click();", button)

            await new Promise((resolve, reject) => {
              const child = spawn('powershell.exe', ['-ExecutionPolicy', 'ByPass', '-File', process.cwd() + "\\utils\\login.ps1", '-pin', pin])

              child.on('close', (code) => {
                if (code === 0) {
                  resolve()
                } else {
                  reject(new Error(`Child process exited with code ${code}`))
                }
              })
            })
......

login.ps1

param (
    [Parameter(Mandatory=$true)][string]$pin
 )

Start-Sleep -Milliseconds 2000

$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('Title of the Window - Google Chrome')
Start-Sleep -Milliseconds 500
$wshell.SendKeys('{ENTER}')

Start-Sleep -Milliseconds 2000

$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('Windows Security')
Start-Sleep -Milliseconds 500
$wshell.SendKeys($pin)
$wshell.SendKeys('{ENTER}')
Arachnatur commented 1 year ago

@meshuggahbobo Thank you very much for the workaround. Doesn't work for me, unfortunately, because the system is very restrictive (no selenium driver for me..). Still, nice work, thanks 👍 @AtofStryker Thanks for your reply - If I can find the time, I'll take your challenge - seems kind of sad that cypress can't do this, it's such an amazing framework otherwise! I realize it's probably not an important feature for most people, because in the wild you can use more comfortable logins. Still, there are some of us who must do testing on somewhat old-fashioned and/or restrictive corporate systems.

bimimicah commented 1 year ago

@AtofStryker @mjhenkes @flotwig @Arachnatur This would need to be implemented at the Cypress proxy level, just like the file-based clientCertificates support.

Basically the differences between smartcard-based client certificates and file-based client certificates are:

Cypress is using node.js' 'tls' module under the hood, so instead of passing the 'cert' or 'pfx' options to tls.connect(), you would instead pass the 'clientCertEngine', 'privateKeyEngine' options with the name of an OpenSSL engine that communicates with the HSM, as well as pass the 'privateKeyIdentifier' option with the PKCS#11 URI pointing to the smart card and slot to use. See https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions for a list of the securecontextoptions for tls.connect().

You'll also need to bundle the "OpenSSL engine that communicates with the HSM" mentioned earlier. An example would be the libp11 engine from the OpenSC team, and the underlying OpenSC library. If setup and configured with the default config mentioned on the libp11 page, the engine name you pass to tls.connect() would be 'pkcs11', and you would pass the PIN as the 'pin-value' attribute of the PKCS#11 URI.

HSM-based (smartcard-based) client certificates are fundamentally more secure than storing them in the file system, especially when the current Cypress implementation doesn't have a way to get the passphrase protecting the keys at runtime, instead the passphrase must be stored in plain text on the file system as well.

I hope that the Cypress team will seriously consider adding this support.

bimimicah commented 1 year ago

So, to summarize, unless I'm missing something, to implement this, Cypress would need to:

The user would then just set 'smartcardURI' as a valid PKCS#11 URI pointing to the desired HSM device and slot. (and, as optionally noted above, set 'cert' to a certificate file that goes with the private key on the HSM))