How to fix the base64_encode and base64_decode to avoid injection? - wordpress

I am using a theme option for WordPress with base64_encode and base64_decode. Since base64_ risk for the code injection, need to change that code.
Actually I have no idea how to do that.
The code contain base64_encode is like this:
new AjaxUpload(<?php echo ($value['id']); ?>, {
action: '<?php echo THEME_DIR; ?>/admin/upload-file.php',
name: '<?php echo ($upload_security)?>',
data: {
upload_path : '<?php echo base64_encode(UPFW_UPLOADS_DIR); ?>'
},
onSubmit: function(file, ext){
//Check if file is an image
if (! (ext && /^(JPG|PNG|GIF|jpg|png|jpeg|gif)$/.test(ext))){
// extension is not allowed
status.text('Only JPG, PNG or GIF files are allowed');
return false;
}
jQuery('#<?php echo ($value['id']); ?>loader').addClass('activeload');
},
onComplete: function(file, response){
//On completion clear the status
status.text('');
//Successful upload
if(response==="success"){
$file = file.toLowerCase().replace(/ /g,'_').replace(/(_)\1+/g, '_').replace(/[^\w\(\).-]/gi,'_').replace(/__/g,'_').replace(/#/g, '_');
//Preview uploaded file
jQuery('#<?php echo ($value['id']); ?>preview').removeClass('uploaderror');
jQuery('#<?php echo ($value['id']); ?>preview').html('<img class="preview" src="<?php echo UPFW_UPLOADS_URL; ?>/'+$file+'" alt="<?php echo ($value['id']); ?> Image" />').addClass('success');
//Add image source to hidden input
jQuery('input#<?php echo ($value['id']); ?>').attr('value', '<?php echo UPFW_UPLOADS_URL; ?>/'+$file);
//Append thumbnail to gallery
jQuery('.thumbs').append('<a class="preview" href="<?php echo UPFW_UPLOADS_URL; ?>/'+$file+'"><img src="<?php echo UPFW_UPLOADS_URL; ?>/'+$file+'" /></a>');
//Save Me Fool
activate_save_animation();
} else{
//Something went wrong
jQuery('#<?php echo ($value['id']); ?>preview').text(file+' did not upload. Please try again.').addClass('uploaderror');
}
jQuery('#<?php echo ($value['id']); ?>loader').removeClass('activeload');
}
});
and the code content base64_decode is like this:
<?php
//Upload Security
$upload_security = md5($_SERVER['SERVER_ADDR']);
$uploaddir = base64_decode( $_REQUEST['upload_path'] ) . "/";
if( $_FILES[$upload_security] ):
$file = $_FILES[$upload_security];
$file = $uploaddir . strtolower(str_replace('__', '_', str_replace('#', '_', str_replace(' ', '_', basename($file['name'])))));
if (move_uploaded_file( $_FILES[$upload_security]['tmp_name'], $file)):
if(chmod($file,0777)):
echo "success";
else:
echo "error".$_FILES[$upload_security]['tmp_name'];
endif;
else:
echo "error".$_FILES[$upload_security]['tmp_name'];
endif;
endif;
?>
Would really appreciate your help.
Thank you.

Moving this from comments to an answer because this is going to get detailed and hopefully it'll become one of those cool Stack Overflow answers people link to all over the place and I'll get all sorts of upvotes and job offers and free drinks at the local bar. Well, I'll settle for the upvotes. Or the drinks.
Hey, I'm home today. The baby's asleep. I've got some time to ramble.
base64
Things weren't always as standardized as they are now. For example, the computer I take my username from, the Commodore 64, encoded characters using Commodore's proprietary PETSCII instead of ASCII. Lots of IBM Mainframes used their own EBCDIC encoding, which was based on six-bit character encodings their older machines used. Six bits can represent the values 0-63, or 64 distinct characters.
There's also the fact that some characters are special. They represent things like tabs, invisible spaces, and data transfer control protocols. ASCII 7 is "BEL" and that would actually ring the bell on an ASR-33 teletype. But those characters are in different places and mean different things in the different character encodings.
To make sure email & Usenet worked between these ancient systems and the current standards, early Internet pioneers came up with base64 encoding, which encoded 8-bit data six bits at a time into a set of 64 "printable" (not control) characters. You still needed to decode it to eight bits at the other end, but any old 6-bit or 7-bit systems it passed through along the way couldn't mess it up.
So all base64_encode() and base64_decode() do is encode the data using a limited, standard set of characters. It's handy for storing binary data (like photos) in databases or encoding them in HTML <img> tags. It's a way around backslash escaping, which used to happen automatically sometimes in PHP. But it's not a kind of encryption or anything.
But base64 encoded text is friendly to old mainframes, not people. For example, "Hello World" becomes SGVsbG8gV29ybGQ=. And that's why people use it to hide evil code or things they don't want changed like copyright notices or links back to the theme builder's web site:
echo 'Get great answers at Stack Overflow';
becomes
eval(base64_decode('ZWNobyAnR2V0IGdyZWF0IGFuc3dlcnMgYXQgPGEgaHJlZj0iaHR0cDovL3N0YWNrb3ZlcmZsb3cu
Y29tIj5TdGFjayBPdmVyZmxvdzwvYT4nOw=='));
And that is what the WordPress.org theme review team doesn't want to see. That's the use of base64 they're concerned about. It's a way for people to obfuscate code, make it so people can't read it. It's risky, and it's against the spirit of open source.
Your example code
This code is dangerous:
data: {
upload_path : '<?php echo base64_encode(UPFW_UPLOADS_DIR); ?>'
}
UPFW_UPLOADS_DIR is a directory somewhere on your server, and it's just being base64 encoded. That means your JavaScript AJAX code sends a variable upload_path which has a value like L3Zhci93d3cvbXlzaXRlL3dwLWNvbnRlbnQvdXBsb2Fkcw== -- that's just /var/www/mysite/wp-content/uploads base64 encoded.
The PHP code on the server takes that value and decodes it:
$uploaddir = base64_decode( $_REQUEST['upload_path'] ) . "/";
then attaches the filename to that directory to build a file path:
$file = $uploaddir . strtolower(str_replace('__', '_', str_replace('#', '_', str_replace(' ', '_', basename($file['name'])))));
and moves the uploaded file to that new path:
if (move_uploaded_file( $_FILES[$upload_security]['tmp_name'], $file)):
This code is a back door to upload files anywhere your web server software(Apache, Nginx or similar) can write to on the disk
I could post to your /admin/upload-file.php script and give it an upload_dir like L3Zhci93d3cvbXlzaXRlL2luZGV4LnBocA==. Your code would take my file and replace WordPress's main index.php file (/var/www/mysite/index.php) with it.
Instead of sending the upload_dir to the browser and trusting you'll get back the same thing you sent, your code should just use UPFW_UPLOADS_DIR directly:
$file = UPFW_UPLOADS_DIR . '/' . strtolower(str_replace('__', '_', str_replace('#', '_', str_replace(' ', '_', basename($file['name'])))));
It looks like your upload code is a stand-alone PHP script instead of using WordPress's AJAX functionality. If that's the way you'd prefer it, you can bootstrap WordPress by including wp-load.php so its variables, constants (like UPFW_UPLOADS_DIR), and API are available in your code.

Related

How to log queries that go to wp-admin/admin-ajax.php

I need a way to log queries that go to wp-admin/admin-ajax.php (extra points if I can log specific queries). This will help to troubleshoot some stuff happening in production with my custom plugin.
Scenario
I’ve been working on a plugin which varies the message a user gets depending on the time on the server. The process also depends on other settings retrieved from the server.
The plugin uses javascript to call admin-ajax.php to do the magic. (due to the nature of the plugin, I don’t think I can or should use straight client-side JS for this).
In development it works reliably but in production there are definitely situations where the result returned is unexpected.
This has led me to think that the results of admin-ajax.php are sometimes cached, I need a way to validate my current guess by doing some logging of responses from the production server.
Put this code in you theme functions.php
A log file ajaxlog.txt will now be created in your template folder
add_action( 'admin_init', 'my_ajax_checker', 10, 2);
function my_ajax_checker() {
$file = dirname(__FILE__) . '/ajaxlog.txt';
$actual_link = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$message = $actual_link . " - " . date('m/d/Y h:i:s a', time()) . " - " .$_SERVER['REMOTE_ADDR'] . "\r\n" ;
file_put_contents($file, $message, FILE_APPEND);
}
}
Send requests with a unique identifier and then count if they are all in the log.

How to return binary data from custom wordpress rest api endpoint

I am writing a custom endpoint for a REST api in wordpress, following the guide here: https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/
I am able to write a endpoint that returns json data. But how can I write an endpoint that returns binary data (pdf, png, and similar)?
My restpoint function returns a WP_REST_Response (or WP_Error in case of error).
But I do not see what I should return if I want to responde with binary data.
Late to the party, but I feel the accepted answer does not really answer the question, and Google found this question when I searched for the same solution, so here is how I eventually solved the same problem (i.e. avoiding to use WP_REST_Response and killing the PHP script before WP tried to send anything else other than my binary data).
function download(WP_REST_Request $request) {
$dir = $request->get_param("dir");
// The following is for security, but my implementation is out
// of scope for this answer. You should either skip this line if
// you trust your client, or implement it the way you need it.
$dir = sanitize_path($dir);
$file = $request->get_param("file");
// See above...
$file = sanitize_path($file);
$sandbox = "/some/path/with/shared/files";
// full path to the file
$path = $sandbox.$dir.$file;
$name = basename($path);
// get the file mime type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $path);
// tell the browser what it's about to receive
header("Content-Disposition: attachment; filename=$name;");
header("Content-Type: $mime_type");
header("Content-Description: File Transfer");
header("Content-Transfer-Encoding: binary");
header('Content-Length: ' . filesize($path));
header("Cache-Control: no-cache private");
// stream the file without loading it into RAM completely
$fp = fopen($path, 'rb');
fpassthru($fp);
// kill WP
exit;
}
I would look at something called DOMPDF. In short, it streams any HTML DOM straight to the browser.
We use it to generate live copies of invoices straight from the woo admin, generate brochures based on $wp_query results etc. Anything that can be rendered by a browser can be streamed via DOMPDF.

Instagram public RSS feed

I was wondering how ink361 was creating an Instagram RSS feed from a username.
Example feed: http://ink361.com/feed/user/snoopdogg
Blog post:
http://blog.ink361.com/post/23664609916/new-rss-instagram-feed-feature-on-ink361-com
Any insight would be appreciated.
Thanks.
Instagram has a publicly accessible RSS API, it's hard to find any information about it, but it works for tags (we do use it).
For tags the syntax is the following:
http://instagr.am/tags/some-tag-you-want-to-follow/feed/recent.rss
I'm not sure whether they have something similar for users' feeds, as I've said it's really hard to find information about it and it may disappear from a day to another in favor of the official API, but right now it works for tags.
Here's an official blog post about it (although it covers only tags): http://blog.instagram.com/post/8755963247/introducing-hashtags-on-instagram
#user2543857 's answer was good. Unfortunately, the structure of the Instagram data has changed. As of the date of posting this, this will work. Copy/paste into a file on your PHP server and use like: yoursite.com/instarss.php?user=name_of_instagram_user This will return valid XML/RSS feed.
EDIT!! Naturally, the output of the page/JSON has changed with instagram's new look/feel. Here is updated code (June, 2015):
<?php
if (!isset($_GET['user'])) {
exit('Not a valid RSS feed. You didn\'nt provide an Instagram user. Send one via a GET variable. Example .../instarss.php?user=snoopdogg');
}
header('Content-Type: text/xml; charset=utf-8');
$html = file_get_contents('http://instagram.com/'.$_GET['user'].'/');
$html = strstr($html, '{"static_root');
$html = strstr($html, '</script>', true);
//$html = substr($html,0,-6);
$html = substr($html, 0, -1);
$data = json_decode($html);
// print_r($data->entry_data->ProfilePage[0]->user->media->nodes);
$rss_feed = '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel>';
$rss_feed .= '<title>'.$_GET['user'].'\'s Instagram Feed</title><atom:link href="http://'.$_SERVER['HTTP_HOST'].$_SERVER["REQUEST_URI"].'" rel="self" type="application/rss+xml" /><link>http://instagram.com/'.$_GET['user'].'</link><description>'.$_GET['user'].'\'s Instagram Feed</description>';
foreach($data->entry_data->ProfilePage[0]->user->media->nodes as $node) {
$rss_feed .= '<item><title>';
if(isset($node->caption) && $node->caption != '') {
$rss_feed .= htmlspecialchars($node->caption, ENT_QUOTES, ENT_HTML5);
} else {
$rss_feed .= 'photo';
}
// pubdate format could also be: "D, d M Y H:i:s T"
$rss_feed .= '</title><link>https://instagram.com/p/'.$node->code.'/</link><pubDate>'.date("r", $node->date).'</pubDate><dc:creator><![CDATA['.$_GET['user'].']]></dc:creator><description><![CDATA[<img src="'.$node->display_src.'" />]]></description><guid>https://instagram.com/p/'.$node->code.'/</guid></item>';
} // foreach "node" (photo)
$rss_feed .= '</channel></rss>';
echo $rss_feed;
?>
Actually, don't use the above code. I'll try to maintain this Gist in the future.
EDIT December 2016: I'm tired of chasing the every-changing Instagram output only to screen scrape it and have it change a few months later. I'd say just use the API.. If you are still interested in making an RSS feed from a user's page, this Gist should give you an idea of how to do it.
You can access the feed for any Instagram user using the /users/user-id/media/recent API endpoint. This endpoint requires an access_token which you can obtain by authorizing some user with Instagram (not necessarily the one you request the feed for). The process for receiving access_token is described here.
So what ink361 may be doing is get an access_token for themselves (their user on Instagram) and use that to make /users/user-id/media/recent requests for any other users' feeds. Simple as that.
Thanks to torvin for the tip.
Here is how you can get instagram images on your site without using the API.
Create json file from url and username (set this as a cron job, X times per day)
<?
$html = file_get_contents('http://instagram.com/username/');
$html = strstr($html, '["lib');
$html = strstr($html, '</script>', true);
$html = substr($html,0,-6);
file_put_contents("username.json",$html);
?>
Display a few images from json feed
<?
$json = file_get_contents('username.json');
$data = json_decode($json);
$img1 = $data[2][0]->props->userMedia[0]->images->standard_resolution->url;
$img2 = $data[2][0]->props->userMedia[1]->images->standard_resolution->url;
$img3 = $data[2][0]->props->userMedia[2]->images->standard_resolution->url;
print '<img src="'.$img1.'" />';
print '<img src="'.$img2.'" />';
print '<img src="'.$img3.'" />';
?>
If I were ink361, I would just crawl Instagram pages, parse HTML and turn it into RSS. No API, no authorization, no problems.
Unfortunately user2543857's solution above doesn't work anymore. Here's a version that works with the current profile page source though.
Create JSON file from URL and username (set this as a cron job, X times per day)
<?php
$json = file_get_contents('http://instagram.com/username');
$json = strstr($json, '{"entry_data"');
$json = strstr($json, '</script>', true);
$json = rtrim($json,';');
file_put_contents("username.json",$json);
?>
Display a few images from JSON feed
<?php
$json = file_get_contents('username.json');
$data = json_decode($json,true);
$img1 = $data['entry_data']['UserProfile'][0]['userMedia'][0]['images']['thumbnail']['url'];
$img2 = $data['entry_data']['UserProfile'][0]['userMedia'][1]['images']['thumbnail']['url'];
$img3 = $data['entry_data']['UserProfile'][0]['userMedia'][2]['images']['thumbnail']['url'];
print '<img src="'.$img1.'" />';
print '<img src="'.$img2.'" />';
print '<img src="'.$img3.'" />';
?>
You can get access to your rss feed for instagram by using their API. Their API uses oAuth2 for authentication. I use this method on my personal blog to pull in instagram pics on the homepage. I suspect this is how the ink361 site works. Ink361 will connect to the instagram api and prompt the user with a login via instagram box which they use to allow ink361 access to their instagram account. The ink361 site will cache the token received by instagram once authentication is successfull so they then can repeatedly go back to the instagram api on a periodic basis using the same token for authentication. Bingo you have access to the users data and you can create an rss feed from it.
The answer is simple.
To access user data you just must have valid access token.
The ink361 has an app in social network http://vk.com/app3225087 which stores authenticated users access tokens in db.
Its only left to find in db a valid one and get whatever user data you want

Loading Google Maps API with wp_enqueue_script

I'm trying to load the Google Maps API using the following syntax:
add_action('admin_enqueue_scripts', 'load_google_maps');
...
function load_google_maps()
{
// The actual API key is configured in an options page
$key = get_option('google_maps_api_key');
$gmaps_url = 'http://maps.googleapis.com/maps/api/js?key=' . $key . '&sensor=false';
wp_enqueue_script('google-maps', $gmaps_url, NULL, NULL);
}
WordPress is escaping the "&" to "&#038". This actually makes the Google server reject the request. When I type it directly into browser address bar with "&sensor=false" at the end, it loads fine.
I saw a bug of this kind mentioned in the WordPress trac system: http://core.trac.wordpress.org/ticket/9243 but it was dismissed as invalid, and the admin responding to the request showed somehow that the "&#038" approach was fine. It is definitely not fine from Google's point of view.
I could of course just get the function to echo the HTML as a script tag, but I'd rather use the wp_enqueue_script system if possible.
Anyone know of a solution to this?
Cheers,
raff
I've got something similar in our code, and it's working fine (even encoded as &#038). I suspect your problem is that it's being double-encoded, as you already have &. Trying changing it to:
$gmaps_url = 'http://maps.googleapis.com/maps/api/js?key=' . $key . '&sensor=false';
For what it's worth, our (working) code is:
wp_register_script('googlemaps', 'http://maps.googleapis.com/maps/api/js?' . $locale . '&key=' . GOOGLE_MAPS_V3_API_KEY . '&sensor=false', false, '3');
wp_enqueue_script('googlemaps');
($locale in this case is set to hl=en)
Edit
Looks like the behaviour's changed in the latest version of WordPress - the above doesn't work (but I'll leave it for people on legacy versions). The only alternative I can see to echoing the script is to add a clean_url filter, something like this:
add_filter('clean_url', 'so_handle_038', 99, 3);
function so_handle_038($url, $original_url, $_context) {
if (strstr($url, "googleapis.com") !== false) {
$url = str_replace("&", "&", $url); // or $url = $original_url
}
return $url;
}
Pretty ugly, but perhaps marginally better than echoing the script, as it'll still use the WordPress dependency management.

SQLite database path not working

This is my source code in which I used SQLite database in my PHP source code. When I run the script it give me this error
Warning: sqlite_query() [function.sqlite-query]: no such table:
books in D:\wamp\www\sqllite\index.php on line 13
Error in query: SQL logic error or missing database
I think the error is at the path of database
<?php
$db = $_SERVER['DOCUMENT_ROOT']."umer.db";
$handle = sqlite_open($db) or die("Could not open database".sqlite_error_string(sqlite_last_error($handle)));
$query = "SELECT * FROM books";
$result = sqlite_query($handle, $query) or die("Error in query: ".sqlite_error_string(sqlite_last_error($handle)));
if (sqlite_num_rows($result) > 0) {
echo "<table cellpadding=10 border=1>";
while($row = sqlite_fetch_array($result)) {
echo "<tr>";
echo "<td>".$row[0]."</td>";
echo "<td>".$row[1]."</td>";
echo "<td>".$row[2]."</td>";
echo "</tr>";
}
echo "</table>";
}
sqlite_close($handle);
?>
The error is probably the path, which is determined by this line:
$db = $_SERVER['DOCUMENT_ROOT']."umer.db";
To troubleshoot this you should try an
echo $db;
And then compare that to your actual path - from there you should be able to adjust your string.
If the path is still correct, double check your file permissions and make sure it is readable by the user that is running the php process.
Good luck!
php will start looking for the database file relative to the directory where the currently running file came from so constructs like("../../databases/mydb.sq3") are usually more portable than using an absolute path or a path relative to DOCUMENT_ROOT as these can be changed at a whim.
Your program is almost certainly not finding the database file, in these circumstances sqlite will create a new empty file, and, causes great confusion by raising a "table not found" error when you try to use the database.
Look for an empty "umber.db" appearing somewhere unexpected and this will give you some indication of where you are going wrong.

Resources