Hello I'm making a website and I have issues with htaccess clean url redirects when a user adds amps or slashes as a title for the article he is submiting.I decided thath I would like to allow only specific characters like alpanumeric, -, _, #,[,]. no quotes or double quotes etc etc...
I cant seem to make the regexp work I have not great esperience with regex and instead of using string replaces I thought I should ask.
Also any other proposals regarding this matter will be greatly appreciated
In my htaccess I have the following setup:
RewriteRule ^([^/]*)(.*)$ /index.php?page=$1&request=$2
and in my index I have:
include 'db.php';
include 'generic.php';
$pages = array('','main','events','news','rss','search','add-event');
if(isset($_GET['page']) && in_array($_GET['page'], $pages))
{
$event_category = null;
$event_title = null;
$search_string = null;
$rss_category = null;
$events_date = null;
$search_page = null;
validate_evented_page(trim(urldecode($_GET['page'])));
}
else
{
evented_error_page(404);
}
function evented_error_page($err)
{
include('errorpages/error.php');
}
function validate_evented_page($p)
{
$page = strtolower($p);
global $event_category;
global $event_title;
global $search_string;
global $rss_category;
global $events_date;
global $search_page;
if($page == 'main' || strlen($p) == 0)
{
include 'main.php';
}
else if($page == 'events')
{
$params = explode_url($_GET['request']);
if(count($params) == 0)
{
$events_date = "'';";
include 'allevents.php';
}
else if(count($params) == 1)
{
if(check_date($params[0]))
{
$date_split = explode('-',$params[0]);
$events_date = "'".date("m/d/Y" , mktime(0, 0, 0, date('m'), $date_split[1], date('Y')))."';";
}
else
{
$events_date = "'';";
}
include 'allevents.php';
}
else if(count($params) == 2)
{
$event_category = trim(urldecode($params[0]));
$event_title = trim(urldecode($params[1]));
include 'event.php';
}
else
{
evented_error_page(404);
}
}
}
the following url:
/events/Drum+%26+Bass/Innersense+presents+ETHOS_v04-0
gives $_GET['request'] = /Drum
when it should have been Drum & Bass (in the database I have stored this category as "Drum & Bass").
You need to tell mod_rewrite to encode the & that gets grouped in the backreferene using the B flag:
RewriteRule ^([^/]*)(.*)$ /index.php?page=$1&request=$2 [B,L]
The URI gets decoded when it gets sent through the rewrite engine. So the %26 gets decoded to &, the B flag ensures it gets re-encoded as the backreference.
Related
I have a group of pages, and I want to include one of them dependent on a variable in url, but the page is always appear is dashboard
link1 : index.php?pth=&page=dashboard
link2 : index.php?pth=modules/institution&page=inst_classification
if statement :
if (isset($page) && !empty($page)) {
$page = htmlspecialchars($_GET["page"]);
$pth = htmlspecialchars($_GET["pth"]);
}else{
$page ='dashboard';
$pth = '';
}
include ('../admin/template/'.$template_fldr.'/html/'.$pth.$page.'.php');
thanks!
You access $page before you write to it. You also never check for the existance of your pth value. Try:
if (empty($_GET['page']) || empty($_GET['pth'])) {
$page ='dashboard';
$pth = '';
}else{
$page = htmlspecialchars($_GET["page"]);
$pth = htmlspecialchars($_GET["pth"]);
}
include ('../admin/template/'.$template_fldr.'/html/'.$pth.$page.'.php');
You probably also need a / here in your include, if modules/institution/inst_classification.php is the file you are looking for:
include('../admin/template/'.$template_fldr.'/html/'.$pth.'/'.$page.'.php'); - but that is not clear from your question.
if (isset($_GET["page"]) && isset($_GET["pth"])) {
$page = htmlspecialchars($_GET["page"]);
$pth = htmlspecialchars($_GET["pth"]);
} else {
$page = 'dashboard';
$pth = '';
}
include ('../admin/template/'.$template_fldr.'/html/'.$pth.'/'.$page.'.php');
I just wrote a simple PHP class that i think it will help me prevent or detect possible SQL attacks. I am not sure if that really works and thats why im posting it here to read your ideas and suggestions.
So lets start from the classic config.php file that creates a connection with the DB and selects a specific Database. I turned it into a class and it returns the connection link in the main page. Lets see:
<?php
class mysql_init
{
public function initDb(){
$con = mysqli_connect("localhost","user","password");
mysqli_select_db($con,"geneticDb");
return $con;
}
}
?>
So after this lets see the code of the main page safe.php. It has a simple example of fetching data from the DB through an Id that user gives with GET request.
<?php
require_once('filter.php');
require_once('config.php');
$connection = new mysql_init();
$con = $connection->initDb();
$safe = new filter($con,'GET');
$n_id = $_GET['noteId'];
$data = mysqli_query($con,"SELECT noteType FROM notes WHERE noteId = $n_id ");
if($data){
$row = mysqli_fetch_array($data, MYSQLI_BOTH);
echo $row["noteType"];
}
?>
As you can see i am including also and the filter.php which is a class that scans the GET or POST requests that user sends. If it will find something curious it takes the thread id of the session with the Database and shut it down. The truth is that is not finished and i mean that i will put it some futures to save some logs (of the attacker) to the database, and redirect him/her to another page informing him/her for the record.
<?php
class filter
{
private $_activity = 0;
public function __construct($sql_connection,$method){
switch ($method) {
case 'GET':
$this->check_GET($sql_connection);
break;
case 'POST':
$this->check_POST($sql_connection);
break;
case 'ALL':
$this->check_POST($sql_connection);
$this->check_GET($sql_connection);
break;
default:
# code...
break;
}
}
private function check_GET($con){
if($_SERVER['REQUEST_METHOD'] == 'GET')
{
echo $_SERVER['REQUEST_URI']."<br>".$_SERVER['SCRIPT_NAME']."<br><br>";
foreach($_GET as $index => $value)
{
if(preg_match('/\s/', $value)) # no whitespaces
$this->_activity = 1;
if(preg_match('/[\'"]/', $value)) # no quotes
$this->_activity = 1;
if(preg_match('/[\/\\\\]/', $value)) # no slashes
$this->_activity = 1;
if(preg_match('/(and|or|null|not|if)/i', $value)) # no sqli boolean keywords
$this->_activity = 1;
if(preg_match('/(union|select|from|where)/i', $value)) # no sqli select keywords
$this->_activity = 1;
if(preg_match('/(group|order|having|limit)/i', $value)) # no sqli select keywords
$this->_activity = 1;
if(preg_match('/(into|file|case)/i', $value)) # no sqli operators
$this->_activity = 1;
if(preg_match('/(;|--|#|\/\*)/', $value)) # no sqli comments
$this->_activity = 1;
if(preg_match('/(=|&|\|)/', $value)) # no boolean operators
$this->_activity = 1;
if(isset($this->_activity) && $this->_activity == 1){
echo "Something detected => ".$index." : ".$value."<br>";
$thread_id = mysqli_thread_id($con);
mysqli_kill($con, $thread_id);
$this->_activity = 0;
}
}
}
}
private function check_POST($con){
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
echo $_SERVER['REQUEST_URI']."<br>".$_SERVER['SCRIPT_NAME']."<br><br>";
foreach($_POST as $index => $value)
{
if(preg_match('/\s/', $value)) # no whitespaces
$this->_activity = 1;
if(preg_match('/[\'"]/', $value)) # no quotes
$this->_activity = 1;
if(preg_match('/[\/\\\\]/', $value)) # no slashes
$this->_activity = 1;
if(preg_match('/(and|or|null|not|if)/i', $value)) # no sqli boolean keywords
$this->_activity = 1;
if(preg_match('/(union|select|from|where)/i', $value)) # no sqli select keywords
$this->_activity = 1;
if(preg_match('/(group|order|having|limit)/i', $value)) # no sqli select keywords
$this->_activity = 1;
if(preg_match('/(into|file|case)/i', $value)) # no sqli operators
$this->_activity = 1;
if(preg_match('/(;|--|#|\/\*)/', $value)) # no sqli comments
$this->_activity = 1;
if(preg_match('/(=|&|\|)/', $value)) # no boolean operators
$this->_activity = 1;
if(isset($this->_activity) && $this->_activity == 1){
echo "Something detected => ".$index." : ".$value."<br>";
$thread_id = mysqli_thread_id($con);
mysqli_kill($con, $thread_id);
$this->_activity = 0;
}
}
}
}
}
?>
So thats all for now. Do you think that this code is effective, or somehow it can be bypassed ?
Thank you.
Can with this class prevent or detect possible SQL attacks?
No.
It is inviable for the real life too.
Imagine a similar approach were used on this very site of Stack Overflow. Were you able to post your question, full of "dangerous" characters?
I have joomla 2.5 site, and I have
http://www.something.com/places?x=target
I would like to have URL like this:
http://www.something.com/places/target
How can I do this?
EDIT:
on .htaccess following works:
RewriteRule ^places/(.*)$ http://www.something.com/places?x=$1 [L,P,nc]
However it does not work with spaces ('/places/tar get' will only go to '/places?x=tar'). How can I fix that?
EDIT 2:
RewriteRule ^places/([^\ ])\ (.)$ http://www.something.com/places?x=$1\%20$2 [L,P,nc]
RewriteRule ^places/(.*)$ http://www.something.com/places?x=$1 [L,P,nc]
doest the trick. Thank you all!
If the places page belongs to a custom component (not a Joomla! built-in component), you will need to write or adjust the router.php file, in the component's directory.
It will need to contain something like:
function yourcomponentnameBuildRoute(&$query) {
$segments = array();
if (isset($query["x"])) {
$segments[] = $query["x"];
unset($query["x"]);
}
return $segments;
}
function yourcomponentnameParseRoute($segments) {
$vars = array();
$count = count($segments);
switch($segments[0]) {
case "target":
$vars["x"] = "target";
break;
}
return $vars;
}
UPDATE for your specific case:
Unfortunately there is no way to do this without a core hack.
So backup your *components/com_content/router.php* file, and then edit it as follows:
Replace the following code (around line 132):
if ($view == 'article') {
if ($advanced) {
list($tmp, $id) = explode(':', $query['id'], 2);
}
else {
$id = $query['id'];
}
$segments[] = $id;
}
unset($query['id']);
unset($query['catid']);
with this:
if ($view == 'article') {
if ($advanced) {
list($tmp, $id) = explode(':', $query['id'], 2);
}
else {
$id = $query['id'];
}
if(isset($query['x']) && $query['x']) {
$segments[] = $query['x'];
}
$segments[] = $id;
}
unset($query['x']);
unset($query['id']);
unset($query['catid']);
and this code (around line 212):
if (!isset($item)) {
$vars['view'] = $segments[0];
$vars['id'] = $segments[$count - 1];
return $vars;
}
// if there is only one segment, then it points to either an article or a category
// we test it first to see if it is a category. If the id and alias match a category
// then we assume it is a category. If they don't we assume it is an article
if ($count == 1) {
// we check to see if an alias is given. If not, we assume it is an article
if (strpos($segments[0], ':') === false) {
$vars['view'] = 'article';
$vars['id'] = (int)$segments[0];
return $vars;
}
with this:
if (!isset($item)) {
$vars['view'] = $segments[0];
$vars['id'] = $segments[$count - 1];
$vars['x'] = $count >= 2 ? $segments[$count - 2] : NULL;
return $vars;
}
// if there is only one segment, then it points to either an article or a category
// we test it first to see if it is a category. If the id and alias match a category
// then we assume it is a category. If they don't we assume it is an article
if ($count == 1 || ($count == 2 && (int) $segments[0] === 0)) {
// we check to see if an alias is given. If not, we assume it is an article
if (strpos($segments[0], ':') === false) {
$vars['view'] = 'article';
$vars['x'] = $count == 2 ? $segments[$count - 2] : NULL;
$vars['id'] = (int)$segments[$count - 1];
return $vars;
}
Then in your article's PHP code, you would use:
$target = JRequest::getVar("x");
I haven't tested it, so I'm not sure if it works. Let me know.
I'm looking for a way to extract both (partials) youtube urls and single ids from a user input string.
This article How do I find all YouTube video ids in a string using a regex? got me going quite well but still i'm struggling a bit.
Is there a way to find both playlist and/or video ids from a strings from:
E4uySuFiCis
PLBE0103048563C552
Through:
?v=4OfUVmfNk4E&list=PLBE0103048563C552&index=5
http://www.youtube.com/watch?v=4OfUVmfNk4E&list=PLBE0103048563C552&index=5
use:
$urlInfo = parse_url($url); // to get url components (scheme:host:query)
$urlVars = array();
parse_str($queryString, $urlVars); // to get the query vars
check out the youtube api for more details on the format
I wrote a script to do this once where the YouTube URL is posted via "POST" under the key "l" (lowercase "L").
Unfortunately I never got round to incorporating it into my project so it's not been extensively tested to see how it does. If it fails it calls invalidURL with the URL as a parameter, if it succeeds it calls validURL with the ID from the URL.
This script may not be exactly what you're after because it ONLY retrieves the ID of the video currently playing - but you should be able to modify it easily.
if (isset($_POST['l'])) {
$ytIDLen = 11;
$link = $_POST['l'];
if (preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $link)) {
$urlParts = parse_url($link);
//$scheme
//$host
//$path
//$query["v"]
if (isset($urlParts["scheme"])) {
if ( ($urlParts["scheme"] == "http" ) || ($urlParts["scheme"] == "https") ) {
//$scheme = "http";
} else invalidURL($link);
} //else $scheme = "http";
if (isset($urlParts["host"])) {
if ( ($urlParts["host"] == "www.youtube.com") || ($urlParts["host"] == "www.youtube.co.uk") || ($urlParts["host"] == "youtube.com") || ($urlParts["host"] == "youtube.co.uk")) {
//$host = "www.youtube.com";
if (isset($urlParts["path"])) {
if ($urlParts["path"] == "/watch") {
//$path = "/watch";
if (isset($urlParts["query"])) {
$query = array();
parse_str($urlParts["query"],$query);
if (isset($query["v"])) {
$query["v"] = preg_replace("/[^a-zA-Z0-9\s]/", "", $query["v"]);
if (strlen($query["v"]) == $ytIDLen) {
validUrl($query["v"]);
} else invalidURL($link);
} else invalidURL($link);
} else invalidURL($link);
} else invalidURL($link);
} else invalidURL($link);
} else invalidURL($link);
} else invalidURL($link);
} else invalidURL($link);
}
<?php
// Default page
if (!$_SERVER['QUERY_STRING']) $Page = "news";
// View
elseif (isset($_GET['newsID'])) $Page = "newsView";
elseif (isset($_GET['userID'])) $Page = "profile";
elseif (isset($_GET['messageID'])) $Page = "message";
elseif (isset($_GET['threadID'])) $Page = "thread";
elseif (isset($_GET['forumID'])) $Page = "forum";
elseif (isset($_GET['imgID'])) $Page = "imageView";
// Pages
elseif ($_GET['content'] == "search") $Page = "search";
elseif ($_GET['content'] == "gallery") $Page = "gallery";
elseif ($_GET['content'] == "forums") $Page = "forums";
elseif ($_GET['content'] == "messages") $Page = "messages";
many more...
// If page don't exist
else $Page = "error";
// Output page
include($config['PAGE_PATH'].$Page.'.php');
include($config['TEMPLATE_PATH'].$Page.'.html');
?>
This is some code my friend wrote years ago...
I'm wondering how safe this is and if I could make it a little cleaner?
Thanks.
As it is you who defines what pages are allowed to be included (white list), I cannot see any way to poison the $Page variable. So this seems pretty safe.
But you could clean it up using arrays such as:
$argToPage = array(
'newsID' => 'newsView',
'userID' => 'profile',
'messageID' => 'message',
'threadID' => 'thread',
'forumID' => 'forum',
'imgID' => 'imageView'
);
$contents = array(
'search',
'gallery',
'forums',
'messages'
);
$Page = null;
if (trim($_SERVER['QUERY_STRING']) == '') {
$Page = 'news';
} else {
foreach ($_GET as $key => $val) {
if (isset($argToPage[$key])) {
$Page = $argToPage[$key];
break;
}
}
}
if (is_null($Page) && isset($_GET['content']) && in_array($_GET['content'], $contents)) {
$Page = $contents[$_GET['content']];
} else {
$Page = 'error';
}
But that’s not much cleaner.
Well it's safe in the sense that the code sanitizes the parameter. People often do that (to disastrous results usually).
I'm not a big fan of this pattern however. By that I mean a single controller that includes files passed on a parameter. I much prefer to have a script per page and just include what's needed. Structurally I think it's better.
That being said, there's nothing fundamentally wrong with the above approach.
Just make sure you treat any data that comes from the user with extreme paranoia.
I'd be very cautious about cleaning this up any more than it is. Using a variable provided by user input in an include is a major security flaw. I would leave this as it is.
You could make an array with GET options as key and pages as values.. then use switch() statement. This should make the code cleaner. But as Cletus said, this isn't the best way to make a controller.