Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incorrect port number returned by $_SERVER['server_port']

Tags:

php

ssl

Scenario: I'm working on a Apache web server with some PHP. I point my browser at https://my.example.com/test.php that has the following lines of code in it:

<pre>
<?php
print_r($_SERVER);
?>
</pre>

The value printed out for SERVER_PORT is 80, not 443. But if I go to https://my.example.com:80/test.php the web server (Apache) barfs (An error occurred during a connection to my.example.com:80. SSL received a record that exceeded the maximum permissible length. Error code: ssl_error_rx_record_too_long). If I go to https://my.example.com:443/test.php then the URL redirects to https://my.example.com/test.php with no errors or problems except that my PHP prints out that the server port is 80 instead of 443.

Here's the relevant section from the conf.d/ssl.conf file (I removed what I believe are extraneous directives out and replaced the actual IP address with the word IP_ADDRESS):

Listen IP_ADDRESS:443    
<VirtualHost *:443>
        ServerName my.example.com
        ServerAlias my
        DocumentRoot "/path/to/document_root/htdocs"
        Options +Indexes
</VirtualHost>

Here is the full print out of $_SERVER variable (with my server details redacted/changed to anonymous examples):

Array
(
    [HTTP_HOST] => my.example.com
    [HTTP_USER_AGENT] => Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11
    [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    [HTTP_ACCEPT_LANGUAGE] => en-us,en;q=0.5
    [HTTP_ACCEPT_ENCODING] => gzip,deflate
    [HTTP_ACCEPT_CHARSET] => ISO-8859-1,utf-8;q=0.7,*;q=0.7
    [HTTP_KEEP_ALIVE] => 115
    [HTTP_CONNECTION] => keep-alive
    [HTTP_COOKIE] => PHPSESSID=randomstring_yes_I'm_that_paranoid
    [PATH] => /sbin:/usr/sbin:/bin:/usr/bin
    [SERVER_SIGNATURE] => 
Apache/2.2.3 (Red Hat) Server at my.example.com Port 80


    [SERVER_SOFTWARE] => Apache/2.2.3 (Red Hat)
    [SERVER_NAME] => my.example.com
    [SERVER_ADDR] => IP_ADDRESS_1
    [SERVER_PORT] => 80
    [REMOTE_ADDR] => IP_ADDRESS_2
    [DOCUMENT_ROOT] => /path/to/document_root/htdocs
    [SERVER_ADMIN] => [email protected]
    [SCRIPT_FILENAME] => /path/to/document_root/htdocs/test.php
    [REMOTE_PORT] => 49178
    [GATEWAY_INTERFACE] => CGI/1.1
    [SERVER_PROTOCOL] => HTTP/1.1
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => 
    [REQUEST_URI] => /test.php
    [SCRIPT_NAME] => /test.php
    [PHP_SELF] => /test.php
    [REQUEST_TIME] => 1292273758
)

As you can see, the SERVER_PORT is 80 and $_SERVER['HTTPS'] isn't set. According to the PHP docs, I thought it was supposed to be set to a non-empty value if the PHP script is accessed via HTTPS (which is what I'm doing).

Any idea what's going on? I'm just the web developer - I don't manage this server but my server admin tells me everything is working but I would like to know why $_SERVER['SERVER_PORT'] is returning 80 instead of 443 when viewing an HTTPS URL.

EDIT: I've edited the above example to illustrate my results in printing out the entire $_SERVER variable.

EDIT 2: Trying https://my.example.com:443/test.php as suggested in the comments below does the same thing - SERVER_PORT is 80 and HTTPS isn't set (specifically trying this URL redirects to https://my.example.com/test.php).

EDIT 3: OK, I've posted what I believe is the answer to this below (TL;DR: moving the SSL directives inside the VirtualHost directive and changing that directive to reference my site using it's IP address rather than a wildcard appears to have resolved the problem).

like image 526
Bill Avatar asked Dec 13 '10 20:12

Bill


3 Answers

I just recently encountered the same problem because some code I had that was masking the same problem had gotten in the way.

My diagnosis of the problem is as follows: This is a quirk of a change made in Apache 2.0. The Port directive is no longer part of the httpd.conf directives, essentially split up between the ServerName and Listen directives.

So my apache httpd.conf (Apache 2.2.23) had these entries

ServerName myservername.com
Listen: 5150

Yet PHP $_SERVER['SERVER_PORT'] was returning 80 to all requests being made on port 5150.

So poring over the Apache docs, I found a tidbit about Port being a deprecated directive and ServerName subsuming it.

I set my servername directive as follows

ServerName myservername.com:5150
UseCanonicalName On
Listen 5150

and suddenly the php script was doing the right thing, reporting _SERVER['SERVER_PORT'] as 5150.

Alternatively your httpd.conf could read

ServerName myservername.com
UseCanonicalName Off
Listen 5150

So then I dug into the php (5.3.20) source code and the apache source code until I found the source of the behavior.

The behavior is rooted in (at least for apached 2.2.23 source code) in dirOfApacheSource/server/core.c Look at the function AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r)

Here you will see that if your httpd UseCanonicalName directive is set to "On" then port gets parsed out of the ServerName directive. When the ServerName directive is not of the form ServerName myserver.com:myport number, the case code will fetch an ap_default_port of the request object (which is kindly set to default to 80 by the apache folks).

If it bothers you that you need to add the port explicitly to the ServerName directive in httpd.conf, your other choice is to set the UseCanonicalName to "Off", this forces the code in server/core.c to parse the URI request to extract the servername and port.

Six of one, half a dozen of the other, tweak your apache httpd.conf file and you will soon see the expected results.

like image 120
Stephan Doliov Avatar answered Nov 19 '22 17:11

Stephan Doliov


OK, I think I figured it out. My server admin changed the conf.d/ssl.conf from

SSLEngine On
# and other SSL directives

<VirtualHost *:443>
        ServerName my.example.com
        ServerAlias my

        # and more directives
</VirtualHost>

to

<VirtualHost IP_ADDRESS:443>
        ServerName my.example.com
        ServerAlias my

        SSLEngine On
        # and other SSL directives

        # and more directives
</VirtualHost>

and now PHP is seeing the correct SERVER_PORT # (443) and $_SERVER['HTTPS'] is also now set. So either it was putting the SSL directives inside the VirtualHost directive for my.example.com or maybe it was changing the VirtualHost directive to reference an actual IP address rather than a wild card that resolved this issue. Thanks everyone for your help on this.

like image 3
Bill Avatar answered Nov 19 '22 19:11

Bill


I seem to remember having this happen to me when I upgraded my apache a couple years ago. It ended up being a bad SSLCipherSuite, IIRC. Basically make sure you have a complete SSL configuration:

Do you have your ciphers, cert and key defined? And SSLEngine On? Something like this is required in your configuration minimally...

SSLEngine on

SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL SSLCertificateFile /path/to/apache/conf/server.crt SSLCertificateKeyFile /path/to/apache/conf/server.key

... and if you want to verify the client certificates, you also need something like:

SSLVerifyClient require SSLVerifyDepth 10 SSLCACertificateFile /path/to/apache/conf/trustedpubkeys.crt

Good luck!

if you want to verify this is actually happening, fire up a sniffer like tcpdump or wireshark. For tcpdump I'd use a commandline like ...

tcpdump -i eth0 -nn -s 1600 ip proto 17 and host IP_ADDRESS

(where IP_ADDRESS is the fqdn or dotted quad of your server)

Then get your $_SERVER var dump page, or a phpinfo() page, etc.

Your $_SERVER var dump shows your remote port, so you should be able to see which of your connections used that port, and whether it was connected to port 80 or 443.

Wireshark would let you do the same thing, if you're more of a GUI person.

like image 2
btx Avatar answered Nov 19 '22 19:11

btx