I have a domain. Let's call it www.example.com for this question. Thanks to the wonders of how I have things setup and/or Wordpress, if I were to type in example.com it would redirect me to www.example.com and Wordpress would do its thing. I also have a subdomain called members.example.com in which I have built a rather elaborate system using CodeIgniter into the folder /members off of public_html.
In a perfect world, what I would like to do is be able to put some php code into Wordpress that would allow someone reading the "www" side of things to be able to determine if they are also currently logged in on the "members" side of things. I know the CodeIgniter session persists so that whenever I jump on to the members side, I'm still logged in. My first thought was to put together a quick page in CI that did this:
public function isLogged()
{
$x = "off";
if ($this->session->userdata('memberKey') != 0) {
$x = "on";
}
echo $x;
}
Then, whenever I would run the link members.example.com/login/isLogged , I would always get either "off" or "on" to determine if I was in or not. The memberKey would be either 0 for not logged in, or a number based on someone's corresponding key in the database. My hope was that I would be able to run something like THIS from the www side (within Wordpress)
$homepage = file_get_contents('http://members.example.com/login/isLogged');
...and I would be able to act on whether the person was logged in or not. Well, obviously the flaw here is that when I call the above function, I'm always going to get "off" as my result because I'm making that call from the server, and not from the client in question that needs this information.
What would be the most elegant solution to being able to read this information? Is there a way to read cookies cross platform?
Well, I realized that I worked through the process, and I don't want to leave anyone hanging, so my solution was this:
First, I know that for my cookies, I went into codeIgniter's /application/config/config.php and put the following in as my cookie domain (line 269 - and I've changed the domain name here to protect my client, obviously)
$config['cookie_domain'] = ".example.com";
I'm being careful here, and using the "." before the domain to cover all subdomains.
I also know that I'm writing my session data to a database. So, on the Wordpress side of things, I put in this code at a certain location:
$memCCinfo = unserialize(stripslashes($_COOKIE['ci_session'])); // Changed for OBVIOUS reasons
$mysqli = mysql_connect('localhost', 'NAME', 'PASSWORD'); // Changed for OBVIOUS reasons
$mysqlDatabase = mysql_select_db('DATABASE',$mysqli); // Changed for OBVIOUS reasons
$isLoggedCC = "off";
$sessInfoQry = mysql_query('SELECT user_data FROM ci_sessions WHERE session_id = "' . $memCCinfo['session_id'] .'"',$mysqli);
while ($sessInfo = mysql_fetch_assoc($sessInfoQry)) {
$breakOutInfo = unserialize(stripslashes($sessInfo['user_data']));
if (is_array($breakOutInfo)) { $isLoggedCC = "on"; }
}
Then, I now have a way to determine if a member is logged on to the "members" side or not. Question is... are there any holes in what I'm doing that I should worry about?
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! :)
This seems to be a pretty popular question, however I have not been able to resolve my issue with any of the suggested solutions online. What's going on is, I am trying to set up a site locally that is already deployed on a server and working fine. Aside from changing the
$config['cookie_path'] = "/";
and the cookie_secure to false, I haven't changed much else.
The problem I am having is that the variable I am storing in $_SESSION doesn't seem to stick after redirecting to the main page. The workflow is:
Create a session, store session cookie in a table (this works fine, I can see it in the DB)
Store this var, which is passed in the URL to $_SESSION, you can echo it after the fact and it's there, just not after the redirect
Redirect to main page, get the previously stored var for other stuff
I have something like this:
in auth.php
....
$this->session_var->store_var($var);
// redirect to drupal homepage
header('Location: /');
exit;
in session_var.php
public function store_var($var)
{
$_SESSION[$this->key] = $var;
}
And once it redirects, I've printed throughout the workflow to check the $_SESSION and it's no longer there. Where this functions fine in multiple servers, I think this is probably a configuration issue, right??
I just don't know what other configurations to look at.
session.save_path has no value, after looking at phpinfo locally nor in any of the servers. So how is the session being saved?
cookie_domain is empty, cookie path is '/', httponly is false
If I can provide any other information, I'd be happy to. Any suggestions are welcome.
I tried coding in the following way for one of the website over a localhost. say localhost/abc:
<?php
session_start();
$_SESSION['name']=$row['name']
?>
The output was good. but when the same code was used for another webpage over the same localhost say localhost/xyz. Then there was ambiguity between names. As if I need to distinguish between the sessions for xyz and abc.
So I tried this:
<?php
session_id(226);
session_name(veer);
session_start();
.
.
.//connection with database and all
.
$_SESSION['name']=$row['name'];
echo($_SESSION['name']);
?>
When another machine logged in over the same server then the session I created was accessible by that machine for same webpage.
Is there any solution. Or how to distinguish between two sessions.?
To put it in simple terms... you are accessing same memory area of server when you access two different sites on same web server using the same browser instance. Thus
http://localhost/xyz and http://localhost/abc are referring to the same site localhost and thus you will not start another session by session_start() but instead resume it. You can alternatively create virtual hosts as Jon said but for the sake of testing which I guess you are, just use different browsers.
Also, you cannot share session over different machines normally, so I think that's your logical mistake. Alternatively try
session_start();
echo (session_id());
on the top of the page and see if you are starting or resuming the same session which I think you are not. I think your page is storing same data in different sessions which you are mistaken as same session.
Use session_regenerate_id(); method in the second file(xyz).
this?
<?php
session_start();
if (!isset($_SESSION['xyz'])) { $_SESSION['xyz'] = array(); }
$_SESSION['xyz']['name'] = $row['name'];
?>
sometimes instead of doing the above i just prefix my session keys
example: $_SESSION['xyz_name'];
I did that after i realized that my CPanel has been used some sessions of its own
that caused a conflict to mine.
Requests from the same user agent to the same web server will share the same session, barring explicit configuration that depends on your exact server setup.
Normally this problem is avoided because the "other webpage" would actually be on another domain entirely, so the session cookie (and by extension the session data) would not be shared. This is also what you should do if you want to run separate applications independently on localhost: set up separate virtual hosts on separate internal domains.
You could also solve the problem purely in code by not using $_SESSION directly to store your data but a subkey based on some differentiating factor such as $_SESSION['SCRIPT_NAME']. A very simple example:
$sessionKey = "default";
if (strpos($_SESSION['SCRIPT_NAME'], "dir1/")) {
$sessionKey = "dir1";
}
else if (strpos($_SESSION['SCRIPT_NAME'], "dir2/")) {
$sessionKey = "dir2";
}
$_SESSION[$sessionKey]['mydata'] = 'foo';
However this last one is a really inelegant solution which I would not recommend.
At my work I often need to figure out where our traffic comes from. We buy google ads and that traffic gets identified by a query string in the url. (mywebsite.com/?x="google_ad_group_4").
On every page I include some sessions stuff that sets $_SESSION['x'] to $_GET['x'] if $_GET['x'] is there. If there is no $_GET['x'] I go through some other options to see where they came from and set that in $_SESSION['x']:
$refurl = parse_url($_SERVER['HTTP_REFERER']);
$query = $refurl['query'];
parse_str($query, $result);
if (isset($result['q'])&& strstr($_SERVER['HTTP_REFERER'],'google')) {
$_SESSION['x'] = 'G-'.str_replace('\\"',"X",$result['q']);
}elseif (isset($result['p'])&& strstr($_SERVER['HTTP_REFERER'],'yahoo')) {
$_SESSION['x'] = 'Y-'.$result['p'];
//took out bing, aol, ask etc in the name of brevity
}else{
if ($refurl['host']){
$_SESSION['x'] = $_SESSION['x'].'_ref-'.$refurl['host'];
}
}
This way I can append the search query that brought the user to the site and what search engine they used. I log the incoming $_SESSION['x']'s.
Many users are coming in with $_SESSION['x']'s of "_ref-mywebsite.com" which doesn't make sense, if they were coming from my own domain, they'd have already had a $_SESSION['x'] set on whatever page they'd been on. Is this because they have their browser's security turned up high or something?
Am I missing something obvious? Is there a smarter way to do this?
You can get the referrer like this
echo $_SERVER['HTTP_REFERER'];
But as mentioned in comment, it can easily be manipulated.
Unless the client (the browser) passes you the "HTTP_REFERER" in the heading, you won't get it. And that depends on the site they come from.
I don't know what your workflow is like, but one thing you can do is get it with JavaScript and pass it to your PHP script. Hope this helps.
I think that a possible scenario is:
A new visitor comes to the website with normal referrer;
He closes his browser(this clears his session cookie) with the website's tab opened;
Reopens the browser with the website restored in old tab;
Clicks on any link on the page and gets to another page with referrer from same domain and clean session.
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.