Attach middleware to specific existing route - php

Is there a way to attach middleware to a specific route in Wordpress or just in PHP? I'd like to run a middleware function before allowing access to the uploads folder to check if the user has access to the file before allowing them to download it.
I come from a background in node.js/express so if it helps I'd like to do something like this:
app.use('/wp-content/uploads', function(req, res, next) {
// do stuff with req and call next to continue,
// or use res to end the request early.
});

There are various WordPress plugins for restricting access to content or downloads, mostly based on the logic of been registered or not and what privileges that user has.
Basic logic behind them is like this :
// Redirect guests
function guest_redirect() {
$guest_routes = array(
'member-login',
'member-account',
'member-register',
'member-password-lost',
'member-password-reset'
);
// Force login or registration
if ( !is_user_logged_in() && !is_page($guest_routes) ) {
wp_redirect( 'member-login' );
exit;
}
}
add_action( 'template_redirect', 'guest_redirect' );
Also, WordPress has REST API (official doc here for your need). However, using own custom thing for production site using REST API without properly testing can be risky and complicated to use.

From what I get about your use case from your description, you can solve your problem just using one of the many wordpress plugins already available, for example you can use a "downnload manager" to handle the upload and download of the contents: https://wordpress.org/support/plugin/download-manager
or use something like https://wordpress.org/plugins/easy-digital-downloads/
You can find more plugins here https://wordpress.org/plugins/
If you really want to write something custom, you can add use a RewriteRule to send all the requests for the uploads folder through a "protect.php" script, adding something like this in the .htaccess file
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/(.*)$ protect.php?filename=$1 [L,QSA]
In the protect.php script you can implement you custom logic to allow/forbid the access the the specific "filename"
To use the WP functions inside you custom protect.php file you should include the wp-load.php, more or less like this:
require_once(rtrim($_SERVER['DOCUMENT_ROOT'], '/') . '/wp-load.php');

Related

php include - alternative with better url

i want to build a website with different views, but a stable header and footer - no problem so far. But i dont like the kind of urls i got at the moment with the php GET method.
My site at the moment works like this (what istn working properly):
$_page = $_GET['p'];
if ($_page == "city-sitemap"){ include "views/city-sitemap-view.php"; }
if ($_page == "place"){ include "views/place-view.php"; }
else { include "views/index-view.php";}
this isnt a very sweet solution but i dont know a other for now. I tried to use a mvc framework but failed dramatically. So everytime i add a link i use for example this "index.php?p=place" - not very nice.
The including of the views isn very smart as well? is there a better way?
I would like to use something like the rewriteEngine that the new url is like a folder.
Can you help me to find a better solution?
Thanks a lot
Page including
For the page inclusion, you can use a simple array to dynamically allocate your page to a specific name. As so:
$pages = array('city-sitemap'=>'views/city-sitemap-view.php',
'place'=>'views/place-view.php',
)
if(array_key_exists($_GET['p'], $pages){
include $pages[$_GET['p']];
}else{
include 'views/error.php';
}
This array should be added in a general configuration file. With this configuration if you want to display your city-sitemap-view.php view, you will have to write this url: http://www.domain.com/index.php?p=city-sitemap
Url rewriting
It is possible to rewrite an URL with a .htaccess file. Here is an example of code you would can to write in your .htaccess file.
RewriteEngine On
RewriteRule ^(.*)$ index.php?p=$1 [L,QSA]
An url that looks like this:
http://www.domain.com/index.php?p=city-sitemap
will be converted to this one:
http://www.domain.com/city-sitemap
There is something called the front controller that you should look into. You can write a very simple one yourself. It works in conjunction with URL rewriting. If for example your url looks like this:
/mypage.html
the url rewriter will reroute the request to index.php .
Index.php will then look at the URL and do somehting like this:
1) break the page out into string, ignoring .html extention
preg_match("\/(.*?)\.html", $_REQUEST['URI'], $matches);
$pageClass = $matches[1];
//$pageClass = "mypage"
2) look for and load a class named "Mypage.php"
$controller = new $pageClass();
3) call the run method on the page class, passing it all the request parameters
$request = new Request($_REQUEST);
$controller->run($request);
you can then do all the page specific stuff inside your controller class that is specific to the page.
At each simple step along the way, you will find you want to do more and more things like authentication, filtering, tracking, etc. You will get end up developing a front controller that is specific to your needs, as well as a base Controller class that does a bunch of standard stuff that all your controllers have in common.
As per my comment, i really think you should consider the framework i linked, (it is very logical) or any other micro framework, but if you really wish to do this yourself then you can handle your includes like so:
<?php //index.php
$requested_page=isset($_GET['p'])?$_GET['p']:'home';
//maybe have this included from pages.php for organization
$pages=array(
'home'=>'home_content.php',
'about'=>'about_content.php',
'contact'=>'contact_content.php'
);
include "views/header.php";
if(array_key_exists($requested_page, $pages)){
include "views/".$pages[$requested_page];
}else{
header('HTTP/1.0 404 Not Found');
include "views/error404.php";
}
include "views/footer.php";
This keeps your pages in a single array, and protects against arbitrary inclusion vulnerabilities.
For nicers urls, see The other users .htaccess rewrite rules

Laravel route filter to specific url

I'm trying to apply route filter to a specific url. So, my file is under public directory:
/public/js/kcfinder/browse.php
My filter:
Route::filter('admin', function ()
{
if (!Sentry::check())
{
// if not logged in, redirect to login
return Redirect::to_route('admin_login');
}
elseif (!Sentry::user()->has_access('is_admin'))
{
//logout
Sentry::logout();
// has no access
return View::make('error.access_error');
}
});
Finally my pattern:
Route::filter('pattern: js/kcfinder/*', 'admin');
If I try to access
/public/js/kcfinder/browse.php
the rule does not work.
I can see whole contents of file.
If I try to acess
/public/js/kcfinder/blahblah.php
the filter works great. Because there is no file which named
blahblah.php
under the directory.
Any help would be really great!
That's because your .htaccess file says that if the request URI matches an existing file, then don't rewrite, just show/execute the file as it is.
So, you have two options:
You can create the necessary filters in your .htaccess file so that files in certain directories always get rewritten and sent to index.php.
RewriteCond %{REQUEST_URI} ^/js/kcfinder
RewriteRule \.php$ index.php [L,QSA]
This condition-and-rule set would need to be placed above the conditions that check for files and directories.
Alternatively, you can create authenticated routes in Laravel that manually fetch and execute the files. (This is not recommended as you would have to make use of eval().)
I've been down this path with trying to integrate ckfinder into Laravels auth system and ultimately found it to be too much effort. It's probably possible but my solution was simple hack. When I render the Ckeditor widget, I set a plain old session variable in PHP, then check for the existence of the session var in ckfinders auth check routine.
// When rendering widget
session_start();
$_SESSION['enable_ckeditor'] = true;
// In ckfinder somewhere
session_start();
return isset( $_SESSION['enable_ckeditor'] ) ?: $_SESSION['enable_ckeditor'];

Creating Vanity URLs

I currently run a site where I want to give people the ability to make their own URLs. For example, here is my URL:
http://www.hikingsanfrancisco.com/hiker_community/hiker_public_profile.php?community_member_id=2
You see, it is named just by id, which is uninteresting and also bad for SEO (not that it matters here).
Ideally I want my site members to also have their names in the URL. How is that typically done? So in my case, it would be something like:
http://www.hikingsanfrancisco.com/alex-genadinik and have the id hidden.
Is that possible? Any advice would be appreciated!
Thanks,
Alex
you need router, see https://stackoverflow.com/questions/115629/simplest-php-routing-framework
Generally this is accomplished via the use of an htaccess file on a server with mod_rewrite (most Linux servers). An example might be like:
Options -Indexes
RewriteEngine On
RewriteRule ^([0-9a-zA-Z\-]+)$ $1.php
RewriteRule ^/(alex[\-]genadinik)$ /hiker_community/hiker_public_profile.php? community_member_name=$1
This implies that your hiker_public_profile.php script will need to accept "alex-genadinik" as $_GET variable "community_member_name," and then query the database via the name instead of the ID.
So you'd take the above code, save it in a file called ".htaccess," and then upload it to the root directory of your website. Learning regular expressions is recommended.
Code Igniter is a great MVC framework which provides configuration derived routes, which can easily be configured to send all requests through a common controller, where content can be dynamically pulled from a database and rendered.
Here is an example of a basic routing rule, which excludes request for users, students, and lessons, but routes all other request to a common content controller.
So if you request http://mydomain.com/hiking-and-camping-info, the url would be parsed, and hiking-and-camping-info would be looked up in the database and the related content pulled down.
Routing configuration:
$route['^(?!lessons|students|users|content).*'] = 'content';
and the content controller then grabs the url segment and finds the matching content and loads it:
class Content extends Controller {
function __construct() {
parent::Controller();
$this->load->model('Content_model', 'content');
}
function index() {
$content_url = $this->uri->segment(1);
$data['content'] = $this->content->get_content_by_name($content_url);
$this->load->view('content', $data);
}
}

how to protect server directory using .htaccess

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")

Zend Framework "under maintenance" page

I'm trying to figure out how to set up a holding/"under maintenance" page in Zend Framework for when I am upgrading the database or something and don't want anyone using the site. I'd like to have a static HTML page and have all traffic redirected to that.
I'd rather not use .htaccess and would like to do it via the bootstrap file.
Any ideas?
Thanks.
I've set Apache to show index.html in preference to index.php (which bootstraps the ZF). As long as you don't link directly to /index.php anywhere, then you can just drop in an index.html file, and it will show that in preference to the ZF site.
An alternative is to have an entry in your configuration .ini file, and as soon as you have read the configuration:
if ($config->maintenance) {
readfile(APPLICATION . '/../public/maintenance.html');
exit;
}
You may want to add another check in there for a particular IP address (your own) as well, so that you can get though even when everyone else is blocked.
I've done this by creating a plugin that check the validity of the request each time that a page is requested.
During the execution of the plugin in the "preDispatch()" you can analyze a variable from the config that it will hold your current status as active/under maintenance and let the request flow to the original destination or redirect it to a landing page for this purpose.
Code sample
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
// get your user and your config
if( $config->suspended && $user->role()->name != "admin"){
$request
->setModuleName( 'default' )
->setControllerName( 'index' )
->setActionName( 'suspended' )
->setDispatched(true)
;
}
}
You could check your configuration file for a maintenance_mode switch and redirect every request from within the bootstrap to your static html maintenance page.
I have a blog post that demonstrates how to do this. Setting up a maintenance page with Zend Framework
I would use plugin with dispatchLoopShutdown() and based on the config settings i would redirect the request to any controller you want.
I followed all of these suggestions to a TEE on Zend 1.12. I googled around. Tried using application.ini, setting the plugin path, using zend_loader_autoloader_resource(), using Zend_Loader_PluginLoader. NONE of these worked for me. I ended up writing a .htaccess:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/maintenance\.php$
RewriteRule ^(.*)$ /maintenance.php [R=503,L]
This is why Zend is the worst framework. Tons of different options on how to do something simple, Official Documentation is extremely ambiguous and unclear, and nobody fully understands or can explain the correct way to do anything so I end up wasting an hour of my time trying to do things correctly.

Categories