Problem with including files based on (non-defined) variable - php

i have a PHP site with the following code in it:
<?php
$p = $_GET['p']
include("$p.inc");
?>
Whenever I send a visitor to a page like index.php?p=contact for example I want the file contact.inc to be included. This works fine.
Now I want a certain file to be included (e.g. start.inc) when the visitor is sent to index.php without any GET variables. However, an error message is returned which tells me that $p is undefined (which it logically is).
I tried fixing this problem by using the isset function like so:
<?php
if(!isset($p)) $p = "start";
else $p = $_GET['p'];
include("$p.inc");
?>
but this doesn't work because now $p always contains the string "start" and I can't send the visitor to index.php?p=contact anymore - it will still include start.inc
Can somebody please help me with this issue?
Thanks in advance!

Explicitly specify the allowable values​​, obtained from outside.
<?php
$allowed_pages = array(
'home' => 'home.inc',
'contact' => 'contact.inc',
);
$page = #$_GET['p'];
$file = array_key_exists($page, $allowed_pages) ? $allowed_pages[$page] : $allowed_pages['home'];
include($file);
?>

You should white-list your pages anyway, for security. so:
<?php
$p = $_GET['p']
switch($p){
case 'contact':
include("contact.inc");
break;
default:
include("start.inc");
}
?>

Define your $p variable just like this:
$p = array_key_exists('p', $_GET) ? preg_replace('!\W!', '', $_GET['p']) : 'start';

you're checking $p instead of $_GET['p'] so, as $p is never set, you always land at starting page.
anyway you have to sanitize this variable first.
good practice would be like this (assuming pages stored in a "pagedata" folder and have .php extension):
if(isset($_GET['p'])) {
$p = basename($_GET['p']);
} else {
$p = "start";
}
$fileName = "pagedata/$p.inc.php";
if(is_readable($fileName)) {
include($fileName);
} else {
include("pagedata/404.html");
}

You should prefer an array-map or a switch like Nanne suggested.
At the very least use basename() if you want to keep using the $p variable directly in the include statement. And this is how you could avoid the "error" (which is a debug notice, btw):
<?php
$p = #$_GET["p"] or $p = "start";
$p = preg_replace("/\W+/", "", $p); // minimum filtering
include("./$p.inc");
?>

Thanks to you all!
I combined most of your suggestions to the following piece of code:
<?php
$pages = array(
'start'=>'Start.inc';
'contact'=>'Contact.inc';
'about'=>'About.inc';
};
$p = array_key_exists(#$_GET['p'], $pages) ? preg_replace('!\W!', '', $_GET['p'] : 'start';
$p = ucfirst($p);
$page = "./$p.inc";
if(is_readable($page)) include($page);
else include(./404.);
?>
I particularly like the array-map (as suggested by Alex and mario) for security reasons aswell as the error page idea by Col. Shrapnel.

Related

PHP Array to string conversion with preg_match

I have this error while I'm using this my script:
$pages = array('/about.php', '/');
//...............function text here................//
$ua = $_SERVER['HTTP_USER_AGENT'];
$mobiles = '/iphone|ipad|android|symbian|BlackBerry|HTC|iPod|IEMobile|Opera Mini|Opera Mobi|WinPhone7|Nokia|samsung|LG/i';
if (preg_match($mobiles, $ua)) {
$thispage = $_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"];
if ($thispage == $_SERVER["HTTP_HOST"].$pages) {
ob_start("text");
}
}
This script changes certain pages style depending on user's useragent. I need this script in such way. But I don't know how to make it in PHP properly. Maybe I need some "foreach ($pages as $i)"? But it didn't work in a way I made it.
You are trying to check if the "requested resource" $_SERVER["REQUEST_URI"] is in predefined list of resource paths.
Change your condition as shown below(using in_array function):
...
if (in_array($_SERVER["REQUEST_URI"], $pages)) {
ob_start("text");
}

How to place include in other place

I make some function in php, but I am getting stuck
if (isset($_GET['page']) )
{
$open = __DIR__.'/../view/html/'.$_GET['page'].'.php';
if (file_exists($open)){
include $open; //<<<<can i//
}
else {
"echo "The file not found";
}
}
If true, I want to include that file in another place, but how?
I am trying to put the code in where I want, but the __DIR__ is not working as I expected. I don't know how to fix it to become right. Solution cannot be found in the tutorial.
I would use:
if( isset( $_GET['page'] ) ) {
switch( strtolower( $_GET['page') ) ) {
case 'download':
include '../download.php';
break;
case 'blog':
include '../blog.php';
break;
// ... And so on
default:
echo 'File not found';
}
} else {
echo 'No file specified';
}
This way you have full control over which files can be included!
You have to do like this.
Use file_get_contents()
if (file_exists($open)){
file_get_contents($open);
}
The answer to your question is yes, that will work. Whether or not you should use readfile(), file_get_contents() or include depends on the contents of the file. If you have php code in that file, you need either include or require. But this actually brings up another problem.
As mentioned in the comments by #anonymous, you are exposing yourself to an LFI attack. To resolve this, pages should be defined as a whitelisted array. You should then check if the page is in the whitelisted array. If it is not, do not attempt to open that file.
$pages = array(
'page1',
'page2'
);
Then you can make a reference and check if it exists.
if(in_array($_GET['page'], $pages)){
//now check for the file
$open = __DIR__.'/../view/html/'.$_GET['page'].'.php';
if(file_exists($open)){
include $open;
}
} else {
//page does not exist, redirect them elsewhere.
header('Location: http://example.com/404.php');
}

PHP Code Cleaning

Am running the following code to gather some data from my page and store it in my database, however, i need to add some extra functionality to it but i don't seem to be able to do it correctly.
The Code:
// Get Referrer and Page
if (isset($_GET["ref"]))
{
// from javascript
$referer = $_GET["ref"];
$page = ((isset($_SERVER['HTTP_REFERER'])) ? (parse_url($_SERVER['HTTP_REFERER'], PHP_URL_PATH)) : (''));
}
else
{
// from php
$referer = ((isset($_SERVER['HTTP_REFERER'])) ? ($_SERVER['HTTP_REFERER']) : (''));
$page = $_SERVER['PHP_SELF']; // with include via php
}
// Cleanup
if (basename($page) == basename(__FILE__)) $page = "" ;
This script is storing $page as "/site/index.php or /site/about.php", for example. I kinda want it to store it as "Index or About" without the whole /site/ and .php part.
Thanks in advance
Use pathinfo(), for example:
<?php
$page = "/site/index.php";
$page_info = pathinfo($page);
$page_name = $page_info['filename'];
echo $page_name; //output: index
?>

Error message whenever I open the index.php page

I'm working on a website and this is what I have for my index.php:
<?php
$p = $_GET['p'];
$pages = array('home', 'culture', 'design', 'art', 'about');
$path = 'http://localhost:8080/projects';
include('header.php');
if(!isset($p) || !in_array($p, $pages)) {
include('header.index.php');
include('content.index.php');
} else {
switch($p) {
case "home";
include('header.home.php');
include('content.home.php');
break;
case "culture";
include('content.culture.php');
break;
case "design";
include('content.design.php');
break;
case "about";
include('content.about.php');
break;
case "art";
include('content.art.php');
break;
default:
include('content.index.php');
break;
}
}
include('footer.php');
?>
I get the following error:
**Notice: Undefined index: p in C:\wamp\www\projects\index.php on line 3
Call Stack
# Time Memory Function Location
1 0.0523 680200 {main}( ) ..\index.php:0**
When you assign p initially, p is not set in $_GET
So you can do this
$p = isset($_GET['p']) ? $_GET['p'] : null;
If you don't care about notices, You can disable them in your php.ini by changing error_reporting to E_ALL & ~E_NOTICE, however I wouldn't recommend it
The switch statement you have is somewhat bogus, especially as you already have the $page array. You actually want to verify if the page exists or load the index page (probably?):
$p = isset($_GET['p']) ? (string) $_GET['p'] : NULL;
$pages = array('home', 'culture', 'design', 'art', 'about');
$path = 'http://localhost:8080/projects';
if (!in_array($p, $pages)) {
$p = 'index';
}
// include $p based on $path
However, you still have the problem with the header. So this is the lesson: make the header part of every include. You can stack as many includes as you like, just take care that every include contains it's correct header. Then you're done. And you won't see any warnings.
So the code after following what #hakre suggested should look like this:
$p = isset($_GET['p']) ? (string) $_GET['p'] : NULL;
$pages = array('home', 'culture', 'design', 'art', 'about');
$path = 'http://localhost:8080/projects';
include('header.php');
if (!in_array($p, $pages)) {
$p = 'index';
include('header.index.php');
include('content.index.php');
}
Thanks #hakre for your help..
Just a suggestion maybe try !empty()
if(!empty($p) || !in_array($p, $pages)) {
include 'header.index.php';
include 'content.index.php';
}
This is NOT an error. As the log claims this is a NOTICE. It is meant to inform you about a potential problem, but does not keep the script from being executed.
In this case the interpreter tells you that the array $_GET does not contain an element with index 'p'. It is not initialized, probably cause it has not been specified in the request in this case.
Try to test first if the element exists before you try to access it. Use isset() for this.

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