I have designed a website, and within it I have a range of PHP scripts which interact with my system. For example, if a user uploads an image, this is processed by the script
image.php
and if a user logs in this is processed by the script
login.php
All these scripts are stored in the folder called: scripts
How do I ensure someone cannot access these pages, however still ensure they can be used by the system? I want to ensure the PHP pages will accept post values, get values and can redirect to other pages, but not be directly accessed via the address bar or downloaded?
I attempted to block access using .htaccess using deny from all and Limit GET, POST but this prevented the system from working as I could not access those files at all.
Blocking files with htaccess makes the files inaccessible to the requestor, e.g. the visitor of the page. So you need a proxy file to pass the visitor's request to the files. For that, have a look at the MVC pattern and the Front Controller pattern.
Basically, what you will want to do is route all requests to a single point of entry, e.g. index.php and decide from there, which action(your scripts) is called to process the request. Then you could place your scripts and templates outside the publicly accessible folder or, if that is impossible (on some shared hosts), protect the folders with htaccess like you already did (DENY FROM ALL) then.
To use the upload script you'd have a URL like http://example.com/index.php?action=upload.
A supersimple FrontController is as easy as
$scriptPath = 'path/to/your/scripts/directory/';
$defaultAction = 'action404.php';
$requestedAction = $_GET['action']; // you might want to sanitize this
switch($action) {
case 'upload':
$actionScript = 'image.php';
break;
case 'login':
$actionScript = 'login.php';
break;
default:
$actionScript = $defaultAction;
}
include $scriptPath . $actionScript;
exit;
Your actionScript would then do everything you need to do with the request, including redirection, db access, authentication, uploading stuff, rendering templates, etc - whatever you deem necessary. The default action in the example above could look like this:
<?php // action404.php
header('HTTP/1.1 404 File Not Found');
fpassthru('path/to/template/directory/error404.html');
There is numerous implementations of the FrontController pattern in PHP. Some simple, some complex. The CodeIgniter framework uses a lightweight MVC/FrontController implementation that might not be too overwhelming if this is new to to you.
Like Atli above suggested, you could use mod_rewrite to force all requests to index.php and you could also use it to pretty up your URLs. This is common practice with MVC frameworks and has been covered extensively here and elsewhere.
You can't really prevent direct requests to the files, and still have them remain accessible to other requests. The best you can do is mask their location, and control how they are accessed.
One way you could go is to create a PHP "switch" script, which would include the scripts for you, rather than have Apache request them directly.
For example, if you had your scripts/image.php rule target switch.php?file=image.php instead, somewhat like:
RewriteRule ([^\.]+\.(jpe?g|png|gif)$ switch.php?file=image.php&rw=1&meta=$1 [L,QSA]
You could add deny from all to the scripts/.htaccess file and do this in your switch.php file.
<?php
/** File: switch.php **/
$allowed_files = array(
'login.php',
'image.php'
);
$script_dir = 'scripts/';
if(isset($_POST['rw']) && in_array($_REQUEST['file'], $allowed_files)) {
include $script_dir . $allowed_files[$_REQUEST['file']];
}
else {
header('HTTP/1.1 404 File Not Found');
include 'error404.html'; // Or something to that effect.
}
?>
The $_POST['rw'] there is a weak check, to see if the rule came from a RewriteRule, meant to prevent direct requests to the file. Pretty easy to bypass if you know it is there, but effective against random requests by bots and such.
This way, direct requests to either scripts/image.php and switch.php?file=image.php would fail, but requests to any image file would trigger the scripts/image.php script.
You can set deny from all on .htaccess and include these files from some accessible directory
I want to ensure the PHP pages will accept post values, get values and can redirect to other pages, but not be directly accessed via the address bar or downloaded?
As long as Apache is configured to associate all .php files with the PHP application, no one can download the PHP content itself. So, if someone browsed to "mysite.com/image.php", PHP will run. The user will NOT see your PHP content.
This should already by done in your httpd.conf file as :
AddType application/x-httpd-php .php .phtml
Now, image.php will be expecting certain post parameters. Short of implementing an MVC architecture as Atli suggested above, you could gracefully and securely deal with any missing parameters if they aren't provided. Then, users can get to the page directly but not do anything with it.
A lot of applications just put files like your scripts not in the public (like /public_html/ or /www/) folder but in the same root folder as your public folder.
so not
root/public_html/ and
root/public_html/scripts/
but
root/public_html/ and
root/scripts/
Anything in a folder above the public folder can't be accessed by visitors, but by specifying in for example /public_html/index.php the file '../scripts/yourscript.php' PHP can access these files and visitors can't. (the folder ../ means "go up one step in the folder hierarchy")
Related
I've worked with laravel earlier this year and I liked the idea of using url to access REST client. I was wondering how I'd be able to redirect from non-existant URL to an existing url and keeping that GET data.
Is it even possible with the current php implementation?
Thanks in advance!
EDIT:
For example, let's say that I want to allow logged in users download a file. Said file would be downloaded from a following url: http://example.ex/image.png
But if I just put my image.png into the root of my webserver, it'd be downloadable by anyone, as it's a direct link. How would I use the same URL, but to supply index.php with $_GET['url'] = image.png, so I could give a temporary download link to a user if the user is logged in.
You can do it like this (in PHP) I don't code Laravel
$arr_url = parse_url('http://example.com/old_location?foo=bar');
$query = !empty($arr_url['query']) ? '?'.$arr_url['query'] : '';
header("Location: http://example.com/new_location{$query}");
exit;
OR you can use $_SERVER ( I forget if it includes the ?)
$query = !empty($_SERVER['QUERY_STRING']) ? '?'.$_SERVER['QUERY_STRING'] : '';
header("Location: http://example.com/new_location{$query}");
Two important things.
no output before calling header not so much as a single space
call exit after using header.
from non-existant URL
As I said I don't use Laraval, so I can't speak on to how it handles 404's so you would need to do the redirection in the controller that handles that, which would require some logic to decide to redirect or not.
UPDATE
I want to do it in PHP, but this doesn't really help if my URL ending with image.png isn't a valid php file.
Saying access REST implies that it will be a PHP file. If you just mean finding if a file exists and redirecting then use HTACCESS
#if not is a real file
RewriteCond %{REQUEST_FILENAME} !-f
//redirect
How you decide where to redirect is an open question, as this is to broad for me to give a specific answer.
One last thing is your .png example. It's possible to use a .php file to return image data, with the correct headers. I once built a site that stored user images in a Zip file and then only displayed images if a user was logged in, by streaming the contents of the file from the ZIP (no temp files).
Everything the server returns is text, the only reason it's seen as an image is because Apache knows that .png should have Content-type: image/jpng or something like that for the content header. Something that can be "faked" in PHP.
For a "classic" website, one would create a /foldername/index.php for every web page. With WordPress, however, this is not the case. For example, if a page was created with WordPress whose URI was http://myblog.org/some_page, you would not find the folder www/myblog.org/some_page in your web host's FTP.
My question then, is, How can I serve up pages located at http://[MY_WEBSITE].com/[page_name] for any arbitrary page_name, without creating a new folder for every page_name?
One method would be to use the page_name as parameter to a common file and use that to serve the contents of the required page.
That behaviour is handled (in an Apache server) by a .htaccess file, wherein rewrite rules are defined. Rewrite rules basically capture incoming traffic and directs those requests to a file on the server (typically a single page which will act as a router).
The router is then responsible for taking the input URI (usually via $_SERVER["REQUEST_URI"] in PHP) and working out what to do with it, and ultimately what the output will be for that request.
As for a decent router, you could look at klein.php. Also, a brief example:
# htaccess file
RewriteEngine On
RewriteRule ^[^\.]+$ index.php
And the index.php:
$route = $_SERVER["REQUEST_URI"];
if($route === '/home')
{
echo 'This is the homepage';
}
You tell your server to rewrite the URL. Most servers do it in their own way, so to find out how to do it look at your server's documentation.
Wordpress uses templates that make use of the require() function and a foreach loop commonly called "The Loop" to retreive content.
Different pages are called using different templates. If you want to know exactly how this logic is calculated, look into this.
As a precaution I am wanting to use PHP to create an easily reusable/modifiable means to redirect users to a specified URL.
I have the usual php header redirect:
<?php
header( 'Location: http://www.stackoverflow.com/' ) ;
?>
What I'd prefer to do however, as this file will be placed across a lot of directories, to make life easier, and have the url extracted from an external file, e.g.
http://www.stackoverflow.com/url.xml
This would of course contain the URL of the website in question, unless there is another way to capture the domain itself automatically? This I'm not sure.
Could anyone be kind enough to show how this would be done or provide the best approach?
Thank you.
I was thinking in the same approach as #adam , i don't recommend you to extract urls from a file because it can be read from an attacker. It's better to include them in a php file as variables, an array or any other data structure.
Store the urls once in a file called config.inc.php:
<?php
define('USER_PATH', '/redirect/user/path');
define('ADMIN_PATH', '/redirect/admin/path');
?>
Then in your php file:
<?php
include 'config.inc.php';
header( "Location: http://{$_SERVER['HTTP_HOST']} . USER_PATH ) ; //or whatever variable you want to use to redirect.
?>
If you want to redirect some folders due to security issues you can do it with an Apache's htaccess.
Just put this at root folder then add the last line as many as you want for each redirection.
Each folder mentioned here can only be accessed by scripts running on the server.
Using this every access request using HTTP gets redirected. So this only works for directories containing scripts to be included. It doesn't work for i.e. image directories whose index shouldn't be shown, but the images should stay accessible.
RewriteEngine on
RewriteBase /
RewriteRule ^includes/(.*) http://www.stackoverflow.com/ [R=301,L]
RewriteRule ^includes/(.*) http://www.stackoverflow.com/ [R=301,L]
If you aren't familiar with this you can use http://www.htaccessredirect.net/
If the file containing the url is on a domain you don't control, there are security risks.
Instead, store the url once in a file called config.inc.php:
<?php
define('REDIRECT_URL', '/redirect/path');
?>
Then include it in your project:
<?php
include 'config.inc.php';
echo REDIRECT_URL;
// /redirect/path
?>
I have use this to generate this code:
<?php
require_once('mobile_device_detect.php');
mobile_device_detect(true,false,true,true, true,true,true,'http://m.mydomain.com',false);
?>
But the only directions are to "copy and paste this code". Um.. copy and paste where? Do I need to create a new php file? Is this index.php? What if I already have an index.html file?
EDIT: I understand that I put mobile_device_detect.php in the root of mydomain.com. My question is where to put the above php code.
Copy and paste this at the beginning of your PHP based pages that you want to detect the visitors for their device. If your server parses HTML files as PHP which I doubt then add this in your HTML files as well. If you're just building the website then yes you need this in files which are parsed by the PHP engine for example: ".php".
If you paste this in page that is HTML and not parsed by the server you'll see this same code as output which will do nothing. In order to have it working you need it in PHP files.
If your script is well written and well structured you may need to include it in only one place. It all depends how your website is structured.
------ UPDATE ------
Why you shouldn't be using this class? It have a special license which is not absolutely free.
Instead you can use this simple class: https://github.com/serbanghita/Mobile-Detect
Download Mobile_Detect.php
Include the file at the top in your PHP page where you want the device to be checked:
// Include the mobile device detect class
include 'Mobile_Detect.php';
// Init the class
$detect = new Mobile_Detect();
// And here is the magic - checking if the user comes with a mobile device
if ($detect->isMobile()) {
// Detects any mobile device.
// Redirecting
header("Location: http://your_redirected_url.com"); exit;
}
Creating rewrite rules for using html extension.
If you still want to use '.html' as extension just create rewrite rule that will rewrite your .php as .html. Or otherwise said create your_page_name.php and add the PHP code there. Create .htaccess file in the same DIR and add the following lines:
RewriteEngine On
RewriteBase /
RewriteRule ^your_page_name.html/?$ your_page_name.php [L]
Save, close! Now you should be able to use your php page with .html extension. To access your page now just type: http://yourdomain.com/your_page_name.html
Simple as that!
Suggestion: If I was you I'd add the rewrite rules in the web server's config file. It will be faster and more secure. But that's another lesson. If you decide to use this method just search the Stack.
Copy and paste the code anywhere you want. Just make sure the function is defined on any page that needs it.
You should either buy the script mobile_device_detect.php from the site or use a free method called pay with a tweet option.. Go to the download page and you will see them there..
ok, in case this helps someone, here are the details of what's working for me:
Create an index.php file with just this:
<?php
require_once('mobile_device_detect.php');
$mobile = mobile_device_detect();
// redirect all mobiles to mobile site and all other browsers to desktop site
if($mobile==true){
header('Location:http://m.yourdomain.com/');
}else{
header('Location:http://yourdomain.com/index.html');
}
exit;
?>
Drop the mobile_device_detect.php file in the root of your site.
Then, add this line to your .htaccess file:
DirectoryIndex index.php index.html
I am working with PHP5.3.6 on Windows 2008R2.
In Wordpress, I can set all kinds of friendly URLs which ultimately route to one PHP page. From the outside, it looks like many pages, but is only one.
In the web.config file (or .htaccess) file, there is only one instruction as opposed to having one entry per page/article.
This means that somewhere PHP is looking at the URL, comparing it to the database (where the article titles exist) and then routing transparently to the page.
All of this is transparent to the end user.
How is this accomplished in a non wordpress PHP site?
Here's a related answer that touches on how to do that. In brief, you'll want to check the $_SERVER['REQUEST_URI'] and just parse that.
Here's a simple example of parsing the request (MVC routers are usually configurable, and can route and match for many different URI structures):
If your format is something like news/article-slig you can do this (example code, there are less rigid ways to do this):
list($section, $slug) = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
At this point your PHP script knows how to interpret the request. If this were a full blown MVC application, the router would load the matching controller and pass the request data to it. If you're just doing a simple single page script, loading some data, then your DB calls would follow.
If the request is invalid, then a simple header() call can notify the browser:
header('HTTP/1.0 404 Not Found');
And any data output would be the content of your 404 page.
I can't vouch for wordpress, one method I have used is to redirect 404's in .htaccess to index.php and then have that file sort by parsing:
$sub = $_SERVER['SERVER_NAME'];
$file = $_SERVER['REQUEST_URI'];
$sub can be used to mask non existant subdomains to a specific file. $file can be used in a switch or if clause to include / redirect based on file name.
Obviously, you need to make sure that the alias' are not actual files in your doc root.
Its called Routing(you can also check info about Front Controller pattern). You can write your own implementation by redirecting all your requests to single file using server settings and parse request in this file. You can also check, for example, Zend_Controller_Router docs and sources to understand how it works.
http://framework.zend.com/manual/en/zend.controller.router.html
http://framework.zend.com/manual/en/zend.controller.html