Eduardo Almeida
Eduardo Almeida

Reputation: 21

How do I obtain SSL_CLIENT_* variables from the SSL client certificate using PHP 5.6 running on Apache 2.4?

Introduction

I'm using PHP 5.6 and Apache 2.4 in the back-end of my web site where I use SSL to authenticate clients as an optional form of entering the site. I want to get some SSL_CLIENT_* variables which are contained in the certificate but aren't being shown by Apache 2.4, therefore, I cannot access them using $_SERVER.

Description

I want to get the following environment variables from the client certificate:

When I dump $_SERVER in PHP 5.6 I can see what is returned by the variable SSL_CLIENT_VERIFY. If it returns GENEROUS, all three variables are shown; if it is SUCCESS only the first on the list above, SSL_CLIENT_S_DN_CN, is shown; and, if SSL_CLIENT_VERIFY is NONE, there is no certificate supplied by the client.

I used XAMPP v3.2.2 on Windows 10 (Apache 2.4 and PHP 7.0) locally and I can access all the variables, but, somehow, I cannot do the same using a remote Debian 9 server running Apache 2.4 and PHP 5.6.

I tried unsuccessfully to set up the SSLRequire (which is deprecated according to the docs - mod_ssl) option and Require as well.

I resorted to StackOverflow and ServerFault search for similar questions, involving SSL client certificates, but all the questions I came across involve SSL basic configuration (HTTP to HTTPS, Proxy, etc.) and creating headers with existing environment variables, like SSLRequire that works this way, for what I understood by reading the docs.

In the default-ssl.conf I tried to set the headers using the variables, but when I do the following:

print_r(getallheaders());

PHP 5.6 returns NULL:

Array ( [...] [SSL_CLIENT_SAN_OTHER_msUPN_0] => (null) [SSL_CLIENT_SAN_Email_0] => (null) )

The question again is how to get the variables SSL_CLIENT_SAN_OTHER_msUPN_0 and SSL_CLIENT_SAN_Email_0 from the client certificate.

Attachments

1. My Apache 2.4 SSL configuration file:

default-ssl.conf

<VirtualHost *:443>
    ServerName myserver.name #Omitted
    RequestHeader set SSL_CLIENT_SAN_OTHER_msUPN_0 "%{SSL_CLIENT_SAN_OTHER_msUPN_0}s"
    RequestHeader set SSL_CLIENT_SAN_Email_0 "%{SSL_CLIENT_SAN_Email_0}s"
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    # Configuracoes de cache    
    Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
    Header set Note "Cache desabilitado no servidor/host"

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    # Configuracao do SSL
    SSLEngine on
    # [...] Certificados AC e certificados da aplicacao: OMITTED
    SSLOptions +StdEnvVars +OptRenegotiate -StrictRequire +FakeBasicAuth -ExportCertData  
    # Solicita certificado SSL/TLS do cliente            
    SSLVerifyClient optional_no_ca 
    SSLVerifyDepth 3

    <FilesMatch "\.(cgi|shtml|phtml|php)$">
        SSLOptions +OptRenegotiate -ExportCertData -StrictRequire +FakeBasicAuth +StdEnvVars
    </FilesMatch>
    <Directory /usr/lib/cgi-bin>
        SSLOptions +StdEnvVars
    </Directory>
    BrowserMatch "MSIE [2-6]" \
                 nokeepalive ssl-unclean-shutdown \
                 downgrade-1.0 force-response-1.0
    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost>

2. PHP 5.6 file

index.php

<?php //index.php

// Get client certificate data
$cn = $_SERVER['SSL_CLIENT_S_DN_CN'];
$email = $_SERVER['SSL_CLIENT_SAN_Email_0'];
$id = $_SERVER['SSL_CLIENT_SAN_OTHER_msUPN_0'];

$client_certificate_data = [
    'cn' => $cn,
    'email' => $email,
    'id' => $id
];

echo json_encode(
    $client_certificate_data,
    JSON_NUMERIC_CHECK
);

// Output: {"cn":"EXAMPLE CN CONTENT HERE","email":null,"id":null}

Upvotes: 2

Views: 18339

Answers (2)

Steffen Ullrich
Steffen Ullrich

Reputation: 123320

If SSL_CLIENT_VERIFY is set to NONE it means no certificate was sent by the client. In this case of course no information about a client certificate can be provided.

In all other cases you should get SSL_CLIENT_S_DN_CN. SSL_CLIENT_SAN_* you get only if the client certificate contains SAN entries, i.e. the difference you see is probably due to different client certificates used.

Upvotes: 1

Nadir Latif
Nadir Latif

Reputation: 3763

The Apache directive SSLOptions +StdEnvVars passes the SSL information as environment variables to Php. See these links for more information:

Using SSL Client Certificates with PHP and SSLOptions Directive

Upvotes: 3

Related Questions