Say I have a url like this:
http://www.mysite.com/forum/board1/sub-forum/topics/123
Is there a simple way in PHP (can't use HTAccess) to take that URL and extract board1, sub-forum, topics and 123 so I can use them in a database for example? Are there any built in functions or will I have to write my own?
Thanks,
James
explode('/', getenv('REQUEST_URI'));
If your environment happens to include the query string part in the above value, here's a neat workaround:
explode('/', strtok(getenv('REQUEST_URI'), '?'));
You can, but without redirecting requests your webserver will just return a 404 error for non-existing paths.
However, you can use urls like http://your.site.com/index.php/foo/bar/baz and then split the url into parts like #pestaa said which you can then parse into parameter values.
This is taken from my MVC
http://www.phpclasses.org/package/6363-PHP-Implements-the-MVC-design-pattern.html
The link is outdated at the minute, I have just updated it so it does not have the MVC stuff in, and this can be called with getLoadDetails($_URL); amd $_URL will be exactly the same as $_GET other than it gets the data from the folder path.
function getLoadDetails(&$_URL){
$filePath = $_SERVER['REQUEST_URI'];
$filePath = explode("/", $filePath);
for($i = 0; $i < count($filePath); $i++){
$key = $filePath[$i];
$i++;
$val = $filePath[$i];
$keyName = urldecode($key);
$_URL[$keyName] = urldecode($val);
}
}
I do have one question, if you cant use HTACCESS how do you plan on coping with the folder path please dont tell me your system is going to create the folder paths and index file for every URL that will trash your server Speed and your Host will hate you for it.
If you already have configured your web server to send those requests to your particular PHP file, you can use parse_url and explode to get and then split the requested URI path into its segments:
$_SERVER['REQUEST_URI_PATH'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$pathSegments = explode('/', $_SERVER['REQUEST_URI_PATH']);
Related
Trying to setup a php website on my windows os. PHP code that I got from someone has lines like
$base_path = realpath('./../').'/';
This ends up with the string like c:\abc\xyz/
What settings I need to do on windows to force it to come with /. I read about DIRECTORY_SEPERATOR, but there are various places I need to worry about and hence if I could have it so that the realpath comes up with / it will be of great help to me.
A variation of Timothy's answer which uses the DIRECTORY_SEPARATOR constant instead of a conditional to simplify the function.
function platformSlashes($path) {
return str_replace('/', DIRECTORY_SEPARATOR, $path);
}
$path = "/some/path/here";
echo platformSlashes($path);
The backslash is the directory separator on the Windows platform. But from what I understand and have experienced, when resolving paths your PHP script will still work with forward slashes. As a consequence, you could write all your code with forward slashes and not worry about it. The forward/backwardslashes are really only important if you're displaying the path to the user, like in a setup/installer script (most users of a site would have no need to know about directory structures nor care what platform the service is running on). You could create a display function that would identify the platform and replace the slashes as appropriate, and then pass the paths through this before showing them. The following is an example of what I'm suggesting, though I haven't tested it.
<?php
function platformSlashes($path) {
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
$path = str_replace('/', '\\', $path);
}
return $path;
}
$path = "/some/path/here";
echo platformSlashes($path);
Just do a replace on the string you have:
$base_path = realpath('./../') . '/';
$base_path_mod = str_replace('\\', '/', $base_path);
I want to download a remote file and put it in my server directory with the same name the original has. I tried to use file_get_contents($url).
Problem is that the filename isn't included in $url, it is like: www.domain.com?download=1726. This URL give me, e.g.: myfile.exe, so I want to use file_put_contents('mydir/myfile.exe');.
How could I retrieve the filename? I tried get_headers() before downloading, but I only have file size, modification date and other information, the filename is missing.
I solved it another way. I found that if there is no content-disposition in url headers, then filename exists in URL. So, this code works with any kind of URL's (no cURL needed):
$url = "http://www.example.com/download.php?id=123";
// $url = "http://www.example.com/myfile.exe?par1=xxx";
$content = get_headers($url,1);
$content = array_change_key_case($content, CASE_LOWER);
// by header
if ($content['content-disposition']) {
$tmp_name = explode('=', $content['content-disposition']);
if ($tmp_name[1]) $realfilename = trim($tmp_name[1],'";\'');
} else
// by URL Basename
{
$stripped_url = preg_replace('/\\?.*/', '', $url);
$realfilename = basename($stripped_url);
}
It works! :)
Based on Peter222 's code i wrote a function to get the filename.
You can use the $http_response_header variable:
function get_real_filename($headers,$url)
{
foreach($headers as $header)
{
if (strpos(strtolower($header),'content-disposition') !== false)
{
$tmp_name = explode('=', $header);
if ($tmp_name[1]) return trim($tmp_name[1],'";\'');
}
}
$stripped_url = preg_replace('/\\?.*/', '', $url);
return basename($stripped_url);
}
Usage: ($http_response_header will be filled by file_get_contents())
$url = 'http://example.com/test.zip';
$myfile = file_get_contents($url);
$filename = get_real_filename($http_response_header,$url)
file_get_contents() over the HTTP wrapper does not directly download the file if the file has been pre-parsed by the webserver.
Take the following example: if you call file_get_contents() on a remove webpage (example.com/foobar.php), you won't be presented with the source code of foobar.php, but how the webserver of example.com parses the PHP file. So you will only be able to retrieve the generated HTML output.
If the filename is not present in the URL and there is no way that you can fetch it from anywhere, then you are in a dead end. Data can't be just summoned from the transcendental field of datum.
For alternative solutions, I can only suggest using the cURL library (it is used to handle queries from your server (as it were a client) to other servers with using URL, hence the name cient URL) or file sockets. Here is another question's answer on Stack Overflow which describes how to fetch filename using cURL.
Also, you might try getting in touch with the administrator/maintainer/webmaster team of domain.com, asking if they had a publicly available API to fetch filenames and other metadata.
I have a php script in a folder (I call it the root folder). The script can basically list all files in subfolders of this root folder. The user can specify which subfolder should be displayed by using GET-parameters.
script.php?foo
would display the content of
<root folder>/foo/
and
script.php?.bar
would display the content of
<root folder>/.bar/
However, users could also "cheat" and use commands like /.. to display the content of folders they souldn't be able to see.
For example with
script.php?/../..
the users could get very high in the folder hierarchy.
Do you have an idea how to prevent users of doing "cheats" like this.
For reason of simplicity, let's say the GET-parameter is stored in $searchStatement.
You could use realpath to resolve the relative path to an absolute one and then check if that path begins with your "root" folder's path:
$absolutePath = realpath(__DIR__ . '/' . trim($searchStatement, '/'));
if (strpos($absolutePath, __DIR__ .'/') !== 0) {
die('Access denied.');
}
You just should validate the input before you use it.
For example you might want to only allow the characters a-z and / to allow subdirectories. Probably you want to allow the . as well. If you make this subset small, it's easy to validate if the input is allowed or not by the allowed characters already.
At the moment you allow ., as you have noticed, you have the problem that relative paths could be created like /../../ which could be used for directory traversal attacks.
To validate if a string contains only characters of a specific range, you can validate this with a regular expression or the filter functions. If your website does not need to allow any relative path parts you can look if they exist in the path to validate the input:
$valid = !array_intersect(array('', '.', '..'), explode('/', $path));
Valid will be FALSE if there is any // or /./ or /../ part inside the path.
If you need to allow relative paths, realpath has already been suggested, so to query the input against your directory structure first. I would only use it as last resort as it is relatively expensive, but it's good to know about.
However you can resolve the string your own as well with some simple function like the following one:
/**
* resolve path to itself
*
* #param string $path
* #return string resolved path
*/
function resolvePath($path)
{
$path = trim($path, '/');
$segmentsIn = explode('/', $path);
$segmentsOut = array();
foreach ($segmentsIn as $in)
{
switch ($in)
{
case '':
$segmentsOut = array();
break;
case '.':
break;
case '..';
array_pop($segmentsOut);
break;
default:
$segmentsOut[] = $in;
}
}
return implode('/', $segmentsOut);
}
Usage:
$tests = array(
'hello',
'world/.',
'../minka',
'../../42',
'../.bar',
'../hello/path/./to/../../world',
);
foreach($tests as $path)
{
printf("%s -> %s\n", $path, resolvePath($path));
}
Output:
hello -> hello
world/. -> world
../minka -> minka
../../42 -> 42
../.bar -> .bar
../hello/path/./to/../../world -> hello/world
I can only suggest you first validate the input based on it's own data before letting touch it the filesystem, even through realpath.
Have a look at the chroot function:
bool chroot ( string $directory )
Changes the root directory of the current process to directory, and changes the current working directory to "/".
A call to that method prevents further access to files outside of the current directory.
Note however that requires root privileges.
Have you tried something with realpath, it should resolve all the /.. in your path. By testing the realpath of the arguments against your current path like:
substr($realpath, 0, strlen('/basepath/cant/go/above')) === '/basepath/cant/go/above'
you make sure that any /.. havent escaped from where you want.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
PHP - Iterate through folders and display HTML contents
Using PHP, I'm trying to create a script that will navigate to the root directory of a website, and from there, using scandir() or glob() (those being the only directory scanning functions I've learned), I would use a recursive method to navigate through all the items in the root directory, then re-calling itself when encountering an entry that tested to be a directory, through is_dir($fileName).
Here's where I'm encountering problems - when I reach an entry that is a directory, it correctly navigates the if statement correctly to the commands for directories, but when calling itself, I don't seem to be able to get the glob() directory right, since every time I call it, the page ceases to load anything more. I'm trying to figure out, from the relative URL-based nature of scanning directories how I would reference it. I set a variable $ROOT_DIR, which is the root directory relative to the directory in which the php page is located in (in this case, $ROOT_DIR="../../"), and then I'd think logically, I'd call scanAllFiles [my sitemap method] with $ROOT_DIR . $fileName, where that's the string of the directory found, after removing the leading "../../" from the string. After trying this, it doesn't work.
Should I be using a different directory-traversing method to do this, or am I formatting the method call incorrectly?
Most people just use MySQL to make sitemaps, doing it manually.
Exposing files isn't safe, but you can add some security.
<?php
function files($dir=".") {
$blacklist = array(str_replace("/","",$_SERVER['SCRIPT_NAME']), 'admin.php', 'users.txt', 'secret.txt');
$return = array();
$glob1 = glob($dir."/*");
for($i=0;$i<=count($glob1)-1;$i++) {
$item = $glob1[$i];
$nodir = str_replace($dir, "", $item);
if(is_dir($item)) {
$file1 = explode('/', $item);
$file = $file1[count($file1)-1];
$merge = array_merge($return, files($item));
if(!in_array($file, $blacklist) and !empty($nodir)) $return = $merge;
}
else {
$file1 = explode('/', $item);
$file = $file1[count($file1)-1];
if(!in_array($file, $blacklist) and !empty($nodir)) $return[] = str_replace("./","",$item);
}
}
return $return;
}
// Use like this:
$files = files(); // Get all files from top folder down, no traling slash ...
for($i=0;$i<=count($files)-1;$i++) { // ... Go through them ...
echo "<li>$files[$i]</li>"; // ... And echo the item
}
?>
I want to make sure a file path set via query string does not go outside of the desired subdirectory. Right now, I am checking that:
The path does not start with "/", to prevent the user from giving an absolute path.
The path does not contain "..", to prevent the user from giving a path that is outside of the desired subdirectory.
The path does not contain ":", to prevent the use of a url (i.e. "http://", "ftp://", etc.). Should I ever run this script on a Windows server (not likely), this will also prevent absolute paths beginning with a drive specifier (i.e. "C:\"). Note: I'm aware that a colon is a valid character in a Unix filenames, but I will never be using it in a filename.
The path does not start with "\". Just in case I change my mind about running on a Windows server, this prevents Windows network paths from being specified (i.e. "\\someserver\someshare"). Again, I'm aware that a backslash is a valid Unix filename character, but I also won't be using it in any filenames.
Are these checks sufficient?
Background
I have a PHP script that takes (via query string) the path to a sample source file to be shown to a user. So I might give them a link like "view_sample.php?path=accounting_app/report_view.php" or "view_sample.php?path=ajax_demo/get_info.js".
The script looks basically like this:
$path = $_GET['path'];
if(path_is_valid($path) && is_file("sample/$path"))
{
header('Content-Type: text/plain');
readfile("sample/$path");
}
My concern is that a malicious user would see the url and try to do something like "view_sample.php?path=../../database/connection_info.php" and gain access to a file which is not in the "sample" directory.
Are the four checks I defined above (which would be implemented in the path_is_valid() function) sufficient to lock out a malicious user? (Also, I think checks 1, 3, and 4 are basically irrelevant since I am prepending a relative path, but if I didn't do this would the checks be sufficient?)
Call
$path = realpath("sample/$path");
Then check that the resulting path starts with the directory you're expecting.
<?php
// Current path information
$path = $_GET['path'];
$vroot = "sample";
// Validate that the $path is a subfolder of $vroot
$vroot = realpath($vroot);
if(substr(realpath($path), 0, strlen($vroot)) != $vroot or !is_dir($path)) {lid!
exit("Invalid path");
} else {
echo "Ah, everything is alright!";
}
?>
The use of realpath should not change the path, so I use it in the following way:
function checkPath($pathToCheck) {
global $basepath;
$fullpath = $basepath.'/'.$pathToCheck;
if ($fullpath==realpath($fullpath) && is_dir($fullpath)) {
return $fullpath;
} else {
error_die('path not allowed: '.htmlentities($pathToCheck));
}
}