Node: request 2.65.0 `getPeerCertificate()` always returns `null` on node 4.2.1, works on 0.12.7

Created on 27 Oct 2015  路  13Comments  路  Source: nodejs/node

This seems to be a regression between node 4.2.1 and node 0.1.2:

var request = require('request')
request({
  url: 'https://github.com'
}, function (err, res, body) {
  console.log('err', err)
  console.log('peerCertificate:',res.req.connection.getPeerCertificate());
  console.log('authorized:',res.req.connection.authorized);
  console.log('authorizationError:',res.req.connection.authorizationError);
});

First node 0.12.7:

# grep ver node_modules/request/package.json
  "version": "2.65.0"

# node --version
v0.12.7

# node testssl.js
err null
peerCertificate: { subject:
   { businessCategory: 'Private Organization',
     '1.3.6.1.4.1.311.60.2.1.3': 'US',
     '1.3.6.1.4.1.311.60.2.1.2': 'Delaware',
     serialNumber: '5157550',
     street: '548 4th Street',
     postalCode: '94107',
     C: 'US',
     ST: 'California',
     L: 'San Francisco',
     O: 'GitHub, Inc.',
     CN: 'github.com' }

     ...

Then node 4.2.1:

# node --version
v4.2.1

# grep ver node_modules/request/package.json
  "version": "2.65.0"

# node testssl.js
err null
peerCertificate: null
authorized: true
authorizationError: null
tls

Most helpful comment

After some digging through the request source code, I don't believe the issue is with request.

In node 4.2.1, after the response connection is closed (once the close event is fired on the IncomingMessage), getPeerCertificate returns null. However, if you call getPeerCertificate before the response is closed, it returns the peer certificate as expected.

In node 0.12.x, calling getPeerCertificate even after the connection has closed returns the peer certificate.

This seem to be a regression between node 0.12 and 4.x (or maybe an intentional change that isn't clearly documented), and is especially noticeable with the request library, because the request callback is always called after the connection has been closed.

One workaround is to grab the response before the close event is fired. You can do this like so:

var request = require('request')

var r = request({
  url: 'https://github.com'
});

r.on('response', function(res) {
  console.log(res.req.connection.getPeerCertificate()) // works
})

All 13 comments

Do you also see it when you use the https or tls module directly? request is a third-party library.

WFM with master, by the way:

$ out/Release/node -e 'require("tls").connect(443, "github.com", function() { console.log(this.getPeerCertificate()) })'
{ subject: 
   { businessCategory: 'Private Organization',
     jurisdictionC: 'US',
     jurisdictionST: 'Delaware',
     serialNumber: '5157550',
     street: '548 4th Street',
     postalCode: '94107',
     C: 'US',
     ST: 'California',
     L: 'San Francisco',
     O: 'GitHub, Inc.',
     CN: 'github.com' },
  issuer: 
   { C: 'US',
     O: 'DigiCert Inc',
     OU: 'www.digicert.com',
     CN: 'DigiCert SHA2 Extended Validation Server CA' },
  subjectaltname: 'DNS:github.com, DNS:www.github.com',
  infoAccess: 
   { 'OCSP - URI': [ 'http://ocsp.digicert.com' ],
     'CA Issuers - URI': [ 'http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt' ] },
  modulus: 'B1D4DC3CAFFDF34EEDC167ADE6CB22E8B7E2AB28F2F7DC627008D10CAFD6166A21B0364B170D366304AEBFEA2051956566F2BFB94DA40C29EBF515B1E835B3701094D51B59B4260FD68357599DE17C09DDE013CA4D6F439BCDCF873A15A785DD6683ED930CFE2B6D381C798890CFAD58182D51D1C2A3F2478C6F3809B9B8EF4C930BCB839487EAE0A3B5D97B9B6B0F43F9CAEE800D28A776F125F4C1353CF674ADDE6A33827BDCFD4B76A7C2EEF26ABFA924A65FE72E7C0EDBC37473FA7EC6D8CF60EB365621B6C18AB824824D7824BAE91DA18AA787BE662569BFBE3B726E4FE0E4852508B19189B8D67465769B2C4F621FA1FA3ABE9C24BF9FCAB0C5C0678D',
  exponent: '0x10001',
  valid_from: 'Apr  8 00:00:00 2014 GMT',
  valid_to: 'Apr 12 12:00:00 2016 GMT',
  fingerprint: 'A0:C4:A7:46:00:ED:A7:2D:C0:BE:CB:9A:8C:B6:07:CA:58:EE:74:5E',
  ext_key_usage: [ '1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2' ],
  serialNumber: '0C009310D206DBE337553580118DDC87',
  raw: <Buffer 30 82 05 e0 30 82 04 c8 a0 03 02 01 02 02 10 0c 00 93 10 d2 06 db e3 37 55 35 80 11 8d dc 87 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 75 31 0b ... > }
^C

Thanks @bnoordhuis! Yep tls is fine here. I'd thought it was a node issue since the request version is the same, but given that the tls module itself is fine it looks like this is a request issue?

After some digging through the request source code, I don't believe the issue is with request.

In node 4.2.1, after the response connection is closed (once the close event is fired on the IncomingMessage), getPeerCertificate returns null. However, if you call getPeerCertificate before the response is closed, it returns the peer certificate as expected.

In node 0.12.x, calling getPeerCertificate even after the connection has closed returns the peer certificate.

This seem to be a regression between node 0.12 and 4.x (or maybe an intentional change that isn't clearly documented), and is especially noticeable with the request library, because the request callback is always called after the connection has been closed.

One workaround is to grab the response before the close event is fired. You can do this like so:

var request = require('request')

var r = request({
  url: 'https://github.com'
});

r.on('response', function(res) {
  console.log(res.req.connection.getPeerCertificate()) // works
})

I can confirm that .getPeerCertificate() works after close with v0.10 and v0.12 but not v4.x+. Test case:

var c = require('tls').connect(443, 'github.com', function() { this.destroy() });
c.once('close', function() { console.log(this.getPeerCertificate()) });

/cc @indutny - No opinion on whether it's a good idea to bring back the old behavior. I'm not sure from looking at the code it that's feasible without negating the memory savings.

@bnoordhuis this is rather unfortunate and fortunate at the same time. This gave us a pretty solid cut off in RSS usage. I think we could introduce an option to keep the SSL instance alive after close, but it will come at the increased memory usage cost.

Will this work for you, @mikemaccana ?

I would lean towards just documenting it. I don't think we need more complexity in the tls module and the current behavior doesn't strike me as unreasonable: Connection closed? Then so is the TLS metadata.

It's unfortunate this slipped through but it's not as if the old behavior was documented or even intentional (witness the paucity of regression tests for this feature.) It was just an accident of the implementation.

Sounds about right @bnoordhuis . Is anyone interested in contributing this? ;)

I'll write something for it a bit later.

Ditto. As long as the correct approach is documented (eg, holding connection open it seems) in happy.

I'm, ahem.

Documented in eff8c3e.

Was this page helpful?
0 / 5 - 0 ratings