Insert Client Certificate In Serverside HTTP Headers

Problem this snippet solves:

An example iRule that pulls certain information from a client cert and passes it along to backend server in HTTP headers.

Here's one that illustrates how to use the "session" command in conjunction with SSL certificate information to allow passing of information to backend webservers.

As above, I want to deliver ssl cert serial number to http server behind BIG-IP, and redirect the users who has no cert to an error page at same time.

Code :

# client_cert_header_insert_rule

when CLIENTSSL_CLIENTCERT {

   # Check if client presented at least one cert
   if {[SSL::cert count] > 0}{

      # Insert the following fields in the session table with a timeout of 7200 seconds:
      #   Do the processing now as opposed to in HTTP_REQUEST as there
      #   can be many HTTP requests using the same SSL session ID
      #
      #   Index - item
      #   0 - base64 encoding of the client SSL cert
      #   1 - serial number of the cert
      #   2 - the verification status text for the client cert against the client SSL profile's root CA cert
      session add ssl [SSL::sessionid] [list \
         [SSL::verify_result] \
         [b64encode [SSL::cert 0]] \
         [X509::serial_number [SSL::cert 0]] \
      ] 7200

      log local0. "[IP::client_addr]:[TCP::client_port]: Added session data for cert. Status:\
         [X509::verify_cert_error_string [lindex [session lookup ssl [SSL::sessionid]] 0]] with key [SSL::sessionid]"
   }
}

when HTTP_REQUEST {

   # Check if SSL session ID is in the cache (SSL::sessionid returns 64 zeroes if it's not in v9 and a null string in v10)
   if {[SSL::sessionid] ne "0000000000000000000000000000000000000000000000000000000000000000" && [SSL::sessionid] ne ""}{

      # Get the session table entry (a TCL list) for this session ID
      set session_data [session lookup ssl [SSL::sessionid]]

      # Check if the first element of the session table entry for this session ID is 0 (status for successful cert validation)
      if {[lindex $session_data 0] == 0}{

         log local0. "[IP::client_addr]:[TCP::client_port]: Valid cert per session table entry. Inserting cert details in HTTP headers."

         # Insert cert details in the HTTP headers
         HTTP::header insert SSLClientCertStatus "ok"
         HTTP::header insert SSLClientCertb64 [lindex $session_data 1]
         HTTP::header insert SSLClientCertSN [lindex $session_data 2]

         # Exit this event in this rule
         return
      }
   }
   # If we're still in this rule, cert wasn't valid
   #   so send HTTP 302 redirect to an error page
   HTTP::respond 302 Location "http://[HTTP::host]/cert_error.html"

   log local0. "[IP::client_addr]:[TCP::client_port]: No or invalid cert from client."
}
Published Mar 18, 2015
Version 1.0
  • If you are worried about possible tampering, 'replace' is only marginally better than 'insert'.

    'replace' only replaces the last occurence of the header. So if an attacker adds their forged header twice, you end up with pretty much the same situation.

     

    To be absolutely safe, you can do a 'remove' first (which removes all occurences of the header) and then 'insert'.

  • Just note that the solution using ‘insert’ contains a vulnerability where the cert header info can be manipulated. To correct use 'header replace'

     

    Ex. HTTP::header replace SSLClientCertSN [lindex $session_data 2]