0

Sure, PERL_LWP_SSL_VERIFY_HOSTNAME=0 will probably work if one is willing to write scripts: https://stackoverflow.com/questions/6795030/how-to-ignore-certificate-verify-failed-error-in-perl

But I just want to use it on the command line with HEAD(1):

$ PERL_LWP_SSL_VERIFY_HOSTNAME=0 HEAD -SU \
 https://law.sme.gov.tw/ailt/modules/forum/details/?topic_id=22489
HEAD https://law.sme.gov.tw/ailt/modules/forum/details/?topic_id=22489
User-Agent: lwp-request/6.78 libwww-perl/6.78

500 Can't connect to law.sme.gov.tw:443 (certificate verify failed)
Content-Type: text/plain
Client-Date: Sat, 29 Nov 2025 04:00:42 GMT
Client-Warning: Internal response

As we see, PERL_LWP_SSL_VERIFY_HOSTNAME=0 had no effect. What gives? Perl v5.40.1. By the way, Chrome doesn't need any help to find the 302 redirect to https://law.sme.gov.tw/ailt/ .

2
  • 1
    This used to annoy me a lot until I switched from using self-signed certs to lets encrypt. I can't remember if I ever got it working with HEAD (I vaguely recall hacking a copy of it years ago), but I know that curl -k -I works. Unfortunately, the output is not quite as detailed as HEAD -S -U. e.g. try curl -k -I https://law.sme.gov.tw/ailt/modules/forum/details/?topic_id=22489 Commented yesterday
  • 1
    curl -k -I --verbose shows the full request chain, but it also shows a lot more. it's very verbose, too verbose compared to HEAD -SU Commented yesterday

3 Answers 3

3

To complement @SteffenUllrich's answer, it seems indeed to be a bit of a mess and dependent on the versions of the related perl modules. Here on Debian with these package versions:

package version
libio-socket-ssl-perl 2.095-1
liblwp-protocol-https-perl 6.14-1
libnet-ssleay-perl 1.94-3
libwww-perl 6.81-1

I find that to disable SSL certificate verification altogether, you need to:

  • set the SSL_verify_mode UserAgent ssl_opts to 0 (aka IO::Socket::SSL::SSL_VERIFY_NONE), either as ssl_opts parameter to UserAgent->new() or via UserAgent->ssl_opts().
  • And set verify_hostname to 0 (for instance via the PERL_LWP_SSL_VERIFY_HOSTNAME env var or including verify_hostname => 0 in the ssl_opts) as otherwise, if it's set to 1 as it is by default, SSL_verify_mode is reset to 1 further down the line.

Not that I would recommend doing any such thing, but that means you could do what you want with:

perl <(sed '
  s/RequestAgent->new/&(ssl_opts=>{SSL_verify_mode=>0,verify_hostname=>0})/
    ' /usr/bin/lwp-request
  ) -m HEAD -SU 'https://law.sme.gov.tw/ailt/modules/forum/details/?topic_id=22489'

Where we edit lwp-request (to which HEAD is linked to) on the fly to append (ssl_opts=>{SSL_verify_mode=>0,verify_hostname=>0}) to the RequestAgent->new invocation (RequestAgent being lwp-request's wrapper package around LWP::UserAgent).


Here, it may be not as much that the root CA is not trusted on your system but that that server seems to intermittently return a bogus certificate chain.

Sometimes, I see it returning:

$ openssl s_client -connect law.sme.gov.tw:443
Connecting to 210.69.123.131
CONNECTED(00000003)
depth=2 C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI Root CA - G1
verify return:1
depth=1 C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI OV TLS CA - G1
verify return:1
depth=0 C=TW, L=臺北市, O=政府機關-經濟部中小及新創企業署, CN=startup.sme.gov.tw
verify return:1
---
Certificate chain
 0 s:C=TW, L=臺北市, O=政府機關-經濟部中小及新創企業署, CN=startup.sme.gov.tw
   i:C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI OV TLS CA - G1
   a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Apr  1 01:16:51 2025 GMT; NotAfter: Feb  4 16:00:00 2026 GMT
 1 s:C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI OV TLS CA - G1
   i:C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI Root CA - G1
   a:PKEY: RSA, 4096 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: May 18 02:51:28 2023 GMT; NotAfter: Dec 31 15:59:59 2037 GMT
 2 s:C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI Root CA - G1
   i:C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI Root CA - G1
   a:PKEY: RSA, 4096 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Feb 22 09:46:04 2019 GMT; NotAfter: Dec 31 15:59:59 2037 GMT
 3 s:C=TW, O=行政院, CN=政府伺服器數位憑證管理中心 - G1
   i:C=TW, O=Chunghwa Telecom Co., Ltd., CN=ePKI Root Certification Authority - G2
   a:PKEY: RSA, 4096 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Jul 19 06:46:45 2019 GMT; NotAfter: Aug 19 06:46:45 2031 GMT
 4 s:C=TW, O=Chunghwa Telecom Co., Ltd., CN=ePKI Root Certification Authority - G2
   i:C=TW, O=Chunghwa Telecom Co., Ltd., OU=ePKI Root Certification Authority
   a:PKEY: RSA, 4096 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Nov 17 08:51:35 2015 GMT; NotAfter: Dec 20 02:31:27 2034 GMT
 5 s:C=TW, O=Chunghwa Telecom Co., Ltd., OU=ePKI Root Certification Authority
   i:C=TW, O=Chunghwa Telecom Co., Ltd., OU=ePKI Root Certification Authority
   a:PKEY: RSA, 4096 (bit); sigalg: sha1WithRSAEncryption
   v:NotBefore: Dec 20 02:31:27 2004 GMT; NotAfter: Dec 20 02:31:27 2034 GMT

Where 2 is redundant as it's the root, 3, 4, 5 seem to have nothing to do with the website cert, but at least cert 1 allows openssl to identify the root CA at the top of the issuance chain (HiPKI Root CA - G1 which my system trusts).

While some other times (most times), it returns:

$ openssl s_client -connect law.sme.gov.tw:443
Connecting to 210.69.123.131
CONNECTED(00000003)
depth=0 C=TW, L=臺北市, O=政府機關-經濟部中小及新創企業署, CN=startup.sme.gov.tw
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C=TW, L=臺北市, O=政府機關-經濟部中小及新創企業署, CN=startup.sme.gov.tw
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 C=TW, L=臺北市, O=政府機關-經濟部中小及新創企業署, CN=startup.sme.gov.tw
verify return:1
---
Certificate chain
 0 s:C=TW, L=臺北市, O=政府機關-經濟部中小及新創企業署, CN=startup.sme.gov.tw
   i:C=TW, O=Chunghwa Telecom Co., Ltd., CN=HiPKI OV TLS CA - G1
   a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Apr  1 01:16:51 2025 GMT; NotAfter: Feb  4 16:00:00 2026 GMT
 1 s:C=TW, O=行政院, CN=政府伺服器數位憑證管理中心 - G1
   i:C=TW, O=Chunghwa Telecom Co., Ltd., CN=ePKI Root Certification Authority - G2
   a:PKEY: RSA, 4096 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Jul 19 06:46:45 2019 GMT; NotAfter: Aug 19 06:46:45 2031 GMT
 2 s:C=TW, O=Chunghwa Telecom Co., Ltd., CN=ePKI Root Certification Authority - G2
   i:C=TW, O=Chunghwa Telecom Co., Ltd., OU=ePKI Root Certification Authority
   a:PKEY: RSA, 4096 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Nov 17 08:51:35 2015 GMT; NotAfter: Dec 20 02:31:27 2034 GMT
 3 s:C=TW, O=Chunghwa Telecom Co., Ltd., OU=ePKI Root Certification Authority
   i:C=TW, O=Chunghwa Telecom Co., Ltd., OU=ePKI Root Certification Authority
   a:PKEY: RSA, 4096 (bit); sigalg: sha1WithRSAEncryption
   v:NotBefore: Dec 20 02:31:27 2004 GMT; NotAfter: Dec 20 02:31:27 2034 GMT

Same irrelevant last 3 certs but missing the cert for the intermediate CA that issued its certificate.

2
  • 1
    Rather than edit lwp-request on the fly with sed, IMO it would be better to cp /usr/bin/lwp-request /usr/local/bin/lwp-request-unsafe and edit the copy. Could also add BEGIN{$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0}; to the unsafe version of the script. Commented 17 hours ago
  • 1
    Just tested that on my system, it works. also made an alias: alias HEAD-unsafe='lwp-request-unsafe -m HEAD'. Commented 17 hours ago
2

The problem with law.sme.gov.tw is NOT that the name in the certificate is wrong - it actually matches. Instead validating the certificate requires a CA certificate which is not in the trust store.

This means that using PERL_LWP_SSL_VERIFY_HOSTNAME is wrong from start. This setting is - at least according to the variable name - about hostname verification in the certificate only, not about validating the trust path. Actually despite its name it was used to completely disable validation, at least for some versions of LWP and on some OS and with using the old Crypt::SSLeay library as backend instead of IO::Socket::SSL. In short, it is a mess - see also https://github.com/libwww-perl/libwww-perl/issues/448.

There is really no good way to completely disable certificate validation by environment variable in current versions of LWP (which includes HEAD, which is just a link to lwp-request). If you really need to disable certificate validation (which should better not be done in production code!) it might be better to use curl, i.e. in this case curl --insecure -X HEAD -v ...

-2

What gives?

Take a closer look at the question you linked: "How to ignore 'Certificate Verify Failed' error in perl?"

An important phrase in the question is "in perl". The answer given in that question applies to scripts that use a particular Perl module: IO::Socket::SSL.

Perl scripts that don't use IO::Socket::SSL or programs that are written in different languages will probably not pay attention to the PERL_LWP_SSL_VERIFY_HOSTNAME environment variable. The Perl-specific nature is hinted at in the name of the variable - it begins with PERL_LWP_SSL.

The head(1) command is part of the GNU core utilities, which are written in the C or C++ language instead of Perl. Programs like these are not likely to pay attention to Perl-specific variables. The HEAD(1) command you're trying to use is a Perl script, but it doesn't seem to use IO::Socket::SSL. It uses a couple of HTTP Perl modules (HTTP::Status and HTTP::Date) and URI and URI::Hueristic, but these don't seem to load the module that pays attention to the PERL_LWP_SSL_VERIFY_HOSTNAME variable.

This illustrates that it's important to search for solutions that apply to the programs you intend to use. Solutions that are offered for different programs may not work. You can try them, but knowing that the likelihood they'll work for your different programs is lower. You won't be disappointed.

4
  • 2
    Sorry, but you're mistaken here - HEAD (all-caps, not head lowercase) is a perl program included with LWP (libwww-perl) that fetches just the headers for a request, without the data. i.e. same as the HEAD request in the HTTP protocol, rather than a GET request. Commented yesterday
  • @cas I'm not mistaken in the part where I said "Perl scripts that don't use IO::Socket::SSL ... will probably not pay attention to the PERL_LWP_SSL_VERIFY_HOSTNAME environment variable.". But I'll add verbiage about /usr/bin/HEAD. Commented yesterday
  • 1
    See man LWP. That env var does not require the direct use of IO::Socket::SSL, the var is used by LWP::UserAgent in the options it uses when it calls subs from IO::Socket::SSL. The problem is in the HEAD program, which is really just a Q&D hack - an example program that hasn't been updated since 1999. Commented yesterday
  • 1
    And I don't get why you're still mentioning head from coreutils - that's a completely different and unrelated program that is irrelevant to the question. Commented yesterday

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.