I'm developing a mobile app which has to access to an external webapp (PHP + Codeigniter) to administrate the actions queried by ajax.
So by this way, there is a problem. If anyone see the urls used, could delete rows, or modify the user's info from the database. So I thought in this system to aboid this:
After a sucessful login I would do this:
// getToken : https://stackoverflow.com/a/13733588/2154101
$this->session->set_userdata('private_token', getToken(50));
$public_token = getToken(50);
$this->session->set_userdata('secure_token', md5("$private_token:$public_token"));
$data['token'] = $public_token;
// some stuff ...
// send $data in JSON
Then the client would the public token in the next query I would do this on the server:
$public_token = $this->input->post('token');
$data['token'] = get_public_token($public_token);
// some stuff ...
// send $data in JSON
Where get_public_token is within a helper with this code:
public get_public_token($public_token) {
$last_secure_token = $this->session->userdata('secure_token');
$private_token = $this->session->userdata('private_token');
$actual_token = md5("$private_token:$public_token");
if ($actual_token === $last_secure_token) {
$public_token = getToken(50);
$this->session->set_data('private_token', getToken(50));
$this->session->set_data('secure_token', md5("$private_token:$public_token"));
return $public_token;
} else { // you are cheating me ...
$this->session->sess_destroy();
redirect('/');
}
}
So only the user of this session could modify the data of the database.
I'm just trying to do the same explained here: https://stackoverflow.com/a/17371101/2154101
The session are encrypted, and I store them in a database too.
Do you think this method will work ok? Am I missing something important?
You should create an API for your mobile application. Create a authentication mechanism.
If your database holds user specific data, then you should create account for each user. So if the user sniffs the network and tries to call the api manually, then he could only change he's own data.
There are some API libraries for php out there, you should look into that.
Actually your solution is doing more than necessary. The only token of interest is the public_token sent back and forth. So you can throw away private_token and secure_token from session data, keeping only public_token for checking. Your current check is something like (X + 5)/2 == (14 + 5)/2 (is [received_token + 5]/2 equal to [14 + 5]/2 ?) when you can simplify to X == 14.
However if someone is sniffing the network, he can get the last token sent to a client and use it to hijack into that session. He can execute anything while the original client doesn't send a request with the outdated token, killing the session.
A better solution would be creating a secure_key after login and keep it at both ends (client and server). Then server would keep sending a new public_token at each response, but the client would send a md5(secure_key + public_token) at requests. This would narrow even more the hijacking window to the exact point where the session started. Without the original key, attackers can't create a valid md5.
However we are talking about minor hacking fans here. Anyone more zealous could hack that anyway. If you are concerned about that, then throw away all that stuff and simply use a HTTPS connection. With a trusted connection your sessions and access control rules are protected.
The better way is create API using SOAP or SAML2.
OAuth can be a very good solution: http://oauth.net/. It takes care of token and has a very secured API! If you wish to support secure authentication of web application + mobile application then it can be a good/proven solution!
On the other hand, it really depends on how complex your current system is and how the system is going to be in future.
Related
I am trying to use the same token(server-side generated - PHP) to auth the user in web(JS) and iOS app(SDK). These are the steps I do to auth a user:
generate a JWT token:
I am using using the recommended lib https://github.com/firebase/firebase-token-generator-php:
$generator = new TokenGenerator($firebaseSecret);
$token = $generator->setOption('expires', strtotime('+1 day'))
->setData(array('uid' => $user->id))
->create();
use the token to auth the user via Javascript:
ref.authWithCustomToken(token, registerHandler);
registerHandler: function(error, authData)
{
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Login Success!");
}
All works correctly, the FIRST time, the user is logged in and ready to roll.(Firebase session set to 1 year)
However:
if I logout and use the same method to login again, with the same token, I get the following error: "INVALID_TOKEN: Failed to validate MAC.". If I use another fresh token all works again.
If I send the token(unused) to the iOS app I am also working with, I get the above error all the time.
Can somebody share some light into this situation ? Any help will be greatly appreciated.
I found the problem myself, in the end. I'll post this here so that future devs avoid the issue I ran into.
Be always careful about where you store your data. My tokens were stored into the DB, in a varchar type column, but they were bigger then 256 chars, so they were not saved properly. After changing the column type everything worked flawlessly.
I'm using Live Reporting Google APIs to retrieve active users and display the data inside a mobile application. On my application I'd like to make a HTTP request to a PHP script on my server which is supposed to return the result.
However I read on Google docs that it's better not to request data using APIs more often than 30 seconds.
I prefer not to use a heavy way such as a cron job that stores the value inside my database. So I'd like to know if there's a way to cache the content of my PHP scrpit na dmake it perform an API request only when the cache expires.
Is there any similar method to do that?
Another way could be implementing a very simple cache by yourself.
$googleApiRequestUrlWithParameter; //This is the full url of you request
$googleApiResponse = NULL; //This is the response by the API
//checking if the response is present in our cache
$cacheResponse = $datacache[$googleApiRequestUrlWithParameter];
if(isset($cacheResponse)) {
//check $cacheResponse[0] for find out the age of the cached data (30s or whatever you like
if(mktime() - $cacheResponse[0] < 30) {
//if the timing is good
$googleApiResponse = $cacheResponse[1];
} else {
//otherwise remove it from your "cache"
unset($datacache[$googleApiRequestUrlWithParameter]);
}
}
//if you do no have the response
if(!isset($googleApiResponse)) {
//make the call to google api and put the response in $googleApiResponse then
$datacache[] = array($googleApiRequestUrlWithParameter => array(mktime(), $googleApiResponse)
}
If you data are related to the user session, you could store $datacahe into $_SESSION
http://www.php.net/manual/it/reserved.variables.session.php
ortherwise define $datacache = array(); as a global variable.
There is a lot of way of caching things in PHP, the simple/historic way to manage cache in PHP is with APC http://www.php.net/manual/book.apc.php
Maybe I do not understard correctly your question.
I've already got a database set up with a table that is successfully populated with the final (permanent?) OAuth User Token and OAuth User Secret. The thing I don't understand is how I'm supposed to know what the current user's ID is, especially when it's been 2 weeks since their last login. My app is authorized by all of its users, so theoretically Twitter can look at the list of authorized apps for the current user and share the Twitter User ID, right? Isn't there some good way of requesting (on behalf of the current user) what his ID is? I feel like the temporary tokens should be able to facilitate this somehow... If it helps, every user in my app is just a Twitter account with some extra info. I'm just looking for the best way to utilize the tokens and secrets that are in my database...
I'm using PHP (libraries: Codebird-PHP & tmhOAuth) so if you could show an example in PHP that'd be nice, but really I just want to know how I'm supposed to use this information that I'm storing.
Thanks!
I'm assuming you store the data together with some username or user id that identifies the users of your website and links them to their proper twitter id. In order to get the basic info of your user, after authorization, you have to use the endpoint https://api.twitter.com/1.1/account/verify_credentials.json with a GET.
The documentation for the 1.1 API can be found here.
This returns an array. You find the username uder "screen_name" and the user id under "id" or "id_string".
The question is a possible duplicate of Get current user's info from Twitter API, but I've added an answer because that discussion points to the deprecated API. The code you find there, nevertheless, is still useful (it appears to use Abraham William's library, but the steps are basically the same). Replace the classes and functions with those you have in Matt Harris' library. I don't know codebird, sorry!
EDIT: I am also providing a code sample (tested and working, although I have issues with tmhOAuth, so I use it occasionally only for myself. I have noticed that, when I try to post, it sometimes returns some weird error codes and I can't figure out why):
// Authentication page, with button. You have already connected to your database
$mywebsiteuser = $_SESSION['website_user_id'];
$query= "SELECT * FROM `table_where_you_store_twitter` WHERE website_user_id ='$mywebsiteuser'";
$sql= $mysqli->query($query) or die($mysqli->error.__LINE__); // or whatever else to check is the query fails.
if ($sql->num_rows != 0){
//etc. retrieve data and set the sessions.
// already got some credentials stored?
if ( isset($_SESSION['access_token']) ) {
$tmhOAuth->config['user_token'] = $_SESSION['access_token']['oauth_token'];
$tmhOAuth->config['user_secret'] = $_SESSION['access_token']['oauth_token_secret'];
$code = $tmhOAuth->request('GET', $tmhOAuth->url('1/account/verify_credentials'));
if ($code == 200) {
$resp = json_decode($tmhOAuth->response['response']);
echo $resp->screen_name;
echo $resp->id;
//Etc. Instead of printing them you it's a good idea to store them in the db.
} else {
outputError($tmhOAuth);
}
// we're being called back by Twitter
} elseif (isset($_REQUEST['oauth_verifier'])) {
$tmhOAuth->config['user_token'] = $_SESSION['oauth']['oauth_token'];
$tmhOAuth->config['user_secret'] = $_SESSION['oauth']['oauth_token_secret'];
$code = $tmhOAuth->request('POST', $tmhOAuth->url('oauth/access_token', ''), array(
'oauth_verifier' => $_REQUEST['oauth_verifier']
));
if ($code == 200) {
//etc.
Anyhow, all in all, in order to get the info of a user you need them to authorize your app first. I check if I have something from my user with the user's session variables on my website, not through twitter. If I have nothing stored, I ask them to authorize the app. I hope this helps.
Access Token : 1274865264-QiVY50RGnmJz6AU9IPRxxiXfv4DYqo0nj6wg8hS
Access Token Secret : fZQnHSuSpwARicIdLqkqQLy1JeG9LxrbNIRKypWcGR
First part of Access Token is user id
i'm trying to use sessions to store the amount of login attempts. When the maximum of login attempts is reached i'm storing the client's ip address in a blacklist table.
Some things i've taken into account, you might need to know about:
I'm using session_regenerate_id(); after i set a session value.
I'm not using any cookies apart from the session since this is not necessary and not 2012 :p
The users ip is blacklisted until i mannually delete his row from the blacklist table.
The SESSION_MAX_ATTEMPTS is a defined constant and set to 5.
The index.php?module=login&task=blacklist page is just showing the user a message that it's blacklisted. This page does not have any functionallity.
i'm using a custom build php framework, so i had to translate some OOP called method's to simplified php code.
The following function is called before a login query is executed:
private function preventAttack()
{
$blocked = getData("SELECT count(*) as blocked FROM blacklist WHERE ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
if($blocked[0]["blocked"] == "1")
{
redirect("index.php?module=login&task=blacklist");
}
$old = (int)$this->session->get("login_attempts");
if(!empty($old))
{
if($old > SESSION_MAX_ATTEMPTS)
{
setData("INSERT INTO blacklist SET ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
redirect("index.php?module=login&task=blacklist");
}
else
{
$old++;
$this->session->set("login_attempts",$old);
}
}
else
{
$this->session->set("login_attempts", 0);
}
}
The first if statement works including both query's but i'm stuck at what's the best way to store the amount of attempts and what's the best way to ++ it? Maybe you guys can set me in the right direction.
If you have any questions about my code, please add a comment. I know it's a bit unreadable since it's from my framework, i've translated this a bit before posting it.
Store the number of failed attempts in the database, not the session. N.B.: You probably need to keep each failure along with a timestamp in its own record (and ignore/delete anything older than a threshold).
By the way, in response to deceze's comment:
Ginormous flaw in this approach: sessions depends on the client sending a cookie. Real attackers will simply not send the cookie back. ziiiing You'll have to go by IP for everything.
The solution to this is that you don't accept login attempts that don't come with a valid session cookie, set elsewhere.
Thanks a lot guys, i've learned a lot from your comments. For users with the same problem i'll explain what i've learned and how i'm using that knowledge.
What i've learned so far:
Real attackers will simply not send a cookie back. So using cookies or sessions doesn't make any sense in this case.
If you want to blacklist attackers, use can use a firewall that does this automatically for you. It makes no sense to do this from your script, sessions are too easily circumvented and if you check IP addresses you could block out entire offices or schools on the same external IP.
Whatever you do: store the login attempts data only on the server in a database/flatfile/etc. The user or attacker will not be able to edit this data so easily.
And if you do store data on your web server for performance, store it in a file, not a DB.
If i forgot something please comment and i will edit this post.
I checked with my hosting provider and they are already blocking out a lot of these attackers using solutions like the firewall mentioned above. So i will stop trying to also do this in my scripts.
My script is fixed now and only blacklisting users and noob hackers guessing passwords:
private function preventAttack()
{
$blocked = getData("SELECT count(*) as blocked FROM blacklist WHERE ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
if($blocked[0]["blocked"] == "1")
{
redirect("index.php?module=login&task=blacklist");
}
$old = (int)$this->session->get("login_attempts");
if($old > 0)
{
if(($old + 1) >= SESSION_MAX_ATTEMPTS)
{
setData("INSERT INTO blacklist SET ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
$this->session->set("login_attempts", 0);
redirect("index.php?module=login&task=blacklist");
}
else
{
$old++;
$this->session->set("login_attempts",$old);
}
}
else
{
$this->session->set("login_attempts", 1);
}
}
Since i'm not quite sure how many users this platform is going to get and i don't want a possible high mysql server load i've decided not to store the login attempts in the database. Maybe in the future, who knows.
I would like to access with R to the content of a php website
http://centralgreen.com.sg/login.php?login=9-1501&password=mypassword
I have passed an example of login + password in the url, but I don't know how to press the login button through the url.
I would like to use the R package RCurl if possible.
The form submits by post - you are using a get request at the moment by the looks of things, you need to use post.
My guess is that rcurl is based on curl - and I know curl can do this, so should be possible.
recently I've been having the same problem. In my case I solved it like this, using RCurl package (with a POST request).
In this code two requests are done one after the other. The fist one, is in order to gain a session cookie (start session in the server). The application I was calling expected the session to be started by the time it checked the login credentials (this won't happen if you send the form upfront). Otherwise some warning about not having cookie support was raised. This might be the case of the asker (though it was time ago)... or someone else's.
login <- function (xxxx_user, xxxx_pass) {
url_login <- 'http://centralgreen.com.sg/login.php'
curlhand <- getCurlHandle()
curlSetOpt(
.opts = list(cainfo = system.file("CurlSSL", "cacert.pem", package = "RCurl")),
cookiefile = "cookies.txt",
useragent = 'YOUR R-PACKAGE NAME',
followlocation = TRUE,
# might need this in case the server checks for the referer..
httpheader = "Referer: http://centralgreen.com.sg",
curl = curlhand)
# (1) first call to initializate session. you get the session cookie
getURL(url_login, curl = curlhand)
params<- list( login = xxxx_user, password = xxxx_pass )
# might need to add some other hidden form param in case there are..
# (2) second call, sends the form, along with a session cookie
html = postForm(url_login,
.params = params,
curl = curlhand,
style="POST")
# ... perform some grep logic with 'html' to find out weather you are connected
}
# you call the function...
login("yourusername", "yourpass")
The 'perform some grep logic' note takes care of the fact that since you are targeting a system not designed for this kind of programatical log in, it's not going to give you any nice hint on the result of the attempt ... so you might need to parse the raw html string you receive against some key sentences (eg: 'wrong username or password' ...)
hope it helps