[GH-ISSUE #156] Unsafe defaults facilitate privilege escalation #99

Closed
opened 2026-02-25 22:32:35 +03:00 by kerem · 2 comments
Owner

Originally created by @VP- on GitHub (Apr 15, 2019).
Original GitHub issue: https://github.com/FiloSottile/mkcert/issues/156

The only safe way to use a local CA on a typical system is to install its certificate in a separate browser profile, which is then used for development only. Otherwise, the following scenario can use the CA as part of an exploit chain:

  1. Gain remote read access to the file system.
  2. Read the CA private key from one of the standard (= easy to find) locations.
  3. Impersonate e.g. GitHub and replace downloaded binaries with your own.
  4. The user downloads and runs a binary (latest pre-built mkcert?)
  5. Remote code execution achieved.

By using a separate browser profile, all that a compromised CA key can achieve is to impersonate the page/app under development.

The default behavior for mkcert -install is to enable step 3 of the above exploit chain. And what's worse, the only way to make it secure is to abuse the environment variable $TRUST_STORES by providing an invalid value, so that the generated certs are not installed anywhere. One must then use -CAROOT to actually find the generated root certs (this one is at least documented).

This is the exact opposite of the "safe by default" policy that any software should follow, much less any software designed to help people make their own software more secure.

As a solution to this problem, you could provide a command which just generates the CA certificates (optionally in a specified directory) and mention it as the safe alternative to -install in all documentation. IMHO it's not necessary to completely replace -install since e.g. a dedicated development system, where the user is not installing random software from the internet, is still a perfectly valid use case for it.

As a bonus, please explicitly document a value for $TRUST_STORES which is guaranteed to not match any future store types, e.g. "none".

Originally created by @VP- on GitHub (Apr 15, 2019). Original GitHub issue: https://github.com/FiloSottile/mkcert/issues/156 The only safe way to use a local CA on a typical system is to install its certificate in a separate browser profile, which is then used for development only. Otherwise, the following scenario can use the CA as part of an exploit chain: 1. Gain remote read access to the file system. 2. Read the CA private key from one of the standard (= easy to find) locations. 3. Impersonate e.g. GitHub and replace downloaded binaries with your own. 4. The user downloads and runs a binary (latest pre-built `mkcert`?) 5. Remote code execution achieved. By using a separate browser profile, all that a compromised CA key can achieve is to impersonate the page/app under development. The default behavior for `mkcert -install` is to enable step 3 of the above exploit chain. And what's worse, the only way to make it secure is to abuse the environment variable `$TRUST_STORES` by providing an invalid value, so that the generated certs are not installed anywhere. One must then use `-CAROOT` to actually find the generated root certs (this one is at least documented). This is the exact opposite of the "safe by default" policy that any software should follow, much less any software designed to help people make their own software more secure. As a solution to this problem, you could provide a command which just generates the CA certificates (optionally in a specified directory) and mention it as the safe alternative to `-install` in all documentation. IMHO it's not necessary to completely replace `-install` since e.g. a dedicated development system, where the user is not installing random software from the internet, is still a perfectly valid use case for it. As a bonus, please explicitly document a value for `$TRUST_STORES` which is guaranteed to not match any future store types, e.g. "`none`".
kerem closed this issue 2026-02-25 22:32:35 +03:00
Author
Owner

@FiloSottile commented on GitHub (Apr 15, 2019):

Thanks for your issue, this has been debated a few times, and I am still not convinced that remote read access to the filesystem of a developer machine is any different from a full compromise. For example, an attacker can read the GitHub cookies from the browser store.

All -install does is install the root in the trust stores. You don't have to run it to use mkcert, so the TRUST_STORES=none mode is simply mkcert without -install.

<!-- gh-comment-id:483346166 --> @FiloSottile commented on GitHub (Apr 15, 2019): Thanks for your issue, this has been debated a few times, and I am still not convinced that remote read access to the filesystem of a developer machine is any different from a full compromise. For example, an attacker can read the GitHub cookies from the browser store. All `-install` does is install the root in the trust stores. You don't have to run it to use mkcert, so the `TRUST_STORES=none` mode is simply `mkcert` without `-install`.
Author
Owner

@VP- commented on GitHub (Apr 16, 2019):

Thanks for your issue, this has been debated a few times, and I am still not convinced that remote read access to the filesystem of a developer machine is any different from a full compromise. For example, an attacker can read the GitHub cookies from the browser store.

True, and red-herring arguments like this are the reason why. The existence of a problem is not an excuse for making it worse.

All -install does is install the root in the trust stores. You don't have to run it to use mkcert, so the TRUST_STORES=none mode is simply mkcert without -install.

Great! So the fix does not involve any code at all. The documentation only needs to mention that -install is optional. Preferably somewhere near the top, where -install itself is explained.

<!-- gh-comment-id:483553380 --> @VP- commented on GitHub (Apr 16, 2019): > Thanks for your issue, this has been debated a few times, and I am still not convinced that remote read access to the filesystem of a developer machine is any different from a full compromise. For example, an attacker can read the GitHub cookies from the browser store. True, and red-herring arguments like this are the reason why. The existence of a problem is not an excuse for making it worse. > All -install does is install the root in the trust stores. You don't have to run it to use mkcert, so the TRUST_STORES=none mode is simply mkcert without -install. Great! So the fix does not involve any code at all. The documentation only needs to mention that `-install` is optional. Preferably somewhere near the top, where `-install` itself is explained.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/mkcert#99
No description provided.