Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make cpanm secure by default #674

Open
wants to merge 5 commits into
base: devel
Choose a base branch
from

Conversation

garu
Copy link
Contributor

@garu garu commented Apr 26, 2024

Hi Miyagawa! Thank you so much for all the work you put to cpanm, it's an amazing tool that I use pretty much daily.

Right now, cpanm downloads from HTTP, not HTTPS, even on systems with a fully working TLS.

This patch enforces HTTPS on everything, though still allowing one to force HTTP via --from or --mirror.

We have built and tested the fatpacked cpanm with these changes and they all work really well. Please let me know if you have any concerns or if there is anything I can do to get this merged and released.

Cheers!

@miyagawa
Copy link
Owner

I think this is good for 99% of the users out there with a working TLS via curl out of the box. My concern is that it would suddenly break the remaining 1% on their CI systems.

I wonder if it makes sense to automatically fallback to HTTP for a while and then disable the fallback as a next step.

@atoomic
Copy link
Contributor

atoomic commented Apr 27, 2024

Would not you face the same problem you are describing when disabling the fallback later?
I would say, if someone really wants to use http, he can either set a custom mirror or use an older release of cpanm.

note: the 5.8 ci was recently removed.

@atoomic
Copy link
Contributor

atoomic commented Apr 27, 2024

We could also consider only supporting SSL but provide a more user friendly message on failures if TLS is not available so users are in charge and can use an alternate solution if needed.

@garu
Copy link
Contributor Author

garu commented Apr 27, 2024

@miyagawa we did a new commit to this PR patching configure_http so that it would let people know when an SSL backend is not available.

We also tweaked sub mirror to detect SSL certificate issues even when the client thinks it can handle SSL but the system's certificates don't exist or are invalid.

We believe this fully covers the edge cases you mentioned. Please let us know if you need any amends.

(if this update is acceptable, would you like us to also send the PR to another branch, like master?)

If none of the available clients from HTTP::Tinyish support SSL
then we should die with a better error message rather than
trying to use 'undef' as a backend (which fix an error when
calling $backend->new a few lines later).

This is also adding an extra check inside the 'mirror' function.
That function is used in multiple locations without checking
directly the error status.
The goal is to detect invalid certificate errors when SSL
is supported by the backend..

Note that depending on the backend and probably client version the error
message can differ.

`HTTP::Tiny` Internal Exception raised with invalid certificates:
	SSL connection failed for cpan.metacpan.org:
	Invalid certificate authority locations error:0D07A086:asn1
	...

`HTTP::Tinyish::LWP` Internal Exception raised with invalid certificates:
	500 Can't connect to cpan.metacpan.org:443 ()

`HTTP::Tinyish::Curl` Internal Exception raised with invalid certificates:
	curl: (60) Peer certificate cannot be authenticated with known CA certificates
	More details here: http://curl.haxx.se/docs/sslcerts.html

`HTTP::Tinyish::Wget` Internal Exception raised with invalid certificates:
	...
	ERROR: cannot verify cpan.metacpan.org’s certificate, issued by
	...

This patch accounts for all the scenarios above.

Signed-off-by: Breno G. de Oliveira <[email protected]>
@miyagawa
Copy link
Owner

Would not you face the same problem you are describing when disabling the fallback later?

Sure, but that can be done much later. I would say that HTTPS by default but allowing automatic fallback is still better than HTTP by default. Nobody would disagree that HTTPs by default and disallowing fallback is the most secure, but I am not sure if we're ready to pull that trigger now.

That can definitely change if e.g. perl ships with IO::Socket::SSL as a core module (iiuc there's a discussion about it in PSC).

@stigtsp
Copy link
Contributor

stigtsp commented Apr 28, 2024

[...] allowing automatic fallback is still better than HTTP by default. Nobody would disagree that HTTPs by default and disallowing fallback is the most secure, but I am not sure if we're ready to pull that trigger now.

Automatic fallback from HTTPS to HTTP would imho be equivalent to not using HTTPS at all, as it could allow for a downgrade attack.

That can definitely change if e.g. perl ships with IO::Socket::SSL as a core module (iiuc there's a discussion about it in PSC).

Wouldn't HTTPS support in those cases be provided by for example curl, wget, via HTTP::Tinyish?

Allowing HTTP (or unverified HTTPS) with an explicit --insecure parameter or similar would make more sense, and should accommodate corner cases like an environment that is missing cacerts or TLS libraries.

atoomic added a commit to CPAN-Security/cpanminus that referenced this pull request Apr 28, 2024
This is a backport of miyagawa#674 to the current release branch.
atoomic added a commit to CPAN-Security/cpanminus that referenced this pull request Apr 28, 2024
This is a backport of miyagawa#674 to the current release branch.
atoomic added a commit to CPAN-Security/cpanminus that referenced this pull request Apr 28, 2024
This is a backport of miyagawa#674 to the current release branch.
atoomic added a commit to CPAN-Security/cpanminus that referenced this pull request Apr 28, 2024
This is a backport of miyagawa#674 to the current release branch.
atoomic added a commit to CPAN-Security/cpanminus that referenced this pull request Apr 28, 2024
This is a backport of miyagawa#674 to the current release branch.
@garu
Copy link
Contributor Author

garu commented Apr 28, 2024

@miyagawa we have worked on an HTTP fallback, which is now #678, merging directly to master.

It contains an --insecure flag, like curl, that turns everything back to http-only, restoring current cpanm behavior when used. It also fallbacks to http-only whenever you deliberately use an http:// uri either directly or on --mirror.

If it pleases you, we can also do that same enforcement here.

Thanks!

@miyagawa
Copy link
Owner

miyagawa commented Apr 28, 2024

@stigtsp

Automatic fallback from HTTPS to HTTP would imho be equivalent to not using HTTPS at all, as it could allow for a downgrade attack.

i'm aware I was being unclear. I was not promoting automatic downgrade from HTTPS to HTTP. It was only about choosing the default whether it should be HTTPs or HTTP. Once HTTPS is chosen, of course it should not automatically fall back to HTTP.

So the fallback to HTTP should only be allowed if:

  • the user doesn't set a default mirror via CLI options, and
  • the user doesn't have a working TLS

The tricky bit here is that when the HTTP client gets a certificate error, there's no way to identify that if it's connected to an untrusted source, or if the user's TLS environment is not working (e.g. system certificates are too old).

@atoomic
Copy link
Contributor

atoomic commented Apr 29, 2024

I think the behavior implemented here is providing what you describe:

  • the user doesn't set a default mirror via CLI options, and
  • the user doesn't have a working TLS

but I would be encline to ignore that discussion for now, and focus on change from #678 which is the real fix for current customers.

Note that with the current released version of cpanm (which does not use HTTPTinyish) it's even harder to detect if the failure is coming from a certificate error. HTTPTinyish make it easier as it provides an object (aka HashRef) which contains all information including the error message.

But you are right when there is a certificate error, we do not know what's wrong. This is why we currently rely on the user and ask him to investigate the issue, and if needed use the --insecure option, which can be too strong if used blindly.

@@ -2673,7 +2679,8 @@ sub mirror {
die <<"DIE";
TLS issue found while fetching $uri:\n
$reply->{content}\n
Please verify your certificates or force an HTTP-only request/mirror at your own risk.
Please verify your certificates or force an HTTP-only request/mirror
using --insecure option at your own risk.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You say "at your own risk" a couple of times...

Using the whole tool is and has always been at my own risk/liability. "at your own risk" sounds like legal terms and seems to suggest there might be a time where someone else accepts the legal liability for the use of this tool.

If the aim is to say non-https is more risky than https it's worth saying "with the risks of non-secure http" or "but be aware of the risks of non-https"... Given how long cpan was non-http and non-https I'd say it might even be worth including a discussion of how risky it actually is to fetch tar balls without host verification or a secret communication channel.

There's a difference between the risks of usint the cpan cdn without https+verification and the risks of a 192 mirror without https+verification.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review. Could you please check if the new message properly addresses your concerns? Thank you!

@stigtsp
Copy link
Contributor

stigtsp commented May 6, 2024

Hi @miyagawa

The tricky bit here is that when the HTTP client gets a certificate error, there's no way to identify that if it's connected to an untrusted source, or if the user's TLS environment is not working (e.g. system certificates are too old).

I agree with the situations you're describing: hypothetical determinations like "this CA store is too old, ignore it" or similar will be fragile, and are likely to introduce vulnerabilities. A fatal error is an important feature when we cannot verify that the connection is to a trusted host.

So, do we actually need to optimize for edge cases where users update their cpanminus, but have a broken TLS setup?

I'm hoping an explicit --insecure and/or some environment variable would be a sufficient break-glass option for users who don't need secure transport when downloading and executing code from the network.

Thanks again for looking at this

stigtsp pushed a commit to stigtsp/cpanminus that referenced this pull request Aug 2, 2024
This is a backport of miyagawa#674 to the current release branch.
@stigtsp
Copy link
Contributor

stigtsp commented Aug 27, 2024

This is CVE-2024-45321

@shogo82148
Copy link

After applying this patch to actions-setup-perl, I received a report that cpanm doesn't work with Strawberry Perl <=5.22.

shogo82148/actions-setup-perl#1920

@guest20
Copy link

guest20 commented Sep 2, 2024

The question is, where can one get a trustworthy CA chain from if your client doesn't have the ability to verify current day host keys?

It seems like some choices for addressing old CA chains with a newer version of cpanm are:

  • fetch a new Mozilla::CA from a cpan mirror with a cert from a CA that appears in ancient versions of M:CA, or
  • hard-code cpan's PGP key into cpanm so it can verify the CHECKSUMS file(s) after a non-secret fetch or
  • bundle the newest M::CA into cpanm during make dist
  • bundle some hard-coded [version,sum] pairs from carefully chosen versions of M:CA so cpanm fetch/verify them itself

Maybe it's worth mentioning in the "could not verify host" message that maybe you need a new version of M:CA or cpanm?

@stigtsp
Copy link
Contributor

stigtsp commented Sep 2, 2024

The question is, where can one get a trustworthy CA chain from if your client doesn't have the ability to verify current day host keys?

OSes should provide a ca-certificates package and a secure way to update it. LWP and the next version of HTTP::Tiny use IO::Socket::SSL (and Net::SSLeay) to figure out the details of what CA store to use, defaulting to the OS one.

I think its preferable to use the system CA store when possible as it's updated by the OS vendor, and to support custom PKI setups that would otherwise be ignored if Mozilla::CA is the default.

Not familiar with Strawberry though..

@guest20
Copy link

guest20 commented Sep 2, 2024

I think its preferable to use the system CA store when possible

I think that just means changing my suggested error message to include "or update your OS's ca-certs package", but at least it means there's a second, likely non-deadlocked, way out of the situation... which is nice.

cpanm will fall back to (non-TLS) HTTP only requests when
all default options are used AND TLS support is unavailable, but
please note that, for security reasons, if TLS support is
available but broken (e.g. invalid/expired certificates), requests
will fail unless you explicitly use --insecure.

Also note that if you use --insecure but provide custom HTTPS
URLs, they will still fail if TLS support is not available.

Similarly, if you provide custom HTTP-only URLs, they will go
over plain HTTP and not require TLS support.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants