When could the HTTP Host header be undefined? - php

According to RFC 2616, which defines HTTP/1.1, the Host: header is mandatory.
A client MUST include a Host header field in all HTTP/1.1 request messages .
But the PHP manual implies that it could be empty:
'HTTP_HOST': Contents of the Host: header from the current request, if there is one.
In which situations could this header, and thus $_SERVER['HTTP_HOST'], be empty? Could my application depend on its being there?

It can be empty in HTTP 1.0. If no host header is specified, virtual hosting won't work at all, so the default vhost in your web server will be used.
I just tested this myself; in PHP under Nginx the $_SERVER['HTTP_HOST'] variable got set to the name of the virtual host, which is _ in my case. But that also depends on your fastcgi_params configuration in Nginx.
On shared hosting this is not important since the default vhost will be set to some information page from the hosting company, and so your script will not be run. Could be a good thing to keep in mind for your own server though.

Crawlers (e.g. google), scrapers or even perfectly legal scripts interfacing with your API may accidentally or ignorantly skip the Host header.
I added this answer because this question came up on google when I looked for the same thing.

Related

AWS & PHP: https website redirects to http when there is no slash at the end

We have a client that hosts their IIS web server on AWS. When navigating to a particular PHP web application on this server, it works when there is a slash on the end, but not when it is absent.
this works:
https://example.com.au/application/
However, if one were to enter this into the address bar:
https://example.com.au/application
it redirects to the equivalent http address with a slash on the end:
http://example.com.au/application/
http is disabled via the firewall, so the result is an error.
Here is the request details in Chrome debugger
So my question is, what does my client need to check to ensure this redirect does not occur? or that instead of redirecting to HTTP, it redirects to HTTPS?
Additional info:
This same issue does not seem to occur with .NET web applications. Eg 'https://example.com.au/dotnetapp' will not redirect to 'http://example.com.au/dotnetapp/'.
There are no rules configured in "URL rewrite"
IIS logs show requests when the HTTPS url is triggered, but not the HTTP one.
Edit: This seems to be due to browser caching. After disabling browser caching, i can see the 301 entry in the log files.
'index.php' is set as a default document
One possible reason is that the PHP project doesn't know that the secure connection is active and so it's redirecting the page to the http version when adding the slash.
PHP application can detect the secure connection by the $_SERVER['SERVER_PORT'], $_SERVER['REQUEST_SCHEME']. But if the application is behind some reverse proxy (e.g. Varnish or Amazon’s Elastic Load Balancer), the connection to the PHP application is probably not secured. PHP should be informed about the original secure connection with X-Forwarded-* headers.
Please check if the PHP has these variables set:
$_SERVER['HTTP_X_FORWARDED_PROTO']: should be https,
$_SERVER['HTTP_X_FORWARDED_PORT']: should be 443.
Symfony framework
If the application is using the framework, e.g. Symfony, it should be configured to trust the IP of the reverse proxy and to trust also these headers:
# config/packages/framework.yaml
framework:
# ...
# the IP address (or range) of your proxy
trusted_proxies: '192.0.0.1,10.0.0.0/8'
# trust *all* "X-Forwarded-*" headers
trusted_headers: ['x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port']
# or, if your proxy instead uses the "Forwarded" header
trusted_headers: ['forwarded']
See https://symfony.com/doc/current/deployment/proxies.html for more details and https://symfony.com/doc/current/deployment/proxies.html#but-what-if-the-ip-of-my-reverse-proxy-changes-constantly for more detaiils if the IP address of reverse proxy server changes.
Looks like you are setting location header in the 'index.php' file and so browser is redirecting to the http url.
If the index.php has code like below, replace the http to https and to the correct URL
header("location:http://example.com.au/application/");
Updated :
Also check your folder to see if any other files are redirecting.
Please make sure the index.php is listed as the first in the default document list and none of the other files contain redirect code.
You can search for "meta http-equiv="refresh" http tags in all the files in folder to see if they are redirecting.

Getting multiple HTTP response headers for Wordpress site running on Apache2

I am currently trying to configure the response headers for a Wordpress website.
Web Server: Apache 2
Operating System: Ubuntu 16.04
Right now, I have figured out that I am able to modify the HTTP response headers for the website through 3 different ways:
Configuring apache2.conf in /etc/apache2
Configuring the .htaccess file within the Wordpress directory
(I do not have mod_headers.c therefore I cannot use this method)
Directly specifying the headers to use in the header.php file of the current Wordpress theme
Previously I set the website's response headers through directly specifying the HTTP headers in the header.php file (which contains all the scripts and whatnot to be used for each page). However right now, I'm trying to move all header configuration into apache2.conf such that all sites hosted on the server have the same response headers.
However, after restarting the Apache server, I am getting duplicate response headers even though I had removed all header declarations from the header.php file, and I am not sure why...
Because of security issues, the authorities of the main domain that my department's site is on is requiring us to only specify one instance of each HTTP header.
Does anyone know how to resolve this issue, also, what are the best practices for setting HTTP response headers for sites hosted on an Apache 2 server?

Apache Won't Request My SSL Client Certificate

First off, please note that I am new to configuring SSL. In the past, I've always been fortunate to have an IT department to set that up for me ahead of time. So be prepared for the possibility that I might need to ask for clarification on some of your answers. =)
What I'm Trying to Do
I'm setting-up a company intranet website for employees. For example, there will be a browser start page that shows content customized for each employee. As such, I need to be able to identify said employee without requiring any username/password or other prompting (one-time setup is ok though; I just don't want them having to be prompted every time). Naturally, SSL would seem to be the best way to go about doing this.
I'll have a MySQL database setup to associate "user" accounts with SSL_CLIENT_M_SERIAL and SSL_CLIENT_I_DN, which I'm assuming will be unique for each client certificate(?). I got that idea from this article: http://cweiske.de/tagebuch/ssl-client-certificates.htm
The first time the user goes to the internal website, they won't have a certificate (I do NOT want to be generating them manually for clients!), in which case $_SERVER["SSL_CLIENT_VERIFY"] == "NONE". If that happens, it'll go to the user account setup page, which will include a step where PHP generates an SSL client certificate and sends it to the browser for the user to install. Nice and simple. The user then installs the cert, the association is made, and after restarting the browser (for good measure), the user goes back to the internal website.
At this point, Apache should request the client certificate, which the browser then sends. The PHP script then parses the necessary $_SERVER variables, compares against the MySQL database, and good times are had by all. Again, nice and simple.
What's Working So Far
I have the server-side certificates installed. And yes, they are self-signed (for obvious reasons). Apache has mod_ssl installed and all that seems to be working fine. I created a PHP script that just dumps the $_SERVER array, and all the SSL_SERVER_* key values match the certificate I created for it.
The Problem
I can't get the client certificates to work! In that same PHP script, no matter what I do, SSL_CLIENT_VERIFY == "NONE" and the other SSL_CLIENT_* keys are missing. This is what happens if I have SSLVerifyClient set to optional in ssl.conf. According to every tutorial I've read, they all say that the webserver should ask the browser for a client certificate. Thing is, I can't get it to do that! It just goes straight to the PHP script and assumes I have no client certificates at all. This happens in Firefox, Chrome, and IE.
So I tried setting SSLVerifyClient to required and restarted the webserver. With that option in place, I can't even establish an SSL connection. Firefox just says the connection has been reset (other browsers display their own versions of that error as well). What's weird is that the logs don't show ANY activity on these connection attempts! I.e. access_log, error_log, ssl_access_log, ssl_error_log, AND ssl_request_log all don't show ANYTHING; it's as if the attempt never even occurred. This is frustrating because it means I don't even have an error message to work from. Just a webserver passive-aggressively telling me to go to hell.
I tried generating/installing my own client certificate manually using PHP's OpenSSL extension. The certificate installed just fine, though I can't find any information on how to associate that certificate with the server one (assuming I even need to?). Also, it doesn't seem to matter anyway, because Apache still won't even request a client certificate if optional is set. And if require is set, it just blows up without explanation. I need it to be set to optional anyway for this schema to work.
The Environment
OS: CentOS 5.7 64-bit (VirtualBox)
Apache: 2.2.3
PHP: 5.3.10
I'm guessing you might need more info to help me, so please ask! I'll provide you with whatever you need.
To summarize, I need to know how to get Apache to request an SSL client certificate given the conditions outlined above. Also, if there's any special signing/etc that has to be done to make the client certificate "compatible" with the server certificate (again, WITHOUT doing so manually via shell for each client cert!), I'll need to know that as well.
I am 100% stuck on this as of now. Can't find anything even remotely helpful on Google. Any help you can provide on this would be TREMENDOUSLY appreciated!! Thanks! =)
First, you need to configure Apache Httpd to request a client certificate. For this, you need at least to use SSLVerifyClient optional on the location/directory you want to be authenticated with this method.
Secondly, the certificates sent by the client needs to be trusted by Apache Httpd too. (You could in principle use SSLVerifyClient optional_no_ca, let any client cert through at the Apache Httpd SSL/TLS stack, and only then verify the certificate within PHP, but that's quite a bit of work, for which you need be a bit more careful since that's not necessarily easy code; more importantly, this would be quite useless in this context, since you're in a scenario where you're in control of your CA.)
As far as I understand, SSL_CLIENT_VERIFY (a variable that I haven't used much myself) seems only really useful with the SSLVerifyClient optional_no_ca. It might work with SSLVerifyClient optional, but I doubt so. SSLVerifyClient require will reject connections using a client certificate that is not trusted (by one of the CAs in SSLCACertificateFile/SSLCACertificatePath), or if there is no certificate. As far as I know, SSLVerifyClient optional will let the client through without a certificate or with a trusted certificate, but will also reject the connection if the certificate is not trusted.
Here, by rejecting the connection, I mean closing the SSL/TLS connection abruptly with an alert. There is no chance to produce an HTTP(S) error page. All you'll get in the standard browser error, something along the lines of ssl_error_unknown_certificate_.... (You should consider this in terms of usability.)
From then onwards, what you need is to set up your own CA, possibly web-based with in-browser key-generation and within the same website. You wouldn't want SSLVerifyClient require for that, because you would need to let the users who don't have a certificate yet in (use optional instead). This being said, these directives need not apply to the entire host, but can be specific to certain locations/directories.
Integrating your own web-based CA (or more generally, creating your own CA) isn't necessarily easy if you're new to all this. Ready-made tools exist (e.g. OpenCA), or you can build your own using various bits of JavaScript/ActiveX, and you would need the server-side code to handle the SPKAC or PKCS#10 requests (and to issue the actual certificate). (For such a CA to be useful, you'd want the users who apply for a new certificate to provide some proof of ID at the time of application, perhaps a password.)
When this is set up, you should configure SSLCACertificateFile (or ...Path) to point to the CA certificate of your internal CA (whether it's a web-based CA or not, on the same site or not). (Of course, keep the private key of your CA private, perhaps configured within your CA web-based application, but Apache Httpd itself doesn't need to know about it.) Browsers will only suggest certificates issued by those CAs or intermediates (unless you've also configured SSLCADNRequestFile, which would be used to send the list of accepted CAs instead).
Note that these two steps (setting up your CA and setting up your website to use client-certificates) really are independent in fact. The fact that both can be part of the same site can be convenient, but isn't necessary. You could try out the Apache Httpd set up without deploying an entire CA on the site first (I'd recommend that, even if it's just to see what you're getting into). There are a number of tools to create your own little CA that are manageable with a handful of certificates: OpenSSL's CA.pl or TinyCA for example. You could also use these test certificates (localhost and testclient, testclient_r is revoked if you want to use the CRL, probably not necessary at first): all passwords are testtest.
As you've already anticipated (with your MySQL DB), you'll need to manage the certificates you issue and map them to users. SSL_CLIENT_M_SERIAL and SSL_CLIENT_I_DN are not the right variables to use, though. SSL_CLIENT_I_DN is the Issuer DN (i.e. the CA's Subject DN). What you'd be looking for is SSL_CLIENT_S_DN: the client cert Subject DN. SSL_CLIENT_M_SERIAL is the certificate serial number: don't use it, since it's unique per certificate: one user could have multiple certificates with the same Subject DN (e.g. if one expires or is revoked).
Despite all this, I'm not sure whether client-certificates are the best way to achieve your goal (letting the employees in your company log on without password).
Firstly, the user should protect their own certificates with a password anyway. What you're really after is some form of Single-Sign On (SSO), I guess.
Secondly, depending on the degree of computer-literacy of your users, certificates can actually be quite difficult to manage.
The fact that the word "certificate", strictly speaking, doesn't include the private key at all, but sometimes implies usage of the private key can be confusing for some. On the one hand, you sometimes hear "Import your certificate into your browser" and "Use your certificate to log in"; on the other hand, you can also hear "send me your certificate". The former implies usage and availability of the private key ("certificate" might just mean .p12 in these expressions). The latter definitely shouldn't involve the private key.
Browser user interfaces tend to be quite poor or confusing for managing the certificates or logging out. Again, if the certificate isn't recognised, the SSL/TLS connection will not be established, so the web server doesn't get a chance to display an HTML error page of any sort.
Perhaps you could also consider other forms of SSO (e.g. CAS, something SAML-based or Kerberos/SPNEGO.)
I have a similar problem with:
CentOS 6.3
Apache 2.2.15
After some tries i recognize my problem.
If I set SSLVerifyClient optional or SSLVerifyClient optional_no_ca and I specify also SSLCACertificateFile or SSLCACertificatePath, Apache acquires the client certificate only if it's released from CA found in the CA reference file/path specified in configuration.
You may have a look to the apache doc if not done already.
The general principle is that you create your self-signed cert and check it before trying to use it.
Then it looks like the client connects to your intranet site through http. From there, there are many different ways to switch to https using your ssl cert. The easiest way is to use the apache rewrite module. But in your case, as you are making php/mysql checks, you may redirect your client from http to to https, which is not the simple way.
In any of both cases (apache automatic redirect through mod_rewrite, or redirection by cascading tests (php/javascript/html), you need to set up your 2 vhosts (one for http and one for https) in the proper way, but this assumes some hypothesis.
For example (debian - apache 2.2), here is an automatic redirect, done by Apache (eg 1st case described above) :
cat /etc/apache2/sites-available/test
# VHOST test
<VirtualHost *:80>
DocumentRoot /home/www/test
ServerName www.test.dev
# ######################
# Redirect commons
# ######################
RewriteEngine on
# Case of vhosts
RewriteOptions Inherit
# ######################
# Redirect (empty index)
# ######################
# Condition 1 to redirect : request matching with the server
RewriteCond %{HTTP_HOST} ^www\.test\.dev [NC]
# Condition 2 to redirect : non empty HOST
RewriteCond %{HTTP_HOST} !^$
# Automatic Empty requests Redirect
RewriteRule ^(.*)/$ /index.php
# ######################
# Redirect to SSL
# ######################
RewriteCond %{HTTP_HOST} ^www\.test\.dev [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{SERVER_PORT} ^80$
RewriteCond %{REQUEST_URI} /
# Redirection
RewriteRule ^/(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</VirtualHost>
The second virtual host for SSL :
cat /etc/apache2/sites-available/test-ssl
# VHOST for ssl
DocumentRoot "/home/www/test"
ServerName www.test.dev
# SSL
SSLEngine on
SSLCACertificateFile /etc/apache2/ssl/cur_cert/ca.pem
SSLCertificateFile /etc/apache2/ssl/cur_cert/serveur.pem
SSLCertificateKeyFile /etc/apache2/ssl/cur_cert/serveur.key
<Directory "/home/www/test">
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from 127.0.0.1 192.168.0.0/16
</Directory>
<Directory "/home/www/test/cgi-bin">
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from 127.0.0.1 192.168.0.0/16
Options +ExecCGI
AddHandler cgi-script .cgi
</Directory>
</VirtualHost>
Your case might defer slightly from this, eg you will not have the redirect portion in the 1st vhost, but only a simple vhost and the second one for https (ssl). The redirection will be done by php/javascript once you have achieved your mysql checks.
Here is an example abstract from a php class for the way to cascade the switch from http to https, using php, then javascript, then html :
public function Redirect($url){
if (TRUE !== Validator::isValidURL($url))
die ("FATAL ERR: url not valid");
// PHP ABSOLUTE URL REDIRECT (HTTP1.1)
if (!headers_sent()) {
header("Status: 200");
header("Cache-Control: no-cache, must-revalidate"); // required for HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // past Date
header("Pragma: no-cache");
header('Location: '.$url); // note: 302 code return by default with "Location"
flush();
exit();
// if headers are already sent... do javascript redirect... if javascript is disabled, do html redirect.
} else {
// Js redirect
echo '<script type="text/javascript">';
//echo "<!--";
echo 'document.location="'. $url .'";';
//echo "//-->";
echo '</script>';
// HTML redirect if js disabled
echo '<noscript>';
echo '<meta http-equiv="refresh" content="0;url="'.$url.'" />';
echo '</noscript>';
exit();
}
return FALSE;
} /* end of method (redirect) */
Hope it helps you to better understand how to proceed and adapt this approach to your specific case.

Server Signuture turn off need access to php.ini

I want to add the following settings to my server:
ServerSignature Off
ServerTokens Prod
However after research I have to add these settings in my httpd.conf or apache2.conf file. It wont work in my php.ini or .htaccess on my public webroot. If I have not got access to these two server files (httpd.conf or apache2.conf) how can I get access or is there an alternative way to get these settings to work. It is a security issue I need to sort out ASAP. Thanks
No, you can't configure Apache (as these are apache settings) via php nor any other language.
You may hide these values from showing on error pages with a little of mod_rewrite that any request to your server or web application gets directed to a php script that outputs whatever error you want.
These values are also shown in the http response headers that apache sends to the browser, so maybe you can overwrite them with php via the header function, using the optional param $bool_replace = true (which is the default value):
header("Server: IIS/6.0", true); // this will fool observers
header("X-Powered-By: Feces-Throwing-Monkey 3.14", true);
edit: Judging from a comment in php's header documentation, this works, and you should also overwrite the X-Powered-By header.

Nginx: Location header sent by PHP redirects withoung changing domain

I'm having a strange problem here and can't figure the cause. I have a php-script on an nginx server which triggers a redirect to a different (sub)domain on the same server:
For instance:
foo.domain.com/redirect.php
header("Status:301");
header("location:http://www.domain.com/new_url/");
The result is that I'm getting redirected to:
foo.domain.com/new_url
The domain doesn't change at all although my response headers look fine .
Any ideas?
Ok, I finally found the cause for my troubles. Neither Nginx, nor PHP caused the issues. My webservers are behind a loadbalancer running with Pound.
Pound has a feature to rewrite domains inside header redirects (enabled by default). We now turned this feature off and all redirects finally work as expected!
Check your containing your site in an iframe,
Some hosts can do this if its free hosting, all so some domains setup allows the site to be contained in an iframe witch would cause the properly your describing

Categories