How to allow only specific pages to be included - php

Yesterday I asked a question about how to include files passed in via the URL, and someone give me this:
if (isset($_GET['file'])){
include($_GET['file'].'.php');
}
But one of the answers told me to do something with this to avoid possible attacks from hackers or something like that. The problem is that I don't understand how to do it myself.
He said I should do something like this:
$pages_array=('home','services','contact').
And then check the GET var:
if(!in_array($_GET['page'], $pages_array) { die(); }
What does this do, and how do I integrate it into my original code above?

Your original code is looking for a file parameter in the URL, and then including whatever file was passed in. So if somebody goes to your PHP page and adds ?file=something.txt to the URL, you'll include the contents of something.txt in your output.
The problem with this is that anybody can manually modify the URL to try to include whatever file they want - letting them see files on your system that should be private.
The solution is to have a list of allowed filenames, like this:
$pages = array('home', 'services', 'contact');
And then before you include the file, check that it's one of the allowed filenames first.
$pages = array('home', 'services', 'contact');
if (isset($_GET['file'])){
if (!in_array($_GET['file'], $pages_array)) {
exit('Not permitted to view this page');
}
include($_GET['file'].'.php');
}
We're using a PHP array to define the list of allowed pages, checking if our page is in the list with the in_array() function, and then stopping all script execution if it's not in the list with the exit() function.

The code checks the GET information passed from the browser to your PHP page by making sure that the page name is present in your $pages_array.
As long as you list all of the pages in your $pages_array, the code will execute. If the page is not in your array list, then it will die and not be executed.
When using GET it is always beneficial to validate the code sent in this way, as arbitrary statements can be sent and executed without validation.
The code, in this instance, is being validated - so you have taken the necessary steps; as long as there is nothing more to the code that you haven't submitted.
Correct code
$pages_array=array('home','services','contact');

You almost answered your own question...
Except this line becomes...
$pages_array=array('home','services','contact');
Instead of what you had...
$pages_array=('home','services','contact').

//change the variable array declaration
$newArray = array('home','services','contact');
Just do an else statement in your if like
else {
//include your file
include($_GET['page'].'.php');
}

Basically, Your syntax for an array definition is wrong, but also why die() if $_GET['file'] is not set? would it not be better if you reverted to a default so as to fail silently.
Using in_array()
<?php
$pages_array = array('home','services','contact');
if(isset($_GET['file']) && in_array($_GET['file'], $pages_array)){
include($_GET['file'].'.php');
}else{
include('home.php');
}
?>
Or even using switch() with hard coded values.
<?php
$page = isset($_GET['file']) ? $_GET['file'] : 'home';
switch($page){
case "home" : include($page.'.php'); break;
case "services" : include($page.'.php'); break;
case "contact" : include($page.'.php'); break;
default:
include('home.php');
break;
}
?>

$pages=array('home','services','contact');
if(isset($_GET['page']))
{
$page=$_GET['page'];
if(!in_array($page,$pages))
{
die ('');
}
else {
include($page.'.php');
}
}
So, your links will look like:
yoursite.com/index.php?page=home -> with this tool:
http://www.generateit.net/mod-rewrite/
you can make nicer URL's.

Related

link using $_GET whilst using isset cant find page

Im currently trying to get a link:
<a href='?p=bid?Sale_ID=$Sale_ID'>BID</a>
to work but I keep getting a "Page you are requesting doesn´t exist" message, this page works if i use this link:
<a href='include/bid.php?Sale_ID=$Sale_ID'>BID</a>
this leads me to believe that my problem lies with the isset im using to include pages on link:
<?php
if (isset($_GET['p']) && $_GET['p'] != "") {
$p = $_GET['p'];
if (file_exists('include/'.$p.'.php')) {
#include ('include/'.$p.'.php');
} elseif (!file_exists('include/'.$p.'.php')) {
echo 'Page you are requesting doesn´t exist<br><br>';
}
} else {
#include ('include/login-form.php');
}
?>
Ive tried adding another isset replacing p with q which just throws my pages in to dissaray.
So my question is, is there a way around this?
Thanks
You have two question marks here:
?p=bid?Sale_ID=$Sale_ID
Multiple querystring parameters are separated by ampersand:
?p=bid&Sale_ID=$Sale_ID
The query string you show: ?p=bid?Sale_ID=$Sale_ID is not valid. The structure of a URL with a string is:
filename.extension?first_parameter=first_value&second_parameter=second_value
So, if you want p to indicate which page:
?p=bid&Sale_ID=$Sale_ID
.. use the ampersand (&) to separate your query string values.
Also, please note that the approach you are using to include a file is insecure. What if I sent this:
?p=../../.htpasswd&Sale_ID=0
An attacker could use this method to output the contents of files that you do not wish to expose to the public. Make sure you are checking the value of this variable more carefully before blinding including the file.
I also wants to warn you against using the error suppressor (#). Errors are your friends! You want to know exactly what happens in your code, using the error suppressor prevents critical problems from being brought to your attention. Really -- never, ever use the error suppressor. Instead of #include, use include
I suggest something more like this:
$file_exists = false;
$page = false;
if (
isset($_GET['p']) &&
strlen(trim($_GET['p'])) > 0
){
$page = preg_replace("/[^a-zA-Z0-9 ]/", "", $_GET['p']);
$page = str_replace(" ", "-", $page);
$file_exists = file_exists('include/'.$page.'.php');
if ($file_exists) {
include ('include/'.$page.'.php');
} else {
$page = false;
echo 'Page you are requesting doesn´t exist<br><br>';
}
}
if (!$file_exists ||$page === false)
include ('include/login-form.php');
The first part of the code ensures that the query string value exists and has some content. Then it cleans out any non-alphanumeric characters (this helps prevent exploitation). Then, we check to see if it exists, storing that result in a variable so we can use it again.
If the page exists, the file is included. If not, a "page not found" message is output, and the login form file is included. If no page is specified in the query string, the login form file is included.
Documentation
$_GET - http://php.net/manual/en/reserved.variables.get.php
Query string on Wikipedia - http://en.wikipedia.org/wiki/Query_string
Exploiting PHP File Inclusion - an article about security when using include and $_GET - http://websec.wordpress.com/2010/02/22/exploiting-php-file-inclusion-overview/
preg_replace - http://php.net/manual/en/function.preg-replace.php
str_replace - http://php.net/manual/en/function.str-replace.php
?p=bid "redirects" to your default file, usually index.php. You want it to work in bid.php.
You can set the default file in apache with:
DirectoryIndex index.php bid.php
The other problem is you use multiple ? signs.
?p=bid&Sale_ID=$Sale_ID would work a lot better
Keep in mind that file_exists does not use the include path, so you should be doing this:
if (file_exists( get_include_path() . 'include/'.$p.'.php')) {
More info:
http://ca2.php.net/manual/en/function.file-exists.php

What is the mechanism of "using $_GET instead of a new file"?

I saw some websites that (for example):
if you want to view your message box, then it is: example.com/file.php?action=pm and also if you want to send a message again the address and the file is the same, but the $_GET is different : example.com/file.php?action=sendpm
How does it work?
Is it just:
if ($_GET['action'] == "pm")
{
//lots of html code(divs, forms, etc) paste here for action=pm.
}
else
{
//lots of html code paste here for action=send
}
instead of having files : pm.php, send.php ... ?Or the mechanism is different?
I saw that SMF use this mechanism and also I saw something like this for facebook.com/?ref=something.
How does it work?
The easiest way is this:
$action = isset($_GET['action']) ? $_GET['action'] : 'default';
if(!in_array($action, array('list', 'of', 'allowed', 'pages')))
$action = 'default';
include($action . '.php');
Note that the validation step is incredibly important. Not properly validating the value leads to nasty security holes from users being able to read any files your script can access to users being able to execute arbitrary code.
Putting all code in a single file like you suggested in your question is a bad idea since it results in unreadable code. Especially when mixing PHP and HTML in the same file like your question suggested (tip: use a template engine!).
This is called routing and allows a single entry point to many functions within an single application.
Requests are taken, processed and routed to the correction function. This routing pattern fits well into the Model View Controller (MVC) pattern for organizing code.
Take a look at how the Zend Framework describes MVC and Routing:
http://framework.zend.com/manual/en/learning.quickstart.intro.html#learning.quickstart.intro.mvc
You can have a switch statement. For example
example.com/file.php?action=pm or example.com/file.php?action=sendpm
if (isset($_REQUEST['action'])) {
$param = $_REQUEST['action'];
switch($param) {
case "pm":
// Code goes here
break;
case "sendpm":
// Do something here
break;
default:
// handle the default case
break;
}
} else {
// Handle it here
}
Well there are even other ways to do it !!
Switch is one of the easiest way !!

Trouble defining a variable in PHP?

Alright, so a content page uses this:
$tab = "Friends";
$title = "User Profile";
include '(the header file, with nav)';
And the header page has the code:
if ($tab == "Friends") {
echo '<li id="current">';
} else {
echo '<li>';
}
The problem is, that the if $tab == Friends condition is never activated, and no other variables are carried from the the content page, to the header page.
Does anyone know what I'm doing wrong?
Update:
Alright, the problem seemed to disappear when I used ../scripts/filename.php, and only occurred when I used a full URL?
Any ideas why?
When you include a full URL, you're not including the PHP script -- you're including the HTML it generates. It's just like you went to http://wherever.your.url.goes, but it's done by the server instead of the browser. The script runs in a whole separate process, caused by a separate request from the server to itself, and none of the $variables are shared between the two.
Short version: When you include http://wherever.your.url.goes, $tab will always be blank. If you include the actual file name, the variable will be shared.
Your code as posted should work. How are you actually including that file? Does that happen inside a function? Then you need to use the global statement for it to work. Example:
File 1:
function my_include($file) {
global $tab; // <-- add this
include '/some/path/' . $file;
}
$tab = 'Friends';
my_inlcude('file_2.php');
File 2:
if ($tab == 'Friends') { ... }
Now you see why it's awful practice to post some stubs and skeches instead of the real code
Try to think, Your question is not a rocket science. Include is like copy-pasting code in place of include operator. Go load your inclided URL in browser, copy resulting code and paste it into your first file and see.

How do I use PHP to display one html page or another?

I wanted to use PHP and the if statement, and I wanted to do
if ($variable){
display html page1
}
else {
display html page2
}
How do I do this? An please note, that I do not want to redirect the user to a different page.
--EDIT--
I would have no problem doing that with one of them, but the other file, it would be too much of a hassle to do that.
--EDIT--
Here is the coding so far:
<?PHP
include 'uc.php';
if ($UCdisplay) {
include("under_construction.php");
}
else {
include("index.html");
}
?>
My problem is that it would be really complicated and confusing if I were to have to create an html page for every php page, so I need some way to show the full html page instead of using include("index.html")
if ($variable){
include("file1.html");
}
else {
include("file2.html");
}
The easiest way would be to have your HTML in two separate files and use include():
if ($variable) {
include('page1.html');
}
else {
include('page2.html');
}
using the ternary operator:
include(($variable ? 'page1' : 'page2').'.html');
If you want to avoid creating "an html page for every php page", then you could do something like this, with the "real" content directly inside the PHP page.
<?PHP
include 'uc.php';
if ($UCdisplay) {
include("under_construction.php");
exit;
}
?>
<html>
<!-- Your real content goes here -->
</html>
The idea is this: If $UCdisplay is true, then your under construction page is shown, and execution stops at exit; - nothing else is shown. Otherwise, program flow "falls through" and the rest of the page is output. You'll have one PHP file for each page of content.
You could side-step this issue by moving the code that checks $UCdisplay directly into uc.php; this would prevent you from having to write that same if statement at the top of every file. The trick is to have the code exit after you include the construction page.
For those still looking:
See the readfile(); function in php. It reads and prints a file all in one function.
Definition
int readfile ( string $filename [, bool $use_include_path = false [, resource $context ]] )
Reads a file and writes it to the output buffer.

Dynamic Include Safety

Is there any way to safely include pages without putting them all in an array?
if (preg_match('/^[a-z0-9]+/', $_GET['page'])) {
$page = $_GET['page'].".php";
$tpl = $_GET['page'].".html";
if (file_exists($page)) include($page);
if (file_exists($tpl)) include($tpl);
}
What should I add to make this pretty safe?
I'm doing it this way bacause I don't like having to include stuff that has to be included on all pages. The "include header > content > include footer"-way. I don't wanna use any template engines/frameworks neither.
Thanks.
The weakness in your current implementation is that …
the regular expression just tests the beginning of the string, so “images/../../secret” would pass, and
without further validation, “index” would also be a valid value and would cause a recursion.
To make your implementation safe, it’s a good practice to put everything, that’s intended to be included, in its own directory (e.g. “includes” and “templates”). Based on this, you just have to ensure that there is no way out of this directory.
if (preg_match('/^[a-z0-9]+$/', $_GET['page'])) {
$page = realpath('includes/'.$_GET['page'].'.php');
$tpl = realpath('templates/'.$_GET['page'].'.html');
if ($page && $tpl) {
include $page;
include $tpl;
} else {
// log error!
}
} else {
// log error!
}
Note: realpath returns the absolute path to the given relative path if file exists and false otherwise. So file_exists is not necessary.
Despite what you stated about not wanting to store a list of available pages in an array it is likely going to be the best, non-db, solution.
$availFiles = array('index.php', 'forum.php');
if(in_array($_GET['page'].".php", $availFiles))
{
//Good
}
else
{
//Not Good
}
You could easily build the array dynamicly with either DB queries or by reading a file, or even reading the contents of a directory and filtering out the things you don't want available.
You should never use user supplied information for includes. You should always have some sort of request handler that does this for you. While a regular expression may filter somethings it will not filter everything.
If you do not want your site to get hacked you do not allow your users to control the flow of the application by designating an include.
I agree with Unkwntech. This is such an insecure way to include files into your website, I wish PHP programmers would do away with it altogether. Even so, an array with all possible matches is certainly safer. However, You'll find that the MVC pattern works better and it is more secure. I'd download code igniter and take a tutorial or two, you'll love it for the same reason you wanna use dynamic includes.

Categories