I've created an application using PHP and I'm going to sell it to my local market. I will personally be going to their locations to install/configure Apache & MySQL as well as installing my own code.
I would like a security system so that if anyone attempts to copy my code to an unauthorized machine, it won't run.
I know no one can prevent reverse engineering an application. even .exe (binary) files are cracked and with PHP (source code) anyone can do.
In my country those reverse engineers are really hard to find, so I would like to propose minimal security options like:
1) Create class (say, Navigation) which identifies system information like CPU ID, Computer name or any combination of hardware ID to make a UNIQUE_ID and matches with my given UNIQUE_ID (to the individual to whom I sold the application). If it's valid, it returns the navigation menu. Otherwise it will simply destroy the database and halt the execution by throwing an exception, maybe like:
class Navigation {
public function d() {
return current system UNIQUE_ID;
}
public function get() {
$a = file_get_contents('hash');
$c = $this->d();
if (crypt($c) != $a) {
//destory database
throw new Exception('');
} else {
return "<ul><li><a>home</a></li></ul>"; //navigation menu
}
}
}
2) Then during the installation process I'll change system UNIQUE_ID in "hash" file, create an object, and save it into a file (nav.obj):
(install.php)
<?php
$a=new Navigation;
$out=serialize($a);
file_put_contents('nav.obj', $out);
3) in header.php (which gets included in every file):
<?php
$menu=file_get_contents('nav.obj');
$menu=unserialize($a);
echo $menu->get();
?>
I know this method isn't full proof, but I'm pretty sure that around 60% of PHP developers won't be able to crack it!
Now I only need to get current system UNIQUE_ID.
I have created this function to get an unique ID based on hardware (Hard disk UUID). It is possible to use different resources like machine names, domains or even hard disk size to get a better approach depending on your needs.
function UniqueMachineID($salt = "") {
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$temp = sys_get_temp_dir().DIRECTORY_SEPARATOR."diskpartscript.txt";
if(!file_exists($temp) && !is_file($temp)) file_put_contents($temp, "select disk 0\ndetail disk");
$output = shell_exec("diskpart /s ".$temp);
$lines = explode("\n",$output);
$result = array_filter($lines,function($line) {
return stripos($line,"ID:")!==false;
});
if(count($result)>0) {
$result = array_shift(array_values($result));
$result = explode(":",$result);
$result = trim(end($result));
} else $result = $output;
} else {
$result = shell_exec("blkid -o value -s UUID");
if(stripos($result,"blkid")!==false) {
$result = $_SERVER['HTTP_HOST'];
}
}
return md5($salt.md5($result));
}
echo UniqueMachineID();
As per http://man7.org/linux/man-pages/man5/machine-id.5.html
$machineId = trim(shell_exec('cat /etc/machine-id 2>/dev/null'));
EDIT for Tito:
[ekerner#**** ~]$ ls -l /etc/machine-id
-r--r--r--. 1 root root 33 Jul 8 2016 /etc/machine-id
EDIT 2 for Tito: Some things to consider and scenarios:
Is the user allowed to get a new machine? Id guess yes.
Or run on multiple devices?
Sounds like the machine could be irrelevant in your case?
If its user only (no machine restrictions) then Id go for a licencing service (relies on network).
There are many services for this:
Google Play (for Android apps) is a good example: https://developer.android.com/google/play/licensing/index.html
MS and Apple have similar services.
However just search the web for the term "Software Licensing Service" or "Cloud Based Software Licensing Service".
If its user + single device, then youll need to pass up the device id to whatever service you use or make, then allow the machine id to be updated, but not allow revert to previous machine id (would mean multiple devices).
However said services will give you the client code which should take care of that if its a requirement.
Two scenarios from experience:
1: User on any device: we simply made an API in the cloud (in a website) and a login screen in the app, when the user logged in it authenticated via the API and kept a token, and whenever the device was connected to the net the app would query the API and update the login and/or token.
You could alternatively have the login screen in the purchase (like maybe they already logged into a site to purchase), generate a key and pack it with or bind it into the app.
2: User plus machine:
Same thing except when the API is queried the machine id is passed up. The machine ID can change as many times as the user updates their device, but we kept a record of machine ids and made to ban rule on: if we saw an old (previously used) machine id then a certain amount of time had to have passed. Thus allowed the user to break their machine and pull out an old one.
Also to consider if you make one, how will you stop the app from working? Ppl are pretty clever it will need to be core compiled.
However that all being said, the various licensing services are pro at this and can cater for most needs. Plus in their experience theyve already overcome the security pitfalls. Id name one that I like except its yours to search out.
Nice if you can come on back with and positive or negative outcomes from your trails.
function getMachineId() {
$fingerprint = [php_uname(), disk_total_space('.'), filectime('/'), phpversion()];
return hash('sha256', json_encode($fingerprint));
}
This will get a probably-unique id based on a hash of:
The server's OS, OS version, hostname, and architecture.
The total space (not free space) on the drive where the php script is.
The Unix timestamp creation time of the computer's root file system.
The currently installed PHP version.
Unlike the other answers it doesn't depend on shell_exec() being enabled.
Related
Apologies, since I may not know the terminologies for the salesforce API. I just started programming a connector to interact with salesforce and I am stuck.
I have a requirement, where each time a new entry is added to the Leads section, I will have to retrieve a couple of fields (Firstname and Product Code) and pass it to a different software that makes use of PHP.
<?php
require "conf/config_cleverbridge_connector.inc.php";
require "include/lc_connector.inc.php";
// Start of Main program
// Read basic parameters
if ($LC_Username === "")
{
$LC_Username = readParam("USER");
}
if ($LC_Password === "")
{
$LC_Password = readParam("PASSWORD");
}
$orderID = "";
$customerID = substr(readParam("PURCHASE_ID"), 0, 10);
$comment = readParam("EMAIL")."-".readParam("PURCHASE_ID");
// Create product array
$products = array();
$itemID = readParam("INTERNAL_PRODUCT_ID");
$quantity = 1;
if (!ONCE_PER_PURCHASED_QUANTITY)
{
$quantity = readParam("QUANTITY");
}
// Add product to the product array
$products[] = array (
"itemIdentification" => $itemID,
"quantity" => $quantity,
);
// Create the order
$order = array(
"orderIdentification" => $orderID,
"customerIdentification" => $customerID,
"comment" => $comment,
"product" => $products,
);
// Calling webservice
$ticket = doOrder($LC_Username, $LC_Password, $order);
if ($ticket)
{
Header("HTTP/1.1 200 Ok");
Header("Content-Type: text/plain");
print TICKET_URL.$result->order->ticketIdentification;
exit;
}
else
{
$error = "No result from WSConnector_doOrder";
trigger_error($error, E_USER_WARNING);
printError(500, "Internal Error.");
exit;
}
// End of Main program
?>
Now this is the code that I got and have to work with. And this is hosted on a different remote server.
I am very very new to salesforce and I am not really sure how to trigger calling this php file over a remote site.
The basic idea is:
1. New entry in Lead is created.
2. Immediately 2 fields (custID and prodID) are sent to this PHP file I have pasted above (some of the variables are different)
3. This does its processing and sends 2 fields back to salesforce.
Any help or guidance is appreciated. Even links to read up on is okay as I am completely clueless.
PS: I have another example where it makes use of JSON Messages if that may make any difference.
Thanks
I'll repost the links from my comment :)
https://salesforce.stackexchange.com/questions/23977/is-it-possible-to-get-the-record-id
Web hook in salesforce?
If your PHP endpoint is visible on the open web (not a part of some intranet or just your own localhost) then simplest thing to do would be to send an Outbound Message from Salesforce. No coding required, just some XML document you'll have to parse on the PHP side. Plus it will automatically attempt to resend the messages if the host is unreachable...
If your app can't be accessed from SF servers then I think your PHP app will have to be the "actor". Querying SF every X minutes for new Leads or maybe subscribing to Streaming API... This will mean you'd have to store credentials to SF on your PHP app and remember to either change the password periodically or set on the "integration user"'s profile the "password never expires" checkbox.
So you're getting the notification, you generate your tickets, time to send them back. Will you want to pretend the update of Lead was done by the person that created it or will you want to see "last modified by: Integration User"? Outbound message can contain session id which you can use to act as the person who initiated the action (created the lead and fired the workflow) - at least until they log out or the session timeouts.
For message back you can use SOAP or REST salesforce apis - read the docs to figure out how to send an update command (and if you want to make it clear it was done by special user associated with this PHP app - how to log in to the APIs). I think the user's profile must have "API enabled" ticked before you could reuse somebody's session so maybe it's better to have a dedicated account for integrations like that...
Another thing to keep in mind if it'd be outbound messages is to ignore the messages sent from sandboxes so if somebody makes a test environment you will not call your "production" database of tickets. You can also remember to modify the outbound message and remote site setting every time a sandbox is made so you'll have "prod talking to prod, test talking to test". I know you can include user's session id in the OM - so maybe you can also add organization's id (for production it'll stay the same, every new sandbox will have new id).
The problem with this approach is that it might not scale. If 1000 leads is inserted in one batch (for example with Data Loader) - you'll get spammed with 1000 outbound messages. Your server must be able to handle such load... but it will also mean you're using 1 API request to send every single update back. You can check the limit of API requests in Setup -> Company Information. Developer Edition will have this limit very low, sandboxes are better, production is best (it also depends how many user licenses have you bought). That's why I've asked about some batching them up.
More coding but also more reliable would be to ask SF for changes every X minutes (Streaming API? Normal query? check the "web hook" answer) and send an update of all these records in one go. SELECT Id, Name FROM Lead WHERE Ticket__c = null (note there's nothing about AND LastModifiedDate >= :lastTimeIChecked)...
We have a Magneto store that setup in 2 web servers behind a load balancer, which will take care of the load and send customers to either server.
I have a Magento module thats allows customers to upload files through the website, which then get saved in to, [web_server_ip]/media/[visitor_id]/file_name.ext
Since there are 2 servers web_server_ip has to be consistent throughout a customer session when they upload files. Means all files from a customer need to be in same directory in the same server that they got connected at first, regardless of which server they'll be sent by the load balancer within a single session. To ensure that I have following function to get the a consistent Server IP for file uploads.
protected function getServerIp() {
if (Mage::registry("dlite_server")!=""){
$serverip = Mage::registry("dlite_server");
}else{
if (substr($_SERVER['SERVER_ADDR'],0,strpos($_SERVER['SERVER_ADDR'], '.'))=='10'){
//Private IP to Public IP conversion
if ($_SERVER['SERVER_ADDR'] == '10.999.99.999'){
$serverip = '50.99.999.999';
}else if ($_SERVER['SERVER_ADDR'] == '10.888.888.888'){
$serverip = '50.888.88.888';
}else{
$serverip = $_SERVER['SERVER_ADDR'];
}
}else{
$serverip = $_SERVER['SERVER_ADDR'];
}
}
Mage::register("dlite_server", $serverip);
//I have used customer session as below to hold the IP first
//Mage::getSingleton('customer/session')->setDliteServerIp($serverip);
return $serverip;
}
The issue I'm facing is, I tried to hold the IP throughout the customer session using Magento's customer/session which failed 20% of the time. And as you can see now I'm trying to use Mage::registry to hold the IP, that fails even more, like 30% of the time.
So I was wondering is there any thing that's consistent in Magento that I can use to serve my purpose?
I am developing a desktop software where it charge user per execution the main action. For example say it will charge user 0.1$ for per PDF print.
and my software provide multithreading. .
so, if it run single thread it works fine :)
but the problem is if user run multiple thread at one (say 10/20 threads)
it (php) also continues user to allow the server/execution even balance get below zero..
though my php script check whether balance is positive ..
but after user run multiple threads balance become like -5.95$ or -25.75$ etc
and that is a big security/financial issue..
here is the code I am using:
<?php
$strSQL = "Select * from users where Email = '$strUser'";
$return = mysql_query($strSQL, $strDBConn);
$strDBData = mysql_fetch_array($return, MYSQL_ASSOC);
//checking balance
$strBalance = $strDBData['Balance'];
if($strBalance < 0)
{
// if balance 0 then exit so, my software/thread will not process further
mysql_close($strDBConn);
exit('Balance Exceed');
}
//rest of the codes that realted to service executaion
// code that substract the balnce
$dblCost = 0.25;
$strSQL = "Update users set Balance = Balance - '$dblCost' where Email = '$strUser'";
$return = mysql_query($strSQL, $strDBConn);
//rest finising codes
?>
any help/suggestion would be highly appreciated..
thanks in advance.
best regards
I think, this is a quite similar question:
What is equivalent of the C# lock statement in PHP?
First, try to switch away from the old "mysql" to somethin new, maybe some PDO like DB access ;).
Then, for getting around with multi-thread in php, it can be a good idea, to write a file for every userid (!) and lock this file, when there's a request. When file is locked in another thread, wait for x seconds for the file to be unlocked by the locker-thread. If it is not unlocked within time, something went wrong. When in locked-thread all went good, unlock the file after every operation needed.
Theoraticaly you will be good with then till there's a multi-thread soloution in PHP ;)
I'm trying to get the country from which the user is browsing the website so I can work out what currency to show on the website. I have tried using the GET scripts available from: http://api.hostip.info but they just return XX when I test it.
If anyone knows any better methods please share.
Thanks.
I use this:
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
$ip = $_SESSION['ip'];
$try1 = "http://ipinfodb.com/ip_query.php?ip=".$ip."&output=xml";
$try2 = "http://backup.ipinfodb.com/ip_query.php?ip=".$ip."&output=xml";
$XML = #simplexml_load_file($try1,NULL,TRUE);
if(!$XML) { $XML = #simplexml_load_file($try2,NULL,TRUE); }
if(!$XML) { return false; }
//Retrieve location, set time
if($XML->City=="") { $loc = "Localhost / Unknown"; }
else { $loc = $XML->City.", ".$XML->RegionName.", ".$XML->CountryName; }
$_SESSION['loc'] = $loc;
Try these:
http://ip-to-country.webhosting.info/
http://www.ip2location.com/
Both are IP address-to-country databases, which allow you to look up the country of origin of a given IP address.
However it's important to note that these databases are not 100% accurate. They're a good guide, but you will get false results for a variety of reasons.
Many people use proxying to get around country-specific blocks and filters.
Many IP ranges are assigned to companies with large geographic spread; you'll just get the country where they're based, not where the actual machine is (this always used to be a big problem for tracking AOL users, because they were all apparently living in Virginia)
Control of IP ranges are sometimes transferred between countries, so you may get false results from that (especially for smaller/less well-connected countries)
Keeping your database up-to-date will mitigate some of these issues, but won't resolve them entirely (especially the proxying issue), so you should always allow for the fact that you will get false results.
You should use the geoip library.
Maxmind provides free databases and commercial databases, with a difference in the date of last update and precision, the commercial being of better quality.
See http://www.maxmind.com/app/geolitecountry for the free database.
I think it should be sufficient for basic needs.
You can use Geolocation to get the Coordinates and then some Service to get the Country from that, but the geolocation API is browser based so you can only access it via JavaScript and then have to pass theese Informations to PHP somehow, i wrote something on the JS Part once:
http://www.lautr.com/utilizing-html5-geolocation-api-and-yahoo-placefinder-example
When it comes to getting the Location via the IP, there are a bazillion Services out there who offer databases for that, some free, some for charge, some with a lot of IP's stored and much data, some with less, for example the one you mentioned, works just fine:
http://api.hostip.info/?ip=192.0.32.10
So You can ether go with the Geolocation API which is pretty neat, but requires the users permission, works via JS and doesnt work in IE (so far) or have to look for a IPÜ Location Service that fits your needs :)
Try these:
$key="9dcde915a1a065fbaf14165f00fcc0461b8d0a6b43889614e8acdb8343e2cf15";
$ip= "198.168.1230.122";
$url = "http://api.ipinfodb.com/v3/ip-city/?key=$key&ip=$ip&format=xml";
// load xml file
$xml = simplexml_load_file($url);
// print the name of the first element
echo $xml->getName() . "";
// create a loop to print the element name and data for each node
foreach($xml->children() as $child)
{
echo $child->getName() . ": " . $child . "<br />";
}
There are many ways to do it as suggested by those earlier. But I suggest you take a look at the IP2 PHP library available at https://github.com/ip2iq/ip2-lib-php which we developed.
You can use it like below:
<?php
require_once("Ip2.php");
$ip2 = new \ip2iq\Ip2();
$country_code = $ip2->country('8.8.8.8');
//$country_code === 'US'
?>
It doesn't need any SQL or web service lookup just a local data file. It is faster than almost all other methods out there. The database is updated monthly you can download it for free.
The only thing you will need to do for now if you need the country name in your language is map it to an associative array from something like this https://gist.github.com/DHS/1340150
I hope someone can advise / direct / shed some light on :
i have a rails application that uses Authlogic for authentication.
i would like to incorporate cometchat into this application - (in reality any chat IM would do - but cometchat seemed to fit nicely because ajax-im does not like windows)
The environment is the following : The rails app is running on a windows machine - and will be available to the local network (no internet)
So to be able to use cometchat - i am running the WAMP server.
Into the nitty gritty of php(which i dont know well at all)
authlogic keeps my user session for me.
but for cometchat to work i need to pass the getUserID function the current user.
(comet chat assumes that there is a php session variable - but i dont have this with rails)
So how can i pass the rails session user to the getUserID function.
the function looks like this:
*function getUserID() {
$userid = 0;
if (!empty($_SESSION['userid'])) {
$userid = $_SESSION['userid'];
}
return $userid;
}*
the next function has to do with the friends list - but im sure this can be solved with sql inside the php page once i have the current user.
Again - all and any guidance is welcome here. Even if it means an alternate chat solution.
Many thanks in advance.
akza,
You can store the userid(numeric, non-zero, unique preferably the primary key from your users table) of currently logged-in user in a cookie say 'userid_for_cometchat'
and can access it as $_COOKIE['userid_for_cometchat'] in PHP
So your getUserID() should look like:
function getUserID() {
$userid = 0;
if (!empty($_COOKIE['userid_for_cometchat'])) {
$userid = $_COOKIE['userid_for_cometchat'];
}
return $userid;
}
You need to create this cookie on login and make sure to destroy it on logout.
This should work well.
CometChat Support team is real great at doing such stuff and they have performed a lot of custom modifications for me and I'm really satisfied by their job.