I have been tearing my hair with this one for a while.
index.php:
$(document).ready(function(){
$(".index_login_subm").click(function(){
uid = $("input:username").val();
pwd = $("input:password").val();
$.post("backend/login.php",{
uid:uid,
pwd:pwd
},function(data){
alert("Returned data: " + data);
});
return false;
});
});
login.php:
include("../../settings.php");
echo $uid;
echo $_POST['uid'];
none of the echoes returns anything.
settings.php:
foreach ($_POST as $key => $value) {
$$key = mysql_real_escape_string($value);
}
foreach ($_GET as $key => $value) {
$$key = mysql_real_escape_string($value);
}
The code works well if i comment the settings.php-include out (well, of course the echo $uid won't work), so it must be something the mysql_real_escape_string does. Anyone have any idea what I am doing wrong?
index.php also includes settings.php if that makes any difference.
EDIT: Posts below made me want to clarify; the paths are all correct. settings.php (and some other scripts) are all put outside of the root folder to make them inaccessible to a web user. They are working well when accessed by the scripts.
As described in the PHP manual, mysql_real_escape_string requires a real database connection established with mysql_connect (in the second parameter). If none exists, it tries to create one by simply calling mysql_connect(). If this does not work either, it returns false.
So, you would have to create a database connection first. But before you start doing this: note it is a really really bad idea to simply take every POST and GET variable and spit it into the global namespace.
First, I would avoid the variable variables, they're really not necessary in this context and they make for a moving target if you're not sure what keys are actually reaching that script, and they're bad practice due to the security issues that they create. Instead, I would put the escaped values within settings.php into an array. Then you can simply var_dump() the array after including settings.php, and see what was available.
function extract_requests(){
$res = array();
foreach ($_REQUEST as $key => $value) {
if($key){
$res[$key] = $value;
}
}
return $res;
}
Second, manually call login.php with the correct url string, exactly what you would want the javascript to call for, so that you can be sure that any behavioral problems aren't coming from the login.php script itself.
http://localhost/somewhere/login.php?uid=50
include("../../settings.php");
$input = extract_requests();
var_dump($input);
echo $input['uid'];
echo $_POST['uid'];
Finally, I would check your data that you are sending in post(). I'm not a javascript guru, but
uid:uid,
pwd:pwd
looks like it would cause issues to me, so it'd be good to make sure that your data is being sent in the way that you expect.
$(document).ready(function(){
$(".index_login_subm").click(function(){
uid = $("input:username").val();
pwd = $("input:password").val();
var inputData = {uid:uid, pwd:pwd};
alert(uid+" "+pwd);
// If you have a console.log() available, just use it on inputData here instead.
$.post("backend/login.php",inputData
,function(data){
alert("Returned data: " + data);
});
return false;
});
});
Edit: Based on the mysql_real_escape_string-only-when-a-connection-has-been-initiated thing, I've removed the escaping from the code above, because really, you'll be better off escaping the stuff right before you put it into the sql.
In addition, keep in mind that using javascript for the login WILL come back to bite you, with functionality as basic and necessary as login, you don't want a javascript error to break any ability for a viewer to log in. Here it will do so.
Is there a valid mysql link?
string mysql_real_escape_string ( string $unescaped_string
[, resource $link_identifier ] )
If the link identifier is not specified, the last link opened by mysql_connect() is assumed. If no such link is found, it will try to create one as if mysql_connect() was called with no arguments. If no connection is found or established, an E_WARNING level error is generated.
May be your error reporting setting ignores E_WARNING level errors.
index.php obviously is located in root directory of website, but the script that you are calling is in "backend" catalog.
Try using include_once instead of include and use absolute paths, not relative.
Example:
<?php
include_once($_SERVER['DOCUMENT_ROOT']."/settings.php"); //absolute path to file
echo $uid;
echo $_POST['uid'];
?>
Using absolute paths will make life easier for you if you move files around.
Related
this is my first time using PHP in a real project environment. The project is pretty simple, take an existing, working PHP site and update the HTML to be consistent with HTML5. After designing the HTML, I am inserting the PHP from the previous site. This works most of the time, but I get a few errors. For instance:
<?
$sec = $_GET['sec'];
if ($sec == "1") {
echo ('Success!');
}
?>
Is causing the error:
Notice: Undefined index: sec in /file_that_holds_site_build.
Of course that is only if the url doesn't include the append tag (=1) that alerts the message.
So the question is this, what am I missing that causes the $GET when there is no $sec? How do I eliminate this error on first page load?
You're getting that notice because you're trying to access an array index that doesn't exist in some scenarios. Here's how you should be getting the data out of the request.
$sec = array_key_exists('sec', $_GET) ? $_GET['sec'] : null;
Thanks to everyone who provided possible answers to this question. It was Daniel that came up with the easiest fix. Again, I am just adjusting someone else's code to work, so a universal solve would involve too much of my own writing. To the point, the final code looks like this:
<?
if (isset($_GET["sec"])){
$sec = $_GET['sec'];
if ($sec == "1") {
echo ('Success! Your username and password have been sent via email.');
}}
?>
Notice the added if statement. As I said in a comment to Daniel, SO SIMPLE!
Thanks again for everyone's help. I hope to be likewise of service to you all soon.
Simple just use isset($_GET['sec']) to check for the parameter 'sec' before using it in the php code. That should eliminate the error. This is quite trivial I suppose.
I often simply extract() the wohle $_GET super global and then either get the desired variable or not. As a kind of "declaration" I initialize each expected variable first with false. This way I find it much easier to handle than individually doing a check like if(isset($_GET['element'])) ...
$var1=$var2=$var3=false; // my desired variables
extract($_GET); // everything I get from the form
// or: extract($_REQUEST);
if ($var1) { /* do something with it */ }
Possible security risk:
Of course you should be aware that everybody could simply include their own variable as an argument to he page ...
I am thinking of making a loop to gather all my $_POST variables and assign them to dynamically named variables.Something like this (not tested)
for($i; $i <= $_POST[].length; $i++){
${$_POST[i]} = $_POST[i]
}
But I am wondering about the security of something like this. This would then create a variable in the system for every bit of post data sent to the page. Can that variable be damaging even if the script I write doesn't reference it? Is this the type of thing I should avoid entirely? I have some pages that send quite a few variables and a script like this would prevent a whole lot of writing, but is it safe enough?
Yes there are possible security risks.
Say you have a variable $is_admin defined earlier in the code that gives someone admin abilities. If someone POSTS to that page with
$_POST['is_admin'] = true;
Then $is_admin is now true. Not good.
What's wrong with using $_POST?
Yes there can be security concerns/problems, for example one could overwrite any local variables which are already set, like database, config values ect.
So something like this should be avoided:
$yourImportantVar = 'Something relies on this';
//User POSTS yourImportantVar=overwritten
foreach ($_POST as $key => $value) {
$$key = $value;
}
echo $yourImportantVar; //overwritten
But if you want to implement a loop to save a chunk of code, you could create an allowed array which you loop over and extract out the value from the $_POST.
foreach (array(
'name',
'address',
'somethingelse',
'ect'
) as $key) {
$$key = isset($_POST[$key]) ? $_POST[$key] : null;
}
This is a very bad idea for security and maintainability. Simplified example why...
<?php
if (someRandomSessionCheck()) {
$isAdminUser = true;
}
if ($isAdminUser) {
// give access to everything
}
?>
Someone could post to the page with a variable "isAdminUser=1" and would have access to everything.
Another reason it is a bad idea is you can't clearly see from the script where your variables are created. This reduces maintainability of the script. What if you now want to run the script but instead need to get the data from somewhere else and not a POST?
Only issue I can think of at the moment is when that overwrites the existing variable in the scope. This can be very unsafe depending on what you do with it. Think about the variable being the URL you are doing a HTTP request to. Or worse, some flag variable which accesses some critical part of your code.
I will post an example that speaks about HTTP request:
<?php
$url = "http://safe/url/to/POSTto";
$var = array("url" => "http://www.mysite.com/url"); //assume this is $_POST
foreach($var as $key => $value){
${$key} = $value;
}
//now upon the HttpRequest, your site can receive the (critical) data which was actually meant for the safe site.
?>
EDIT: #Galen has posted about the flag variable I was talking about, so may be I need not post any example to highlight the problem.
PHP had a feature (using the term loosely) called register_globals. It has since been deprecated (PHP 5.3) and removed (PHP 5.4), but it mirrored the functionality for which you are looking. It performed the same thing as the PHP function extract() does, which sets variables in the current scope with names of the keys and values of the matching array values.
This is most definitely a security risk. Consider the example of a poor check for authentication:
if($is_logged_in) {
// Allow execution of destructive actions
}
If this feature was enabled (or you mimicked it), a malicious user would be able to set the variable $is_logged_in and bypass the login screen. Don't worry about saving typing. If you need to copy and paste a code block like this at the beginning of your files:
$something = $_POST['something'];
$another = $_POST['another'];
$stuff = $_POST['stuff'];
//etc.
Not only is it much more secure, but it doesn't leave developers (who aren't expecting register_globals) puzzled when undeclared variables start being used. Also, the fact that PHP has removed it and there are plenty of arguments against its use should be evidence enough.
<?php
/* Suppose that $var_array is an array returned from
wddx_deserialize */
$size = "large";
$var_array = array("color" => "blue",
"size" => "medium",
"shape" => "sphere");
extract($var_array, EXTR_PREFIX_SAME, "wddx");
echo "$color, $size, $shape, $wddx_size\n";
?>
Please check this. Same thing what you are going do by using loop. may help you
You are basically implementing extract($_POST, EXTR_OVERWRITE) which will overwrite any already existing variables. The manual already warns to use extract in a way you do:
Do not use extract() on untrusted data, like user input (i.e. $_GET, $_FILES, etc.). If you do, for example if you want to run old code that relies on register_globals temporarily, make sure you use one of the non-overwriting extract_type values such as EXTR_SKIP and be aware that you should extract in the same order that's defined in variables_order within the php.ini.
This can result in overwriting essential and sensitive variables, including those that cannot really be modified directly like $_SESSION, $_SERVER, and $GLOBALS:
POST /foo.php HTTP/1.1
Content-Type: application/x-www-urlencoded
_SESSION[user]=admin
This would have the same effect as $_SESSION = array('user'=>'admin').
This is a bit of an oddity for me. PHP is my forte, and I can normally figure out any issue I encounter.
I have a custom framework that I have been using for years. I have taken it upon myself to rewrite it, and I'm doing everything essentially the same that I was before. The problem lies in the following construct:
function ModPages_GetPage() {
$page = ModPages_GetPageByName($_GET['page_name']);
if($page != false) {
include(TPL_DIR.'pages/pages.view.php');
} else {
ErrorMessage('Invalid Page', 'The selected page could not be found.');
}
}
function ModPages_GetPageByName($page_name = null) {
$db = new Database;
$query = '
SELECT *
FROM pages
WHERE page_name = "'.CleanStr($page_name).'"
AND page_enabled = "yes"
LIMIT 1
';
$page = $db->GetRow($query);
return $page;
}
This code is being called with 'home' for the value of $_GET['page_name']. The call to ModPages_GetPageByName() is working fine, but the value of $page in ModPages_GetPage() isn't getting set. Matter of fact, any debugging statements thrown in after that call are failing to display anything.
I have display_errors set to on, and error_reporting set to E_ALL. I get a couple notices from my Database class, but that's it.
Running the script at a shell fails to produce any errors. When using strace, I do see the process spits out an 'exit_group(255)'.
This one has me quite baffled. I could sure use some direction on this.
I would think it's your query, shouldn't you just return the page name instead of star? as star (*) would return an array which is probably being passed back as the value? just my guess.
$query = '
SELECT *
FROM pages
WHERE page_name = "'.CleanStr($page_name).'"
AND page_enabled = "yes"
LIMIT 1
';
if you do a print_r on the $page return I would think it should be an array
$page = $db->GetRow($query);
echo "Page:<pre>".print_r($page,true)."</pre><br />\n";
Then maybe return something like this
return $page['page_name_field'];
ok before we get to a solution can we first make sure that before setting the $page variable, first just echo $_GET['page_name'] to see if there is a value being received.
PK
Does your script stop right after your database call, or just doesn't display any output?
If the first is true, then it looks like a fatal error. With E_ALL, it should be displayed, are you sure both display_errors and error_reporting are as you say at that point, and that the GetRow function doesn't alter them in any way? If so, maybe there's something in the Apache error log (PHP errors are sometimes logged there).
If the latter is true I'm thinking about an exception being thrown in a method that is being called, and caught in a higher level function. To check this you can put the database call (ie: the point where things go wrong) inside a try/catch block and see if you reach the catch block.
I would try following:
replace $_GET with $_REQUEST (maybe your form is using POST?)
do a print_r to check contents of your variables.
use mysql_error to view any errors, or print your mysql query in your browser, copy/paste it in phpmyadmin, is it returning anything? error.. data?
something similar happend to me once, my framework was encoded in ANSI and my calling php file was UTF8+BOM... I changed everything to UTF8+BOM and it worked.
try also different browser, I know it might not be a browser problem, but it might be that your script is cached somewhere.
are you using some caching? like eaccelerator?
Are those functions in a class? If so, you will need $page = $this->ModPages_GetPageByName().
Also I would echo out the argument and the sql statment in ModPages_GetPageByName(). This way you can verify that it isn't a SQL error.
I can't say for sure why your code isn't working, but I can make some suggestions that might help in locating the error.
The first thing I notice is you don't check that $db actually contains a valid database. I don't know the details of your Database object but I'm assuming there's some mechanism in there for checking if it's actually connected to the database. You should use that to determine if the database is connected before running queries on it.
$db = new Database ();
if ($db -> isConnected ())
{
$query = 'SELECT * (etc etc etc)';
// ...
}
else
{
// Put some kind of DB connection error notification or throw an exception here
}
Just on a stylistic note, you don't need to store the results of your DB lookup before returning it, unless you're planning on doing some processing on the result before returning it. You can just return the lookup directly. Of course that's just a stylistic choice, but it saves a line or two :)
return ($db->GetRow($query));
After you run your getpage function, I'd strongly recommend var_dump()ing the result. Even if your function returned NULL, you'll still see this in the var_dump. If in doubt, dump it out :). I'd also recommend installing xdebug to make the var_dump output more readable.
$page = ModPages_GetPageByName($_GET['page_name']);
var_dump ($page);
I would also strongly recommending var_dumping your query before you execute just to make absolutely sure that you're running the query you think you're running. Copy and paste the outputted query into sqlyog or phpmyadmin or whatever you use for interactive access to your database and make sure it returns what you think it should return.
Other things to check, is the page you're trying to return actually set page_enabled='yes'? Does the page_enabled column actually store the value as 'yes', or is it a bool or an integer or something else? Is magic quotes enabled or disabled? If they're in one state when you think they're in the other they can cause confusion. Are errors actually being reported to the browser? Add a line at the top of your script that's guaranteed to fail just to make sure, like an attempted foreach on an integer. If you don't see an error, then maybe error reporting isn't configured properly. I know those are obvious questions but I also know how easy it is to overlook the obvious if you're not getting what you expect out of a query.
Are you sure $page is not set, or is it just that your debug instructions don't print anything? Try logging to a file or a database instead; maybe your code triggered output buffering or something like that.
Also, you are calling ModPages_GetPageByName before declaring it. That is usually okay, but might not be in special circumstances (e.g. when the code is wrapped in an if block). Try swapping the two.
Also, check your environment and disable opcode caching and other possible error sources. APC for example can call the old version of the script long after you changed the PHP file.
While some of you have put extra effort into responding to this, nobody has been able to see the full picture, even given the details I have provided. I have been unable to trace the issue back to its source, but have moved on to a different project.
I've taken a bit of a memcache script that i've used previously without issue, but on the new page, I don't get any response.
the memcache is in a function which is included from another page.
what I do is put the md5 hash the mysql query and store that as the key for the memcached data. if the key isn't in memcache, then I go, create the data, put it into memcache and return it.
I think the code is fairly simple. Here are the important bits (it's a large page creating the output, so you don't need all that, though the 'return' I think is important as I fear that might be where I'm screwing up.
I call the function with
$outList.= outData($getList);
where $getList is a mysql query
The $outList function is
<?php
#$memcache = new Memcache;
#$memcache->connect("localhost",11211);
function outData($getList)
{
$memVal = #$memcache->get(MD5($getList));
if($memVal=='')
{
$results=mysql_query($getList)or die(mysql_error());
// then I do a bunch of stuff with the data
#$memcache->set(MD5($getList), $memVal, false, 60000);
}
return $memVal;
}
I can display all the stuff to create $memVal, but i suspect the error is in the if line, but the same code is used on another page without issues.
Anything look wrong with this?
with all those #'s suppressing errors, there's no way to know what is failing.
I ran it - sans the #'s and the answer popped right up though - on the line:
$memVal = #$memcache->get(MD5($getList));
#Notice: Undefined variable: memcache in /home/topbit/736160.php on line 9
Where does it get the variable $memcache ? It's not passed into the function.
The problem is scope. When in a function, variables in global scope do not automatically get passed on, you will have to add them as parameters to the function.
function outData($memcache, $getList)
Is there any way to safely include pages without putting them all in an array?
if (preg_match('/^[a-z0-9]+/', $_GET['page'])) {
$page = $_GET['page'].".php";
$tpl = $_GET['page'].".html";
if (file_exists($page)) include($page);
if (file_exists($tpl)) include($tpl);
}
What should I add to make this pretty safe?
I'm doing it this way bacause I don't like having to include stuff that has to be included on all pages. The "include header > content > include footer"-way. I don't wanna use any template engines/frameworks neither.
Thanks.
The weakness in your current implementation is that …
the regular expression just tests the beginning of the string, so “images/../../secret” would pass, and
without further validation, “index” would also be a valid value and would cause a recursion.
To make your implementation safe, it’s a good practice to put everything, that’s intended to be included, in its own directory (e.g. “includes” and “templates”). Based on this, you just have to ensure that there is no way out of this directory.
if (preg_match('/^[a-z0-9]+$/', $_GET['page'])) {
$page = realpath('includes/'.$_GET['page'].'.php');
$tpl = realpath('templates/'.$_GET['page'].'.html');
if ($page && $tpl) {
include $page;
include $tpl;
} else {
// log error!
}
} else {
// log error!
}
Note: realpath returns the absolute path to the given relative path if file exists and false otherwise. So file_exists is not necessary.
Despite what you stated about not wanting to store a list of available pages in an array it is likely going to be the best, non-db, solution.
$availFiles = array('index.php', 'forum.php');
if(in_array($_GET['page'].".php", $availFiles))
{
//Good
}
else
{
//Not Good
}
You could easily build the array dynamicly with either DB queries or by reading a file, or even reading the contents of a directory and filtering out the things you don't want available.
You should never use user supplied information for includes. You should always have some sort of request handler that does this for you. While a regular expression may filter somethings it will not filter everything.
If you do not want your site to get hacked you do not allow your users to control the flow of the application by designating an include.
I agree with Unkwntech. This is such an insecure way to include files into your website, I wish PHP programmers would do away with it altogether. Even so, an array with all possible matches is certainly safer. However, You'll find that the MVC pattern works better and it is more secure. I'd download code igniter and take a tutorial or two, you'll love it for the same reason you wanna use dynamic includes.