As an exercise I am rolling my own CMS to better learn php. Currently I have my .htaccess file set to redirect all URLs to the index.php page.
// .htaccess file
RewriteEngine on
RewriteRule !\.(js|gif|jpg|png|css)$ index.php
Once I am at the index.php page, I will need to determine what content the user should see based off the requested URL. For example, if the user visited mysite.com/about then I would be including the file contents of about.php.
My question is: what is the safest way to parse this URL for the file value? I want to avoid the user writing malicious URLs which, among other things, could access private php files I don't want shown.
Your htaccess has really nothing to do with security. I suggest you use switch case like this:
$site = "home";
switch(strtolower($_SERVER['REQUEST_URI'])){
case "about":
$site = "about";
break;
case "contact":
$site = "contact";
break;
}
include "{$site}.php";
or maybe this:
$site = "home";
$page = strtolower($_SERVER['REQUEST_URI']);
switch($page){
case "about":
case "contact":
$site = $page;
break;
}
include "{$site}.php";
Or without having to make list of allowed pages
$path = "pages/";
$page = trim(strtolower($_SERVER['REQUEST_URI']),"/");
$slashPos = strpos($page, "/");
if($slashPos!==false){
$page = substr($page, 0, $slashPos);
}
$page = preg_replace("/[^a-z0-9-]/", "", $page);
if(file_exists("{$path}{$page}.php")){
include "{$path}{$page}.php";
}else{
//include "{$path}home.php";
}
Related
In my header file I have a switch statement set up to set the page titles. I want to keep it all in the header for easy maintenance, instead of adding a page title variable to each page.
My site is setup with the pages separated into folders, so www.example.com/ and www.example.com/about/ and so on. I'm using the following code: (I'm currently building the site in a subfolder called "alt" hence the first case being alt not empty.)
<?php
$base_url=basename(dirname($_SERVER['PHP_SELF']));
$base_page = basename($_SERVER['PHP_SELF'], ".php");
switch ($base_url) {
case "alt":
switch ($base_page) {
case "index": $title = "Home Page"; break;
case "400": $title = "400"; break;
case "403": $title = "403"; break;
case "404": $title = "404"; break;
default: $title = "Error";
}
case "about": $title = "About Page"; break;
default: $title = "Error";
}
?>
But now I'm adding in error pages, like a 404 page and I'm not sure how to set page titles for them since they are all in the main folder with the main index page.
Thanks.
The updated code is now correct. I needed to nest the switch statement and add a break after it.
Using PHP I would like to be able to change every url in a specific HTML page depending on domain.
This code allows me to change urls one by one but I'm sure there is a better way:
$domain = $_SERVER['SERVER_NAME'];
switch ($domain) {
case "example2.com":
$url1 = "http://".$domain."/secondsubfolder/thispage.html";
break;
case "example3.com":
$url1 = "http://".$domain."/thirdsubfolder/thispage.html";
break;
default:
$url1 = "http://example.com/firstsubfolder/thispage.html";
}
HTML:
first url
Notice that I also need to change the first subfolder depending on domain, thats why I can't use relative urls.
So my aim is to be able change every url in my default HTML code:
hello
bye
should change if my website is accessed through example2.com
<a href="example2.com/secondsubfolder/thispage.html" >hello</a>
bye
Your question isn't very clear to me but I guess you need something like:
<?php
$domain = $_SERVER['SERVER_NAME'];
$scriptPath = $_SERVER['SCRIPT_NAME'];
switch ($domain) {
case "example2.com":
$domain = "example2.com";
$folder = "secondsubfolder";
break;
case "example3.com":
$domain = "example3.com";
$folder = "thirdsubfolder";
break;
default:
$domain = "example.com";
$folder = "thirdsubfolder";
}
$scriptPath = preg_replace('%^/.*?/%', "/$folder/", $scriptPath);
echo "http://{$domain}{$scriptPath}";
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.
I usually use this code below to include the page that I need into the body of my website to include the page when clicking on a link.
<?php
switch($_GET['page']){
case '1':
if(file_exists('main.php'))
{
include_once('main.php');
break;
}
default:
include_once('main.php');
break;
}
?>
but then I have to change this everytime i add a menu item by adding a case '2' ... etc
and now my question can this be written shorter/dynamically so that i just can add a link without having to change the piece of code everywhere?
ps: i did made it a little bit shorter.. but its still not good enough i think..
i also want to add this: i get my links from a ini file. i place it in there like this:
[navigation]
main.php = "Home"
if (!isset($_GET['page'])) {
$_GET['page'] = 'main.php';
}
switch ($_GET['page']){
case 'main.php':
case 'about.php':
case 'portfolio.php':
case 'tips.php':
$file = $_GET['page'];
break;
default:
$file = '404.html';
}
include_once $file;
is it possible to get this too from the ini file?
Try this:
$page = isset($_GET['page']) ? $_GET['page'] : "main.php";
if( file_exists($page)) include($page);
else include("404.html");
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;
}