Host Header Attack on Codeigniter - php

i got this issue from IT-Sec, i have read and search thouroghly but i still can't find any actual solution to fix this issue. Here it is.
"HTTP Host header can be controlled by an attacker. This can be exploited using web-cache poisoning and by abusing alternative channels. Pentester try to request with modify header host. and the response result showing with the modify host header. affected files:
app/formulir
app/kompensasi
app/panduan-agen
app/produk-dan-layanan
app/tentang
app/tentang-
app/training
The impact of this vulnerability
An attacker can manipulate the Host header as seen by the web application and cause the application to behave in unexpected ways."
This is the header sc :
header
Recommended solution thus far is :
The web application should use the SERVER_NAME instead of the Host header
This app are running on xampp with reverse proxy setting for testing. I already do 3 changes to config.php, but the issue is still there. Here is the code.
if(isset($_SERVER[SERVER_NAME])) {
$config['base_url'] = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https' : 'http';
$config['base_url'] = '://'. $_SERVER['SERVER_NAME'];
$config['base_url'] = str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);
}
else{
$config['base_url'] = '';
}
and 2 :
$config['base_url'] = 'http://$_SERVER[SERVER_NAME]';
and 3 :
$config['base_url'] = 'https://jktdc.*********.com/app'
What im asking is, how/where/what exactly i have to change/add to fix this issue. Not a bashing. Thanks a lot.

The answer is
$url = ''
$config[base_url] = $url
so it will accept whatever the servername is.

Related

Codeigniter echoing [::1] instead of localhost

I am using CodeIgniter 3 as a web platform and trying to import semantic-UI CSS into my page. I'm doing so by using CodeIgniter's base_url() method in the href property for the CSS import.
However, semantic.css itself imports some other fonts on my server, which cannot load because of Cross-Origin resource sharing policy. This is the error message chrome gives me:
Font from origin 'http://[::1]' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
This is because base_url() echoes the domain has been [::1] and not localhost as I've typed into the browser.
For some reason, it appears to me that chrome (and also Edge) does not consider [::1] and localhost as the same host, or maybe I'm just being dumb. What I know though is that if I change the path of the main semantic.css file and complex code localhost into it, it works, and it also works if, instead of requesting my page using localhost, I use [::1]
I've done other projects very similar to this and never had this "[::1]" appear. What exactly is causing PHP to echo such a path?
It's because of your base_url is empty.
In config/config.php
$config['base_url'] = 'http://localhost/project_name';
Something more interesting about http://\[::1\]/
You need to edit your $config['base_url'] as follows,
$config['base_url'] = '';
$config['base_url'] = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") ? "https" : "http");
$config['base_url'] .= "://" . $_SERVER['HTTP_HOST'];
$config['base_url'] .= str_replace(basename($_SERVER['SCRIPT_NAME']), "", $_SERVER['SCRIPT_NAME']);
File location: codeigniter/application/config/config.php
Use above code to get dynamic url.
More accurate and dynamic way
$root = "http://".$_SERVER['HTTP_HOST'];
$root .= dirname($_SERVER['SCRIPT_NAME']);
$config['base_url'] = $root;
Though you can still use port.
In order to use base_url(); you must first have the URL Helper loaded. This can be done either in application/config/autoload.php (on or around line 67): or you can manually using
$this->load->helper('url');
than set the
$config['base_url'] = 'http://localhost/your_site_url';
i think it will help you
This is what you need to alter in config/config.php, it works properly in "localhost" as well as in your "server":
$config['base_url'] = "http://".$_SERVER['SERVER_NAME'];
$config['base_url'] .= str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']);
if(!defined('DOCUMENT_ROOT')) define('DOCUMENT_ROOT',str_replace('application/config','',substr(__FILE__, 0, strrpos(__FILE__, '/'))));
$config['base_path'] = constant("DOCUMENT_ROOT");
$config['js_url'] = $config['base_url'].'js/';
$config['css_url'] = $config['base_url'].'css/';
$config['image_url'] = $config['base_url'].'img/';
// Host resolution for cross origin requests
if(ENVIRONMENT == 'production') {
$config['host'] = 'www.<domain_name>.com';
} else {
$config['host'] = 'localhost';
}

Make WordPress Available from any domain

I'm trying to create a plugin that will allow WordPress to be accessed from any domain, of course provided that the domain is pointed to it.
I have filter hooks for option_siteurl and option_home which is proving to be useful in almost all cases.
However, it doesn't appear to be working for images that are attached to a post nor for header images of themes. It looks like for these, it's taking the database value of options -> siteurl.
I've tried update_option, but that hasn't done the trick either.
I'm using the following code to get the host:
public function getGoodURL() {
$scheme = ($_SERVER["SERVER_PORT"] == 80 ? "http://" : "https://");
$host = $_SERVER["HTTP_HOST"];
return $scheme.$host;
}
Thanks!
Might want to try putting the site url configuration in the config file i.e.:
$domain = sprintf('%s://%s',
$_SERVER['SERVER_PORT'] == 80 ? 'http' : 'https',
$_SERVER['SERVER_NAME']);
define('WP_SITEURL', $domain);
define('WP_HOME', $domain);
That way, your site will always accept the current domain.

Get current domain

I have my site on the server http://www.myserver.uk.com.
On this server I have two domains:
one.com and two.com
I would like to get the current domain using PHP, but if I use $_SERVER['HTTP_HOST'] then it is showing me
myserver.uk.com
instead of:
one.com or two.com
How can I get the domain, and not the server name?
Try using this:
$_SERVER['SERVER_NAME']
Or parse:
$_SERVER['REQUEST_URI']
Reference: apache_request_headers()
The best use would be
echo $_SERVER['HTTP_HOST'];
And it can be used like this:
if (strpos($_SERVER['HTTP_HOST'], 'banana.com') !== false) {
echo "Yes this is indeed the banana.com domain";
}
This code below is a good way to see all the variables in $_SERVER in a structured HTML output with your keywords highlighted that halts directly after execution. Since I do sometimes forget which one to use myself - I think this can be nifty.
<?php
// Change banana.com to the domain you were looking for..
$wordToHighlight = "banana.com";
$serverVarHighlighted = str_replace( $wordToHighlight, '<span style=\'background-color:#883399; color: #FFFFFF;\'>'. $wordToHighlight .'</span>', $_SERVER );
echo "<pre>";
print_r($serverVarHighlighted);
echo "</pre>";
exit();
?>
The only secure way of doing this
The only guaranteed secure method of retrieving the current domain is to store it in a secure location yourself.
Most frameworks take care of storing the domain for you, so you will want to consult the documentation for your particular framework. If you're not using a framework, consider storing the domain in one of the following places:
   Secure methods of storing the domain   
  Used By
A configuration file  
Joomla, Drupal/Symfony
The database  
WordPress
An environmental variable
Laravel  
A service registry  
Kubernetes DNS
The following work... but they're not secure
Hackers can make the following variables output whatever domain they want. This can lead to cache poisoning and barely noticeable phishing attacks.
$_SERVER['HTTP_HOST']
This gets the domain from the request headers which are open to manipulation by hackers. Same with:
$_SERVER['SERVER_NAME']
This one can be made better if the Apache setting usecanonicalname is turned off; in which case $_SERVER['SERVER_NAME'] will no longer be allowed to be populated with arbitrary values and will be secure. This is, however, non-default and not as common of a setup.
In popular systems
Below is how you can get the current domain in the following frameworks/systems:
WordPress
$urlparts = parse_url(home_url());
$domain = $urlparts['host'];
If you're constructing a URL in WordPress, just use home_url or site_url, or any of the other URL functions.
Laravel
request()->getHost()
The request()->getHost function is inherited from Symfony, and has been secure since the 2013 CVE-2013-4752 was patched.
Drupal
The installer does not yet take care of making this secure (issue #2404259). But in Drupal 8 there is documentation you can you can follow at Trusted Host Settings to secure your Drupal installation after which the following can be used:
\Drupal::request()->getHost();
Other frameworks
Feel free to edit this answer to include how to get the current domain in your favorite framework. When doing so, please include a link to the relevant source code or to anything else that would help me verify that the framework is doing things securely.
Addendum
Exploitation examples:
Cache poisoning can happen if a botnet continuously requests a page using the wrong hosts header. The resulting HTML will then include links to the attackers website where they can phish your users. At first the malicious links will only be sent back to the hacker, but if the hacker does enough requests, the malicious version of the page will end up in your cache where it will be distributed to other users.
A phishing attack can happen if you store links in the database based on the hosts header. For example, let say you store the absolute URL to a user's profiles on a forum. By using the wrong header, a hacker could get anyone who clicks on their profile link to be sent a phishing site.
Password reset poisoning can happen if a hacker uses a malicious hosts header when filling out the password reset form for a different user. That user will then get an email containing a password reset link that leads to a phishing site. Another more complex form of this skips the user having to do anything by getting the email to bounce and resend to one of the hacker's SMTP servers (for example CVE-2017-8295.)
Here are some more malicious examples
Additional Caveats and Notes:
When usecanonicalname is turned off the $_SERVER['SERVER_NAME'] is populated with the same header $_SERVER['HTTP_HOST'] would have used anyway (plus the port). This is Apache's default setup. If you or DevOps turns this on then you're okay -- ish -- but do you really want to rely on a separate team, or yourself three years in the future, to keep what would appear to be a minor configuration at a non-default value? Even though this makes things secure, I would caution against relying on this setup.
Red Hat, however, does turn usecanonical on by default [source].
If serverAlias is used in the virtual hosts entry, and the aliased domain is requested, $_SERVER['SERVER_NAME'] will not return the current domain, but will return the value of the serverName directive.
If the serverName cannot be resolved, the operating system's hostname command is used in its place [source].
If the host header is left out, the server will behave as if usecanonical
was on [source].
Lastly, I just tried exploiting this on my local server, and was unable to spoof the hosts header. I'm not sure if there was an update to Apache that addressed this, or if I was just doing something wrong. Regardless, this header would still be exploitable in environments where virtual hosts are not being used.
A Little Rant:
     This question received hundreds of thousands of views without a single mention of the security problems at hand! It shouldn't be this way, but just because a Stack Overflow answer is popular, that doesn't mean it is secure.
Using $_SERVER['HTTP_HOST'] gets me (subdomain.)maindomain.extension. It seems like the easiest solution to me.
If you're actually 'redirecting' through an iFrame, you could add a GET parameter which states the domain.
<iframe src="myserver.uk.com?domain=one.com"/>
And then you could set a session variable that persists this data throughout your application.
Try $_SERVER['SERVER_NAME'].
Tips: Create a PHP file that calls the function phpinfo() and see the "PHP Variables" section. There are a bunch of useful variables we never think of there.
To get the domain:
$_SERVER['HTTP_HOST']
Domain with protocol:
$protocol = strpos(strtolower($_SERVER['SERVER_PROTOCOL']), 'https') === FALSE ? 'http' : 'https';
$domainLink = $protocol . '://' . $_SERVER['HTTP_HOST'];
Protocol, domain, and queryString total:
$url = $protocol . '://' . $_SERVER['HTTP_HOST'] . '?' . $_SERVER['QUERY_STRING'];
**As the $_SERVER['SERVER_NAME'] is not reliable for multi-domain hosting!
I know this might not be entirely on the subject, but in my experience, I find storing the WWW-ness of the current URL in a variable useful.
In addition, please see my comment below, to see what this is getting at.
This is important when determining whether to dispatch Ajax calls with "www", or without:
$.ajax("url" : "www.site.com/script.php", ...
$.ajax("url" : "site.com/script.php", ...
When dispatching an Ajax call the domain name must match that of in the browser's address bar, and otherwise you will have an Uncaught SecurityError in the console.
So I came up with this solution to address the issue:
<?php
substr($_SERVER['SERVER_NAME'], 0, 3) == "www" ? $WWW = true : $WWW = false;
if ($WWW) {
/* We have www.example.com */
} else {
/* We have example.com */
}
?>
Then, based on whether $WWW is true, or false run the proper Ajax call.
I know this might sound trivial, but this is such a common problem that is easy to trip over.
Everybody is using the parse_url function, but sometimes a user may pass the argument in different formats.
So as to fix that, I have created a function. Check this out:
function fixDomainName($url='')
{
$strToLower = strtolower(trim($url));
$httpPregReplace = preg_replace('/^http:\/\//i', '', $strToLower);
$httpsPregReplace = preg_replace('/^https:\/\//i', '', $httpPregReplace);
$wwwPregReplace = preg_replace('/^www\./i', '', $httpsPregReplace);
$explodeToArray = explode('/', $wwwPregReplace);
$finalDomainName = trim($explodeToArray[0]);
return $finalDomainName;
}
Just pass the URL and get the domain.
For example,
echo fixDomainName('https://stackoverflow.com');
will return:
stackoverflow.com
And in some situation:
echo fixDomainName('stackoverflow.com/questions/id/slug');
And it will also return stackoverflow.com.
This quick & dirty works for me.
Whichever way you get the string containing the domain you want to extract, i.e. using a super global -$_SERVER['SERVER_NAME']- or, say, in Drupal: global $base_url, regex is your friend:
global $base_url;
preg_match("/\w+\.\w+$/", $base_url, $matches);
$domain = $matches[0];
The particular regex string I am using in the example will only capture the last two components of the $base_url string, of course, but you can add as many "\w+." as desired.
Hope it helps.

How to make a website in PHP work both in HTTP and HTTPS?

I have a website that was written assuming http:// is one and only protocol forever. Now i bought a SSL certificate but when i visit site calling it with https:// i get info in browsers that part of site is insecure. As i found i have some JS, CSS and images and files that i refer to using http:// in the HTML of the site.
So what is best practice to enable full https? Should i change my website in every place when i refer to image, CSS or JS, check if site was loaded with http or https and load the resource with according protocol? It seems like a lot of work for me and bit error prone. Is there any other way, easier to make the whole site fully secure?
Rather than linking to your css, js, and images with http://yoursite.com/css/file.css just use relative paths such as /images/image.jpg and /css/file.css this way it will work with both http and https, also if you change domains or copy your content to another domain, you shouldn't have to change all those links either.
Use relative paths. If you are pointing to something that is on the same site as yours, then you should not be using http://
If for some reason you still need to have http:// then just switch them all to https://. An http:// will never complain because it is pointing to https:// stuff, but an https:// page will complain if it is pointing to non-https stuff.
If you are pointing to content outside of your control, on another site for example, then you need to hope that you can get at that content via https instead. If you can't, then you're hosed and you either need to live with the error, get the content from somewhere else, or proxy the content through your own https connection.
To complement #drew010 's answer, you could use other domains and still refer to the current protocol with //, something like:
<img src="/pics/home.png" />
<img src="//my-cdn.com/pics/info.png" />
The latter example will point to https://.. from https://your-site.com and http://... from http://your-site.com.
the best practice would be either using relative path rather than absolute but sometimes absolute is a better option so you can do the following :
as I can imagine you have a file called config.php or common.php (a file that stores your common used vars and you include it in every page), so put this code there :
function selfURL() {
$s = empty($_SERVER["HTTPS"]) ? ''
: ($_SERVER["HTTPS"] == "on") ? "s" : "";
$protocol = strleft(strtolower($_SERVER["SERVER_PROTOCOL"]), "/").$s;
$port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
return $protocol."://".$_SERVER['SERVER_NAME'].$port.$_SERVER['REQUEST_URI'];
}
function strleft($s1, $s2) {
return substr($s1, 0, strpos($s1, $s2));
}
and then you can assign a var called $http to get the value of the function like :
$http = selfURL();
and then whenever you want to include anything like images, css, etc do something like :
<img src="<?=$http?>images/sample.png" />
this method is reliable as it works in any situation.

Self-referential URLs

What's the most reliable, generic way to construct a self-referential URL? In other words, I want to generate the http://www.site.com[:port] portion of the URL that the user's browser is hitting. I'm using PHP running under Apache.
A few complications:
Relying on $_SERVER["HTTP_HOST"] is dangerous, because that seems to come straight from the HTTP Host header, which someone can forge.
There may or may not be virtual hosts.
There may be a port specified using Apache's Port directive, but that might not be the port that the user specified, if it's behind a load-balancer or proxy.
The port may not actually be part of the URL. For example, 80 and 443 are usually omitted.
PHP's $_SERVER["HTTPS"] doesn't always give a reliable value, especially if you're behind a load-balancer or proxy.
Apache has a UseCanonicalName directive, which affects the values of the SERVER_NAME and SERVER_PORT environment variables. We can assume this is turned on, if that helps.
I would suggest that the only way to be sure and to be secure is to define a constant for the url in some kind of config file for the site. You could generate the constant with $_SERVER['HTTP_HOST'] as a default and replace with a hard coded definition on deployments where security really matters.
define('SITE_URL', $_SERVER['HTTP_HOST']);
and replace as needed:
define('SITE_URL', 'http://foo.bar.com:8080/');
As I recall, you want to do something like this:
$protocol = 'http';
if ( (!empty($_SERVER['HTTPS'])) || ($_SERVER['HTTPS'] == 'off') ) {
$protocol = 'https';
if ($_SERVER['SERVER_PORT'] != 443)
$port = $_SERVER['SERVER_PORT'];
} else if ($_SERVER['SERVER_PORT'] != 80) {
$port = $_SERVER['SERVER_PORT'];
}
// Server name is going to be whatever the virtual host name is set to in your configuration
$address = $protocol . '://' . $_SERVER['SERVER_NAME'];
if (!empty($port))
$address .= ':' . $port
$address .= $_SERVER['REQUEST_URI'];
// Optional, if you want the query string intact
if (!empty($_SERVER['QUERY_STRING']))
$address .= '?' . $_SERVER['QUERY_STRING'];
I haven't tested this code, because I don't have PHP handy at the moment.
The most reliable way is to provide it yourself.
The site should be coded to be hostname neutral, but to know about a special configuration file. This file doesn't get put into source control for the codebase because it belongs to the webserver's configuration. The file is used to set things like the hostname and other webserver-specific parameters. You can accomodate load balancers, changing ports, etc, because you're saying if an HTTP request hits that code, then it can assume however much you will let it assume.
This trick also helps development, incidentally. :-)
$_SERVER["HTTP_HOST"] is probably the best way, after some validation of course.
Yes, the user specifies it and so it cannot be trusted, but you can easily detect when the user is playing games with it.
One idea for validating that $_SERVER['HTTP_HOST'] is valid could be to validate it by DNS. I've used this method in one or two cases without serious consequences to speed and I believe this method fails silently if provided a IP address.
http://www.php.net/manual/en/function.gethostbyname.php
Peusudo code might be:
define('SITEHOME', in_array(gethostbyname($_SERVER['HTTP_HOST']), array(... valid IP's)))
? $_SERVER['HTTP_HOST']
: 'default_hostname';
why {if you wish the user to continue using http:///host:port/ that they are on do you wish to generate full urls}
whan you can use relative urls instead of either
say on page http://xxx:yy/zzz/fff/
you culd use either
../graphics/whatever.jpg
{to go back one directory from current and get http://xxx:yy/zzz/graphics/whatever.jpg
or
/zzz/graphics/whatever.jpg
{to goto site root and work up the directories as specified}
these both avoid mentioning the host:port part and inherit it from the one currently in use

Categories