I'm using these docs to integrate a certain level of protection against session hijacking (bottom of page).
While I can understand the basics of what the article explains, I'm still new to all this and I'm just not able to pin-point what I should do.
I get how this would work:
<?php
session_start();
if (isset($_SESSION['HTTP_USER_AGENT']))
{
if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']))
{
/* Prompt for password */
exit;
}
}
else
{
$_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
}
?>
... and I kinda understand how this can make the above more secure:
<?php
$string = $_SERVER['HTTP_USER_AGENT'];
$string .= 'SHIFLETT';
/* Add any other data that is consistent */
$fingerprint = md5($string);
?>
However, I'm stuck at combining the two into one working script. The docs state:
we should pass this fingerprint as a URL variable.
What does that mean? Do I need to pass the fingerprint in the URL and then use $_GET on each page? Anyone who can help me combining these two snippets of code into one file that I can include in all my PHP files?
yes, you'd need to add this token to any urls and then check it on every page.
Basically what you're trying to accomplish is what cryptographers call a NONCE
(number used once). The idea is to generate the NONCE using the params and then validate that the params haven't been tampered with.
Ideally this should be a hash salted with something random
and used once. There are many libraries that will take care of it for you.
Remember that hashes are not symmetric, i.e you can't un-hash request variables to see that it's the same thing.
What you can do is take a hash of the parameters and compare the hashes. It's important to remember about salts, because without them you'd be susceptible to rainbow tables.
Also if you use $_REQUEST rather than $_GET you can reuse the same logic for both $_POST and $_GET
You can take a look at this library for example, http://fullthrottledevelopment.com/php-nonce-library
you can also borrow the nonce generating code from Wordpress
Related
So I have a PHP script that I myself have not designed but has a known security flaw. There's an admin panel where the admin can change various profile settings for every user, including their email address. The security flaw is such that anyone who knows the correct URL can change the email address of any registered user, including the admin, so long as they know the corresponding user's ID, by simply calculating the MD5 hash of the new email address they want to change to and issuing a GET request, without ever having to login as an admin. For example, entering the following URL into your browser:
admin.php?userid=1&md5hash=c59152a77c0bc073fe6f2a3141b99010&email=blah#blah.com
Would successfully update the email address of user with ID of "1" to blah#blah.com.
Now from what research I've done so far it appears that ditching MD5 hashes for a slight more proprietary/secure form of encryption would be the best/most secure way of going about this. But while I feel I have a fairly good understanding of PHP and have written a few basic scripts myself, since I haven't designed the particular script in question I'm not sure if this would actually be possible and/or plausible. Also, people do still use MD5 hashes in practice so there must exist another equally feasible way to protect aganist such exploits which led me to looking in to Apache's mod_rewrite module to block specific types of GET requests:
[redacted for irrelevance because of max link limit of 2 for new users]
So my questions would be:
1) Disregarding whether or not it would actually be feasible, would changing the PHP script to using some other form of encryption besides MD5 hashes be the BEST possible way to go about this? Or is there some simple function that I can add to the PHP script itself to protect from this kind of exploit?
2) If I went the route of using Apache's mod_rewrite as describe in the above URL, what would be the best method (out of THE_REQUEST, HTTP_REFERER, HTTP_COOKIE, REQUEST_URI, HTTP_USER_AGENT, QUERY_STRING, and/or REMOTE_ADDR, where REQUEST_METHOD is "GET")? Or is it even possible to do what I'm trying to do this way?
3) Someone had also suggested it may be possible to do what I am trying to do via a .htaccess file? Is this possible and would this method be anymore more or less secure than the other 2 mentioned?
The only thing to take into consideration is that via whichever method I end up using, obviously the server would have to still be able to issue the request for when the admin wants to legitimately change a user's email address. I just need to update it so that the general public cannot change a user's email address by simply typing the correct URL into their browser, given they know the correct user ID. Thanks in advance.
---> EDIT: Sorry I was neglecting to name the particular script because it is a publicly available one and I wasn't sure if this particular exploit was a known one but turns out it is, so I guess there's no harm in posting it here. The script is TorrentTrade (v2.08)- you can download the entire script at SourceForge (https://sourceforge.net/projects/torrenttrader/).
I've also copied and pasted the entirety of account-ce.php:
<?php
//
// TorrentTrader v2.x
// $LastChangedDate: 2012-09-28 20:35:06 +0100 (Fri, 28 Sep 2012) $
// $LastChangedBy: torrenttrader $
//
// http://www.torrenttrader.org
//
require_once("backend/functions.php");
dbconn();
$id = (int) $_GET["id"];
$md5 = $_GET["secret"];
$email = $_GET["email"];
if (!$id || !$md5 || !$email)
show_error_msg(T_("ERROR"), T_("MISSING_FORM_DATA"), 1);
$res = SQL_Query_exec("SELECT `editsecret` FROM `users` WHERE `enabled` = 'yes' AND `status` = 'confirmed' AND `editsecret` != '' AND `id` = '$id'");
$row = mysql_fetch_assoc($res);
if (!$row)
show_error_msg(T_("ERROR"), T_("NOTHING_FOUND"), 1);
$sec = $row["editsecret"];
if ($md5 != md5($sec . $email . $sec))
show_error_msg(T_("ERROR"), T_("NOTHING_FOUND"), 1);
SQL_Query_exec("UPDATE `users` SET `editsecret` = '', `email` = ".sqlesc($email)." WHERE `id` = '$id' AND `editsecret` = " . sqlesc($row["editsecret"]));
header("Refresh: 0; url=account.php");
header("Location: account.php");
?>
account-ce.php is the .php file referenced in following list of several known exploits (the first exploit is the only one i'm looking at right now):
https://www.exploit-db.com/exploits/21396/
I figured rather than sit around and wait for TorrentTrader to release a new update I would try and be proactive and fix some of the exploits myself.
You need to include in a session handler. I would like to assume that a user is required to login before being allowed to access any admin page, and that some sort of login credential or user id is saved to a session variable. To implement that you would need to have a script like this included on every page:
<?php
session_start();
if(!isset($_SESSION['uid'])){
$redirect_url='login.php';
if(isset($_SERVER['HTTP_REFERER'])){
$redirect_url.='?target='.urlencode($_SERVER['HTTP_REFERER']);
}
header('Location: '.$redirect_url);
}
?>
$_SESSION['uid'] is somewhat arbitrary and could be any session variable you deem sufficient for the security of your application. Note: session variables are connected to the user and are saved from page to page until the session is destroyed by calling session_destroy().
If the above script is executed prior to every page load, then when some evil hacker tries to trigger the script without being logged in, they will be redirected to login.php before the rest of the script/page is executed/loaded.
The current script is very insecure, but the insecurity does not arise from the use of the md5 hash. It would be really difficult to bolt security on top a system like this using just Apache configuration.
You might want to start by reading up on session security and cross site request forgery.
You need to write some code. And since you've not posted any code nor proposed a specific solution, your question is rather off topic here.
Alright guys I feel moderately stupid now, but thank you for the tips on using the session handler as that is what ultimately pointed me in the correct direction and to look in the right place. After digging around it seems as though that particular admin file (account-ce.php), for whatever reason, was just missing this:
loggedinonly();
which is defined in backend/functions.php as:
function loggedinonly() {
global $CURUSER;
if (!$CURUSER) {
header("Refresh: 0; url=account-login.php?returnto=" . urlencode($_SERVER["REQUEST_URI"]));
exit();
}
}
Also, I plan to read up on session security as you suggested so I can better familiarize myself with how sessions are used for this purpose. Thanks again! :)
how to secure your passing variables between the two pages via url? Let's assume i have a TEST variable in one page and i want to pass on that variable to test2.php page in the secure method?
How to convert test variable into Hash Method and pass on test2.php page via url?
for example
$test=$_POST['test'];
echo $row['test'];
Test
OR
Test
test2 Page
$test=$_REQUEST['test'];
By secure if you mean that you want the variable to be visible but you want to prevent users from changing the variable you can simply pass a hash along with the variable.
I.e.
$variable = 'abc';
$salt = 'your secret key';
$hash = md5($salt.$variable);
Page 2
On the second page you can rehash to see if the value has changed or not.
$variable = $_REQUEST['variable'];
$salt = 'your secret key';
$hash = md5($salt.$variable);
if($hash == $_REQUEST['hash']){
//do staff
}
However this will not hide the variables from URL, you can use other suggested answers for that.
It's not secure at all, because URLs (incl. GET arguments) are usually stored in httpd logs. So use POST for this, use SSL for transmission. If you need to use GET you can try to encrypt your data but mind that some web browsers got limits on max length of URL used, so too much data in GET may make them confused
Secure hash functions are one way, so no good for passing values. The most secure way to do this would be to use SSL, and POST your variables so they aren't displayed in the querystring/address bar.
Use SSL (for encrypted traffic - see here) and POST (see here).
The answer is simple.
either you need that variable in the url to identify the particular page and it's content
or it's internal site variable, like authorization information - it have to be passed via session.
For the first case you shouldn't "secure" this variable at all.
I'm learning php on my own now and I'm developing some simple sites using php include to ease the page creation process.
I've searched this website for ways to make it secure but, as a noob, I'm always afraid of messing up.
<?php
$siteArticles = array('instalacoes','galeria','regiao-e-historia','precos','contactos');
if( isset($_GET['page']) ){
if( in_array($_GET['page'], $siteArticles, true) && file_exists('pt/'.'rbs-article-'.$_GET['page'].'.php') ){
include('pt/'.'rbs-article-'.$_GET['page'].'.php');
}
}else{
include('pt/rbs-article-home.php');
}
?>
As you can see, it first checks if the page's allowed through the array and then add a prefix to the name file.
My question is, how secure is this?
Thank you for your time.
It is secure. The in_array() check is what makes it secure. It is not possible to perform a Local File Inclusion (LFI) attack on this code simply because the requested page must exactly match one of the elements in the whitelist array.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
PHP Pass variable to next page
Here is my current code:
$search = $_POST['s'];
$search = strtoupper($search);
$search = strip_tags($search);
$search = trim($search);
$search = mysql_real_escape_string($search);
I need to be able to carry on the $search variable to my second, third, etc, pages.
I'm a beginner in php and i'm sort of stuck here
It would appear that sessions are your friend here. In the simplest form, sessions will just put data in cookies that are sent to and from the user's browser. Make sure you call session_start() before you do anything with the session, this will start or resume the user's sessions. After that, you can use $_SESSION as a global associative array that will persist between pages.
Xander has already linked you to the docs, Here are some simple examples. Make sure you understand session_start() otherwise you'll have some bugs.
N.B. Do not use this basic session format for sensitive data. Look into using something like memcache to store the data and simply put the memcache key into $_SESSION. Also, consider encrypting the sessions. Those are more advanced things you should think about when dealing with user authentication/login
Assuming it is a search string, there is only sane method:
First, change the form's method to GET
Next, just pass your search variable in the query string using GET method.
The only modification you have to apply is urlencode()
So, the code should be
$query_string = 'search='.urlencode($_GET['search']);
echo "<a href='?page=2&$query_string'>page 2</a>";
producing an HTML code
page 2
so a user can click this link and you will have your search string back
While $_SESSION has been suggested, another option is to use a hidden field (with the same name and filled with the appropriate value) on subsequent generated pages. Then, when those pages are posted back, they too will have the field available in $_POSTS (this time supplied by the hidden field, not the original text field).
Advantages:
"Bound to the current page"; really good for some page context-sensitive stuff! (The session is scoped to the browser, not the page.)
Avoids the need for session/cookies (which is a non-issue if the session is already required for other purposes).
Disadvantages:
"Bound to the current page": value will be lost when navigated away from outside of back/next context. (As Bert notes, a slight modification can use this "breadcrumb" approach to alter the URL and use GET parameters, which can make the data universally persistent, at the expense of a "less pretty" URL.)
Data must be treated as untrusted and insecure, just like the original post.
Requires population of additional [hidden] fields.
Happy coding.
Use session_start() in each of the pages you want to access the search varaible
in the first page
$search = $_POST['s'];
$search = strtoupper($search);
$search = strip_tags($search);
$search = trim($search);
$search = mysql_real_escape_string($search);
set a session variable as
$_SESSION['searchStr']=$search
then in everyother page
session_start(); // at the very begining
if(isset($_SESSION['searchStr'])) {
$search=$_SESSION['searchStr']
}
I've got this code on my page:
header("Location: $page");
$page is passed to the script as a GET variable, do I need any security? (if so what)
I was going to just use addslashes() but that would stuff up the URL...
I could forward your users anywhere I like if I get them to click a link, which is definitely a big security flaw (Please login on www.yoursite.com?page=badsite.com). Now think of a scenario where badsite.com looks exactly like your site, except that it catches your user's credentials.
You're better off defining a $urls array in your code and passing only the index to an entry in that array, for example:
$urls = array(
'pageName1' => '/link/to/page/number/1',
'pageNumber2' => '/link/to/page/number/2',
'fancyPageName3' => '/link/to/page/number/3',
);
# Now your URL can look like this:
# www.yoursite.com?page=pageName1
This is a code injection vulnerability by the book. The user can enter any value he wants and your script will obey without any complaints.
But one of the most important rules – if even not the most important rule – is:
Never trust the user data!
So you should check what value has been passed and validate it. Even though a header injection vulnerability was fixed with PHP 4.4.2 and 5.1.2 respectivly, you can still enter any valid URI and the user who calls it would be redirected to it. Even such cryptic like ?page=%68%74%74%70%3a%2f%2f%65%76%69%6c%2e%65%78%61%6d%70%6c%65%2e%63%6f%6d%2f what’s URL encoded for ?page=http://evil.example.com/.
Yes, you do. Just because you or I can't immediately think of a way to take advantage of that little bit of code doesn't mean a more clever person can't. What you want to do is make sure that the redirect is going to a page that you deem accessible. Even this simple validation could work:
$safe_pages = array('index.php', 'login.php', 'signup.php');
if (in_array($page, $safe_pages)) {
header("Location: $page");
}
else {
echo 'That page is not accessible.';
}
Or, at the very least, define a whitelist of allowed URLs, and only forward the user if the URL they supplied is in the GET variable is in the list.