I have a web site which currently has over 900 html articles currently viewable to anyone. I want to change it to restrict viewing of certain articles by membership only. I have the articles in sql database with flag if restricted. There is an article index with links to each article. The process will be to check if article is restricted, check if user is member logged in, then display, otherwise send to login or subscribe pages. Since there is so many articles, I can't just add some php to not display if the article is accessed directly. My question is where in my web directory to I put these article pages, and how do you protect someone from directly accessing the page, but allow access once the user is authenticated? Any input is appreciated. Anyone know of good reference books on this either?
Move the files so that they're above your document root, and therefore inaccessible through the web server. Or, move the files to a directory which you protect with a password (.htaccess/.htpasswd pair). You never give out that password, it's only there to prevent direct access of the files.
Then, write a script which proxies the articles. That script checks if the user is logged in. If not, it redirects them to the login page. If it is, it reads the article HTML from its actual location, and sends it through.
Ex: http://www.example.com/article.php?view=cooking.html
session_start();
if (!isset($_SESSION['logged_in'])) {
header("Location: login.php");
} else {
readfile("/path/to/articles/" . $_GET['view']);
}
You'll want to do some kind of sanitation on $_GET['view'] to make sure it doesn't contain anything but the name of an article.
You can even keep your current URLs and links to the articles by rewriting them to the proxy script in your .httaccess/httpd.conf with mod_rewrite. Something like this:
RewriteEngine On
RewriteRule article/(.*)\.html articles.php?view=$1 [L]
If you don't already have any existing framework for PHP development that would help with security matters, you might consider something simpler than even using PHP to restrict access. Read up about .htaccess files, and how you can create a protected directory in which you could place all the restricted articles. Then you can setup user account and require people to authenticate themselves before they can read the restricted articles.
Here's a tutorial on how to setup .htaccess for user authorization/authentication:
http://www.javascriptkit.com/howto/htaccess3.shtml
You have a couple of basic options:
Add the code to each page. You can probably automate this, so its not as bad as it sounds. It really shouldn't be more than a single include.
Figure out how to get your web server software (e.g., apache) to do the authentication checks. Depending on how complicated your checks are, a mod_rewrite external mapping program may be able to do it. Other than that, there are existing authentication modules, or writing a fairly simple shim isn't that hard (if you know C)
Feed all page loads through PHP. This will probably break existing URLs, unfortunately. You pass the page you want to see as a parameter or part of the path (depending on server config), then do you checks inside your script, and finally send the page if the checks pass.
The simplest way would probably be to move all the aricle files outside the web root, and then use PHP to fetch them if the client is allowed to see it.
For example:
<?php
if (isset($_GET['id']))
{
$articleDir = "/var/articles/";
// Assuming a "?id=1" query string is used to pass a numberic ID of
// an article. (Like: example.com/showArticle.php?id=1)
$articleID = (int)$_GET['id'];
$articleFile = "article_{$articleID}.html";
// Look through your SQL database to see if the article is restricted.
// I'll leave the codeing to that up to you though.
if (!$isRestricted || $isLoggedIn)
{
$path = $articleDir . $articleFile;
if (file_exists($path))
{
include $path;
}
else
{
echo "The requested article does not exist.";
}
}
else
{
echo "You have to be logged in to see this article.";
}
}
else
{
echo "No article ID was passed. Did you perhaps follow a bad link?";
}
?>
Note that if you want to keep the old links alive, you can use Apache's mod_rewrite to rewrite incoming requests and route them to your PHP file.
Edit
This may help if you are new to mod_rewrite and/or regular expressions:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^article_(\d+)\.html$ fetchArticle.php?id=$1 [L]
</IfModule>
Routs any link such as example.com/article_123.html to example.com/fetchArticle.php?id=123 without the client noticing it.
Similar to what Dan Grossman did, but this one fetches only numbers.
Related
I have been learning php recently and i found this strange statement that i tried to figure out but did not understand it well.
This is the URI that i found
<?php
$datei = file("http://www.abc.de/cms/index.php/pps.html");
foreach($datei AS $stellenangebote)
{
echo $stellenangebote;
}
?>
Now the question that i am trying to understand is that how come it reach the .html page while it has .php before it ?
And if this is a technique then What is it's name ? And why would i use such style ?
This is an Apache thing called PathInfo. It's activated if you have set AcceptPathInfo, which it usually is by default. You can then access the part after the file name through $_SERVER['PATH_INFO'].
There's more information available in the answers to "What exactly is PATH_INFO in PHP?"
This technique is used to create a single entry point to the application.
This allows you to control the flow of you application - all requests are processed by index.php, so you have one place to load classes, initialize objects etc.
Most modern php based sites will use this technique (it is central to the MVC paradigm used in the vast majority of modern web frameworks), though many hide the actual index.php file in the ur using Apache mod_rewrite or similar for other webservers.
As mentioned by fiskfisk, this can be achieved by accessing the request path. A simple example used to load files from a non web accessible location for authorized users:
<?php
$path = $_SERVER['PATH_INFO'];
//if user is authorized, include file from OUTSIDE of webroot
if(isset($_SESSION['auth']) && ($_SESSION['auth'] > 3)){
include '../' . $path;
}else{
die('you are not authorized to see this file');
}
As to the official name for this technique, i am not sure, but google "php single entry point" or "php route through index" should help.
I am looking to find a way I can secure admin area, especially the folder itself from outside access (These include folders with images and css). I have read a lot of suggestions but they all feel rather a compromise or work around than a bullet proof method or I am not understanding which is best for security and hidden from outside world, I want to be the only one that knows about it or access it. Hoping someone can shed some light what they would use, when they want the area completely hidden from outside world, whilst still accessible to you.
Some of the methods I have come across involve:
Moving folder outside of root
Using Htaccess Deny all. (also means I can't login unless I apply a static IP address which I do not have)
Another way I thought of could be to use session variable to store admin, recognize and grant access based on session ID. (This does mean all other css files and image folders are viewable).
Adding an index page in the folder which I see alot of sites do.
I currently have my login script to redirect me to my admin area, so is there anyway for the whole folder to recognize it's me and grant access and serve files on if a logged in admin php file is requesting it?, if not to decline access including images and css etc?
Can't figure out how best to protect this area? Is using session a secure way of identifying an admin?
The easiest way to ensure content is not exposed to the web is to place it above the site folder in your directory structure.
so for example in your Apache configuration mount the site at
/var/www/sites/site/content/
and place the restricted content at
/var/www/sites/site/
that way the content will not be exposed but php can still read it if required.
Obviously this will not stop users from seeing what is in your css files if php reads them and echoes them out but I dont see why a css file should need to be secure
Edit
Supposing you have a folder on your server at /var/www/sites/site/content/some_folder
and you enter www.yoursite.com/some_folder into a browser, assuming you have indexes open in your site you will see a list of files in some_folder
But how can you get to /var/www/sites/site/ from a web brower ? ... you can't!!
but what you can do is some thing like this:
And this would be a php file inside the main site folder (visible to public)
<?php
session_start();
if(isset($_SESSION['admin_logged_in'])){
include '/var/www/sites/site/secret_content.php';
}
The first step would indeed be to move all files you want to prevent public access to to outside the document root. This way there is no way to access the files directly through your webserver.
If you are looking to prevent access for all resources (including images, scripts, stylesheets etc) you could implement a "proxy" which is responsible for serving the files (after checking whether the user is authorized).
The easiest and most flexible way to do this is to have a single entry point in the application. Using apache this can easily be achieved using the following rewrite rule:
RewriteEngine On
RewriteRule ^(.*)$ index.php [L,QSA]
This will make sure every request will go through your index.php file.
No you can easiy check whether you are allowed to access the resources using e.g.:
<?php
session_start();
if (!isset($_SESSION['user'])) {
header('HTTP/1.0 403 Forbidden');
exit; // important to prevent further execution of the script
}
// user is allowed access, do shit
The above is a very simplified example. Normally you may want to render an actual nice looking page telling the user he is not allowed to access you stuff / rendering a login page.
Now to render the protected resources you could do something like:
Directory structure
Project
public (docroot)
index.php
index.php
other protected files
index.php in docroot
<?php
require_once __DIR__ . '/../index.php';
index.php in project
<?php
session_start();
if (!isset($_SESSION['user'])) {
header('HTTP/1.0 403 Forbidden');
exit; // important to prevent further execution of the script
}
$file = $_SERVER['REQUEST_URI']; // important to sanitize or possible check against whitelist the requested resource
$ext = pathinfo($path, PATHINFO_EXTENSION);
switch ($ext) {
case 'jpg':
case 'jpeg':
header('Content-type: image/jpeg');
imagejpeg('/path/to/protected/resources/' . $file);
break;
}
Now you will lhave total control over what you serve and to whom.
Note that whether it is secure depends entirely on what your implementation looks like, but in general:
Always place your non public files outside of the document root
Always sanitize / whitelist user input
Always secure your data
Some generic, but related reads:
Preventing Directory Traversal in PHP but allowing paths (very much related to the $file = $_SERVER['REQUEST_URI']; point)
How can I prevent SQL-injection in PHP?
Secure hash and salt for PHP passwords
Yes, you should move the content out of the document root. You could try using .htaccess to protect your files, but allowing overrides by .htaccess can itself be a security problem. It's certainly a performance problem.
Simply point your 404 handler at something like....
<?php
define('REQUEST_PATH', '/secure');
define('SECURED_CONTENT', '/var/www/restricted');
$req=parse_url($_SERVER["REQUEST_URI"]);
if ((0===strpos($req['path'],REQUEST_PATH))
&& $_SESSION['authenticated']) {
if (is_readable(SECURED_CONTENT . $req['path'])
&& is_file(SECURED_CONTENT . $req['path'])) {
header('Content-type: '
. mime_content_type(SECURED_CONTENT . $req['path']);
include(SECURED_CONTENT . $req_path);
} else {
header('HTTP/1.0 404 Not Found');
}
exit;
}
header('HTTP/1.0 403 Forbidden');
On websites such as facebook and many others you see URLs such as www.facebook.com/username. How does a URL such as this actually load the users information from a MySQL database? and what is the actual file it is displaying on? I would assume there's not really a folder for each user that it is referring to. If my question doesn't make sense can you at least point me in the right direction to set something like this up through PHP?
Again, I want example.com/username to load my users profile. How does this work?
By using apache's .htaccess file to manage a RewriteEngine, all of your pages can be funneled through an index.php file. After confirming that the requested page is not actually a page that you've intended to be a default part of your web page, you can fall back on the code below, to discover a user account. If a user account is not discovered, then the likelihood is that the page being accessed is simply a 404, which you could then redirect to as a catch-all scenario
.htaccess file
RewriteEngine on
RewriteBase /
RewriteRule !\.(xml|js|ico|gif|jpg|png|css|swf|php|txt|html|otf)$ index.php
php logic to run after confirming the requested page, isn't something like a contact-us page, or any typical web page an end user would be attempting to access.
if(preg_match("/^\/(?P<username>[^\/]*)/", $_SERVER['REDIRECT_URL'], $matches)) {
$result = mysql_query("SELECT * FROM users WHERE username = '" . mysql_real_escape_string($matches['username']) . "'");
if($user_record = mysql_fetch_row($result)) {
echo "DO WHATEVER YOUR HEART CONTENTS HERE :)";
} else {
header("Location: error-404.php");
}
}
It is all loaded dynamically via the database. For example, my Facebook name is "benroux". In facebook's user table, there is going to be a unique column called, lets say, nickname. When I visit Facebook, they are parsing the path info and essentially calling a query in the form:
select * from user where nickname = "{$nickName}"
Now, this is an over simplified example, but I hope it gives you an idea of what is going on here. The key is that there are 2 types of url vars, the facebook.com/pagename.php?id=blah and the restful style path info vars facebook.com/pagename/var1/var2/
If you are looking to have example.com/benroux load my user page, you need to make your index.php (I'll use PHP) load the path info ( http://php.net/manual/en/function.pathinfo.php ) and then query the database as I have described above.
try to
print_r($_SERVER);
you will get that parameters. Then you just need to split them.
Something like
$directory = $_SERVER['REQUEST_URI'].split('/')[1];
So, put $directory into query
I've been googling for days now and have come across different ways to secure folders (htaccess, using a PHP page with a password) but these don't tackle my issue.
The problem:
I need to have a site where different clients can access ONLY THEIR content. Client-A needs to be able to access all their Flash content and websites. Client-B and Client-C need to do the same but none of them can access each others content (even by directly linking to it). A username/password system won't work because each client has 400-1000 users and neither myself or the client has time to manage all these users.
I looked into htaccess and htpasswd but I prefer not to use any username/password combo's. Ideally, I'd like a "secret word" or "passphrase" I could pass from an iPad app or Air program to the server to get the content I need. Anyone have some ideas on the best way to handle this?
EDIT: To simplify things... I want to have HTML sites and Flash swf's above my web root and be able to display them to users. How can I make this happen? I have HTML sites that use relative links so using php's readfile() causes these sites to break since those links aren't correct.
What RDBMS are you using ?
With mod_authn_dbd and a basic authentification you would be able to do so.
Something like this,
AuthType Basic
AuthName "My Server"
AuthBasicProvider dbd
# core authorization configuration
Require valid-user
# mod_authn_dbd SQL query to authenticate a user
AuthDBDUserPWQuery "SELECT password FROM authn WHERE user = %s"
If you have control over the software which sends the requests, you could add an own X-header to every request which identifies the user.
With apache_request_headers() you can get your own request header from the request:
http://www.php.net/manual/en/function.apache-request-headers.php
==============
Edit after first comment:
Some code for example:
globals.php
$headers = apache_request_headers();
$key = $headers["X-Authorization-Key"];
$authorized = checkAuthorization($key);
if(!$authorized) {
header('HTTP/1.1 403 Forbidden');
echo "Access denied!";
exit;
}
//... db connection or something else to get user specific definitions, paths, ...
//e.g.:
$user = $users[$key];
define("CONTENT_PATH", "/var/www/mypage/data/".$user);
function checkAuthorization($key) {
//... db connection or something else where the authorization-information are stored in
//check whether the $key is in the auth-info and return true / false for the result
return true; //or false
}
in every script on top:
<?php
require_once("globals.php");
//... work with the user specific definitions, paths
include(CONTENT_PATH."/...");
//...
What you do is when the user creates their login account, they have the option to select what group they are using. Then when page info is displayed, it displays the normal page, but with the permissions name included in it. You would have to build 3 seperate content pages, but they would only see what the content of their chosen group.
homegroup1.php
homegroup2.php
homegroup3.php
if the user is in group one, the direct would be home"group".php for the display. It would call for the group on the site they go to.
I'm working on a small, very application-specific CMS. There's nothing financial going on, and the content isn't sensitive really (it's mostly managing relationships between pieces of art), so security isn't a high priority, but still we're using a login system with a controlled group of users for each installation.
The entry page has a login form - success sets a session variable to the user table's PK for the user, and redirects to an arbitrary view page. That variable is also used to track any changes made by that user.
I'm auto_prepending to all pages a small script that just checks if that session variable is set - if not, the user is bounced back to the login page. This works, but seems clumsy - especially since I need to 'exempt' the login page, so I used an ugly little hack, which is basically this:
if(!isset($_SESSION['user']) && strpos($_SERVER['PHP_SELF'], '/login.php') !== 0) {
header('Location: /login.php');
}
The above is simplified a little from the actual script, but basically demonstrates what's happening.
I'm wondering if there's an established best-practice for something like this - testing for logged-in status and redirecting when that status isn't set...
tyia
It seems like you're using the standard approach. But instead of having to include this script in every one of your PHP pages, I would have all requests go through a single index.php file that is responsible for security. That way, you're certain that there is a single point of failure: your index.php file.
To get this going, you'll need an .htaccess file if you're running apache:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} .*\.php$
RewriteRule ^(.*)$ /index.php?script=$1
</IfModule>
Alternatively, you could define a function and include it at the top of every page that requires a logging in:
// in security.php
function require_login($redirect_to='/login.php) {
if (empty($_SESSION['user'])) {
header('Location: ' . $redirect_to);
exit();
}
}
// and in your .php files except for login.php
include_once 'security.php';
require_login();
You may also want to test that not only 'user' is set in the session, but that it includes some identifier (ie. an ID).