Secure Include php - php

Is this secure including? Or is it possible to use some RFI/LFI or what's it called?
$request_uri = explode('/', $_SERVER['REQUEST_URI']);
$script_name = explode('/', $_SERVER['SCRIPT_NAME']);
for ($i = 0; $i < sizeof($script_name); $i++) {
if ($request_uri[$i] == $script_name[$i])
{
unset($request_uri[$i]);
}
}
$command = array_values($request_uri);
if (file_exists('controllers/' . $command[0] . '.php')) {
include 'controllers/' . $command[0] . '.php';
}
update:
if (isset($_GET['p'])) {
$pages = array('home', 'login', 'register');
$page = filter_var($_GET['p'], FILTER_SANITIZE_URL);
if (in_array($page, $pages) && file_exists($page . '.php')) {
include ($page . '.php');
} else {
include ('404.php');
}
}
else {
include ('home.php');
}

Well, if you make sure that $command[0] only contains alphanumeric characters (and possibly underscores and hyphens), that could be made somewhat secure.
Something like the following might do the job:
if (!ctype_alnum($command[0])) {
// Hacking attempt!
exit;
}
But it's still a bad idea to include files based on user input. A better idea would be to use a lookup table that maps URIs to controllers, and use autoload to include the relevant files. That way, there's a strong separation between user input and the include.

Your code feeds any arbitrary string provided by the user to PHP's include; this is bad and pretty much a textbook example of https://www.owasp.org/index.php/Server-Side_Includes_(SSI)_Injection
What problem are you trying to solve that requires this?

Related

php if page doesn't exist display 404

I have a code I'm trying to make work. Excuse my ignorance, I'm trying to learn but I can't find anything that specifically helps me.
What I want to do is:
On my index, display show_news.php
When index.php?page=blank is used, display the contents of blank.php
and if the chosen page doesn't exist I want to display a custom 404 page. Say if blank2.php was used and doesn't exist it includes 404.php
The 404 page is what I'm struggling with.
<?php
$number = 4;
if(!$_GET[page]){
include "show_news.php";
} elseif ($_GET[page]) {
include $_GET[page].".php";
} else {
include "404.php";
}
?>
try to use file_exists() function
http://php.net/manual/ru/function.file-exists.php
<?php
$number = 4;
define('ROOTPATH', __DIR__);
$project_path = ROOTPATH . "/path/to/page/files/";
$page = $_GET["page"];
$is_file_existing = $file_exists($project_path . $page . ".php");
if(!$page){
include "show_news.php";
} elseif ($is_file_existing) {
include $page . ".php";
} else {
include "404.php";
}
?>
I didn't quite get your question.
But here's how I would do it:
if (isset($_GET['page']))
include_once(dirname(__FILE__) . '/' . $_GET['page'] . '.php');
else
include_once(dirname(__FILE__) . '/show_news.php');
Then in your .htaccess file:
RewriteEngine On
RewriteBase /
ErrorDocument 404 /404.php
Edit:
Make use of mysqli_real_escape_string before passing it in to $_GET.

How to do this more efficiently

Currently I'm using this code:
<?php
/*
Clean, Simple, Minimal.
*/
$pages = array('home', 'login', 'register', 'logout', 'test');
if (isset($_GET['p']))
{
$page = $_GET['p'];
if (in_array($page, $pages))
{
include('pages/' . $page . '.php');
}
}
else {
echo 'Hi';
}
?>
I want this to be as secure and efficent as possible, I've seen multiple versions of this. I wasn't sure how to do what i really wanted, i wanted it so, if the requested file .php exists in /pages/ it'll show it, else throw error. But last time i did that i was vulnerable to LFI.

Creating links to embed page

I have created an index.php that serves as a template with a content box. I also have home.php, about.php, and contact.php which only contain the content to fill that content box. This is the code I use to embed pages into that content box:
<?php
if(!$_GET[page]){
include "home.php"; // Page to goto if nothing picked
} else {
include $_GET[page]."php"; // test.php?page=links would read links.php
}
?>
The home page works fine but I am not sure what code to use in the main menu to link to the other pages. I am having a very hard time getting an answer, so I think I may be searching with the wrong terms, which is why I am asking here.
On the main menu for the website, what code do I use in the links so that they get home.php, about.php, or contact.php?
I made the following test:
$page = "test.php?page=links";
$link = explode("=", $page);
echo $link[1].".php"; //gets links.php
So, your code should looks like:
<?php
if(isset($_GET[page])){
$page = $_GET[page];
$link = explode("=", $page);
include $link[1].".php"; // test.php?page=links would read links.php
} else {
include "home.php"; // Page to goto if nothing picked
}
?>
Saludos.
if(!$_GET[page]){
include "home.php"; // Page to goto if nothing picked
} else {
include $_GET[page].".php"; // test.php?page=links would read links.php
}
It was just missing the '.' before the 'php'.
You should use Quotes for arrays though to avoid a Notice (Undefined constant)
Be careful though, you should verify that $_GET['page'] only contains sites you want to make accessible. Otherwise an attacker could just read any file on your server.
if(array_key_exists('page', $_GET)) {
$page = preg_replace('~[^a-z]~', '', $_GET['page']);
include __DIR__ . '/' . $page . '.php';
} else {
include __DIR__ . '/home.php';
}
Better solution (but you have to manually add all the pages):
$page = (array_key_exists('page', $_GET) ? $_GET['page'] : 'home');
switch($page) {
case 'about':
case 'links':
case 'whatever':
include __DIR__ . '/' . $page . '.php';
break;
default:
include __DIR__ . '/home.php';
break;
}
About
?<key>=<value> in the url.
You look up a value in the $_GET-array by using the key.

Differentiate between Subdomains and Subdirectories

I've got a site that is pretty customized, set up on subdomains; sitename.domain.com and it's got some pages (that are the same for ALL subdomains) sitename.domain.com/this-page. Every single site has "/this-page".
We've got someone interested in using some of the stuff we've developed, but they are MARRIED to using subdirectories; domain.com/sitename which would, of course, have domain.com/sitename/this-page as well.
My question is, I've got some code
$sN = 'http://www.' . $_SERVER['HTTP_HOST'];
$PAGE = $sN . '/this-page/';
but of course this does not work for the subdirectory install (it looks for domain.com/this-page/ instead of domain.com/sitename/this-page
is there a way I can differentiate between subdomains and subdirectories?
$setup = "GET THE HOME PAGE, REGARDLESS OF SETUP"
if($setup ( CONTAINS www.X.X.com)) { //do the code above }
else if ($setup ( CONTAINS www.X.com/X)) { //do different code }
[EDIT] Tried the solution from http://www.php.net/manual/en/reserved.variables.server.php#100881 but didn't work for me, so I did this:
<?php
$sitename = 'sitename';
if (strpos($_SERVER['HTTP_HOST'],$sitename)!==false){
echo 'you are in http://'.$_SERVER['HTTP_HOST'] . '/';
// you are in http://sitename.domain.com/
} else {
$path = explode('/',$_SERVER['PHP_SELF']);
unset ($path[count($path)-1]);
echo 'you are in http://' . $_SERVER['HTTP_HOST'] . implode('/',$path) .'/';
// you are in http://www.domain.com/sitename/
}
?>
The answer from #elcodedocle is the better way of doing this. While they came up with that solution, I had ended up with a (yuckier) solution as well.
//Let's grab the current url for other uses
$cur = 'http://www.' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
//Is this subdomains or subdirectories?
$TYPE = explode('.', $cur);
if(isset($TYPE[3])){
//Yay! It's subdomains! not stupid subdirectories!
$this_root = 'http://www.' . $_SERVER['HTTP_HOST'];
} else {
//Boo.... It's stupid subdirectories.... ;o(
$chunks = explode('/', $TYPE[2]);
$this_root = 'http://www.' .$TYPE[1]. '.' .$chunks[0]. '/' .$chunks[1];
}
?>
This works as well, but it's a less graceful solution. I ended up using a slightly modified version of the code above, but for those of you wondering, this is how I got there before reloading SO. :)

Secure way to include page from GET parameter?

I'm working on a set up where the URLs will be along the lines of:
http://example.com/index.php?page=about
In reality they will rewritten to that from a simpler URL. index.php will include another page, using this code:
if ( isset( $_GET['page'] ) )
{
$page = $_SERVER['DOCUMENT_ROOT'] . '/pages/' . $_GET['page'] . '.php';
if ( is_file( $page ) )
include $page;
else
echo 'That page doesn\'t exist.';
}
Assuming everything in the pages folder is perfectly safe to be included, is this code secure? I've protected against the well-known directory hacks, i.e. using page=../../.passwd. Is there anything else I should be mindful of?
probably better to switch-case it
$page_name = $_GET['page'];
switch($page_name) {
case 'about':
$page = $_SERVER['DOCUMENT_ROOT'] . '/pages/about.php';
break;
case 'home': //fall through to default
case default:
$page = $_SERVER['DOCUMENT_ROOT'] . '/pages/home.php';
}
include $page;
This way, there isn't any injection problem.
Edit
Another solution would be to set up a class dedicated to handling the conversion of page name to address.
class Page {
static private $pages = array ("about", "home");
const DEFAULT_PAGE = "home";
static public function includePage($page_name) {
if (!in_array($page_name, self::$pages)) {
$page_name = self::DEFAULT_PAGE;
}
include ($_SERVER['DOCUMENT_ROOT'] . '/pages/'.$page_name.'.php';);
}
}
This way this is all managed inside a single class and future changes are easier to make without digging through other code
edited above to reflect request.
your code is ok, except that you should validate the parameter before use:
if(!preg_match("~^\w+$~", $_GET['page']))
die("page id must be alphanumeric!");
i won't recommend "switch" approach, because it decreases flexibility, which is the whole point of using dynamic includes.
You can also switch to a framework like CodeIgniter that will do it all for you and force you into adopting some coding standards which is always a good thing.
A very secure way to do this would be to first construct a list of directory contents, then match the user input to that list and use the value from the list for the include. Something in the lines of:
$sdir = $_SERVER['DOCUMENT_ROOT'].'/pages/';
$targetfile = $_GET['page'].'.php';
$filenames = scandir($sdir); // returns an array of directory contents
foreach ($files as $filename) {
if (($filename[0] != '.')
&& ($filename == $targetfile)
&& (is_file($sdir.$filename)) {
include $sdir.$filename;
break;
}
}
Or you could do it simply by:
$targetfile = $_GET['page'].'.php';
$sdir = $_SERVER['DOCUMENT_ROOT'].'/pages/';
$filenames = scandir($sdir);
if (in_array($targetfile,$filenames)) {
include $sdir.$filename;
}
But in the latter case you have to be really sure you get the check conditions right, and also use the regex check suggested in another answer. In the first case, you're only including from a list constructed from the directory contents, so it'll be safe even if the user manages to get some weird input through your checks.
When handling an arbitrary number of pages it might be best to ensure you have SEO friendly filenames. I would recommend alphanumeric filenames with hyphens or underscores:
define(DOCROOT, $_SERVER['DOCUMENT_ROOT']);
// assume you do not include file extensions in $_GET['page']
$page = trim(preg_replace('~[^\\pL\d]+~u', '-', $_GET['page']), '-');
if (is_file($page)) {
include DOCROOT . $page;
}

Categories