I've got a page under http://www.example.com/abc/def/a/ where a user can buy products.
For a marketing activity (printed paper) the customer should type in a shorter url
http://www.example.com/order/
When this url is called, the server executes this script:
<?php
header("Location: http://www.example.de/abc/def/a/");
exit;
?>
The page under http://www.example.com/abc/def/a/ contains some
informations (rebate-code etc.) which should only be visible to users
coming from http://www.example.com/order/
$_SERVER['HTTP_REFERER'] seems to be not reliable from what I've read.
I checked with phpinfo(); if there is any info variable which contains "order" but I haven't found one.
Is it possible or do you recommend an alternative approach?
HTTP is in it's pure form a stateless-protocol, so you won't find anything in the protocol itself that will help you with your current problem.
Using $_SESSION to store data in-between requests is the easiest route to walk, and what I recommend.
As said; since the protocol used to transfer information is stateless you have no choice but to create a method for your web-application to recognize which request is done by which user.. this is a perfect problem solved by php-sessions.
php.net - PHP: Sessions - Manual
As you have discovered, the HTTP Referer, along with all of the other headers, can easily be faked. The only reliable way I see of accomplishing this is logging users as they visit the orders page and when they visit the /abc/def/a/ page, verify that the log entry exists. This kind of log could be stored in $_SESSION, however be sure that when using multiple servers you have the proper setup to ensure all servers share the same session information (you can use a redis server to store session data).
On the order page:
session_start();
$_SESSION['order_visited'] = true;
On the rebate code page:
session_start();
if(!isset($_SESSION['order_visited']) || !$_SESSION['order_visited']) {
header('Location: /order'); // Must visit order first
die();
}
Related
For learning reason, trying to give login access only for one device
so I added new row inside user table in database called ip which stored registered user ip then for example I did something like that
if($user->ip == $myip){ echo 'success'; }
the problem here that ip changing so the form can't let me access or logged in,
So my question is there anyway to logged in from only by using one device without using ip ?
Fixing the IP address might not be possible. There are a number of other ways to achieve your goal, ranging from cheap and simple to very complicated and expensive. Here are a couple.
Use a USB key (such as this one) that enters a strong password on the push of a button.
Use two way SSL (see question), safe but complicated
The easiest of these options (and my recommendation) is to set a long living cookie (using a browser developer toolbar or extension) and check in PHP for the existence of the cookie in the $_COOKIE array
Good luck!
You must use a cookie, and if the cookie is set, you must not allow a new login.
The following experts and accepted solutions agree:
how to identify remote machine uniquely in php?'
Accepted solution: uniquely identify the computer via cookie
Uniquely identify one computer
Accepted solution: set a cookie that must be present on future logins
How to uniquely identify a computer?
Accepted solution: the solution discusses Evercookie but the point seems to be you need a cookie
So, in summary, however you identify this user, if the user has a cookie, let them in. If they don't, I don't know what you're going to do, but maybe that's part of what you are mysteriously trying to learn here.
Not the best solution:
Public IP's are dynamic, meaning when a router is restarted - they obtain a new IP address. Yes, you could never restart the router but you cannot protected against physical things like electricity meaning check-ups, power outs, etc..
The best idea here is to make this Software un-accessible from outside the node you want to be able to interact with. Meaning, use Apache and MySQL (like XAMPP) and run it only on that node.
If you're looking for a long-term solutions to be able to add IP's, used a shared network. Or implement security conventions like Authentication (login).
However, if you want to consist in building it from your point of view: use the $_SERVER super variable to access the current IP and you'd need to know it before they visit (so find it out by going to something like what is my ip.
if($_SERVER['SERVER_ADDR'] == $somePreknownIp) {
// authorised
}
I would recommend using a cookie instead. First add the following code:
If ($user -> me) {
setcookie("HeyItsMe", 'SomeReallyHardToGuessValue', time()+3600*24*365); /* this would expire in 1 year */
}
This will set the cookie for just you since you're logged in. Then, you can get rid of the the code and add the following in your login screen:
if (isset($_COOKIE['HeyItsMe']) && $_COOKIE['HeyItsMe']== 'SomeReallyHardToGuessValue') {
/**show them the login screen **/
} else {
exit;
}
If your have dynamic IP then you can not do it using IP address. Therefor I suggest you to use sessions.
To do that you have to create another PHP file in your root folder(project folder). And do not share that file name with others. (I named that file as loginHandler.php)
loginHandler.php file has following content.
<?php
session_start();
// assign value to loginHandler.
$_SESSION['loginHandler'] = "99";
// redirect to login page
header('Location: login.php');
?>
On your login page (login.php), you have to start session top of the page. And if $_SESSION['loginHandler'] set, Then it will display your login form. Otherwise it will display only rest of the content.
<?php session_start(); ?>
<p>Page Content</p>
<?php if(isset($_SESSION['loginHandler'])): ?>
<div id="loginBlock">
<form method="post" action="">
<p>Your Login Form</p>
</form>
</div>
<?php endif ?>
<p>Page Content</p>
If you want to login. Then first you have to access loginHandler.php file. Then you will be redirected to login.php page. And you can access login form.
But others do not know about loginHandler.php, Therefor they try to access login form directly. Then that login form will not display for them.
Edit:
Upon re-reading the question I see that I've misunderstood what the OP was really asking for. Leaving my original reply underneath, in case someone else finds it useful.
The proper answer to this questions is: Why care about who gets to see the login form? Just use a properly strong password, and the correct techniques for preventing brute-force attempts (throttle limiting).
Any secret key, or similar, you add to this script is just another password after all. Any other information derived from your connection, browser or whatever, can be sniffed and spoofed by an attacker (or even changed from underneath you, for any reason).
Limiting to a single (or range of) IP(s) is only really useful if you have a static IP, and want to make it a bit more difficult for any potential hacker to break your password.It is not a replacement for a good password.
Original answer:
This is actually a rather common problem, and solved quite a few times. While it takes a bit of work to implement the solution, it is quite straight forward.
First off you need to create a table to keep track of the sessions for each user. This table needs only two (or three) fields:
user id
session id
(timestamp)
The timestamp can be omitted as it's not essential, but it might be nice to have for debugging/logging purposes.
Once you have that you need to re-write your login script a bit. So that it first checks if the user has any active sessions already, if they don't then simply create a new session and store its ID in the abovementioned table.
However, if the user does have a session active already, then you need to do one of two things:
If you want to disallow any further logins, simply return an error explaining that they are already logged in.
Or, delete the old sessions and then log them in on the new device.
Of these two approaches I'd prefer the latter one, as the first one can lead to a user accidentally locking himself out of the system. Until you, as the administrator, go in and manually delete the old session.
The second approach will require a bit more work, in order to delete/invalidate the old sessions, but is generally more robust. It will also give the users the least amount of surprise, as they expect to be logged in when attempting to do so. Instead of having to go chase down whatever unit they think they logged in with last.
Another thing you could do, if you decide on approach 1, is to log the timestamp and then use this in conjunction with the max session lifetime. If time_now - max_session_lifetime > timestamp then you know the session is old, and can be deleted automatically. Ensuring that the user will, eventually, be able to log in without having to rely upon finding/getting the old unit, or you manually deleting it.
I won't post any code on this, for two reasons:
You haven't posted the code in which you handle the logins, making it impossible for me to suggest any specific changes.
The changes needs to be done in quite a few places, and requires a redesign of your logic.
However, follow my logic and set up a pseudo code/flowchart diagram and it should be quite easy to implement.
one thing goes into my mind.
If you know his phone number, send him SMS with token to log in.
Of course there is technical issue about sending SMS message, that i'm as newbie are unable to solve...
You can use Mobile-Detect php library and get the device information of particular device and can add device details in db then you can put a check for that particular device.
Official documentation for library is here - Mobile-Detect
And for usage go here - Usage example
There is one for Client side also - mobile-detect.js
Hope this will help you some way (y).
You can combine 2 approaches into one. You have a list with 3 IP-addresses. For example:
$whitelist = [
'192.168.1.2',
'192.168.1.3',
'192.168.1.4',
];
Then you should check address or cookie:
$accessCode = 'Xvj482Sfjfi2Ghj23PoqT'; //some random string
$cookieExpireValue = time() + 3600 * 24 * 365 * 3; //3 years
$isIpValid = ($_SERVER['REMOTE_ADDR'] && in_array($_SERVER['REMOTE_ADDR'], $whitelist, true));
$isCookieSet = (isset($_COOKIES['access_code']) && $_COOKIES['access_code'] === $accessCode);
if ($isIpValid || $isCookieSet) {
setcookie("access_code", $accessCode, $cookieExpireValue);
echo 'success';
}
Pros:
It restricts access
If IP-address changes, user has access for 3 years
You can change $accessCode and $whitelsit to block users which already got access
It simple
Cons:
If some user gets whitelisted IP, he will get access
If a user loses the cookie (OS reinstall, browser clean, etc) with new IP-address, he will lost access (just change the $whitelist)
In case, you have different user's records for every device and you restrict access after form's submitting, you can save a new IP-address for that user if the user has a valid cookie:
if ($isIpValid || $isCookieSet) {
setcookie("access_code", $accessCode, $cookieExpireValue);
$user->ip = $_SERVER['REMOTE_ADDR'];
$user->save();
echo 'success';
}
and change the validation:
$isIpValid = ($_SERVER['REMOTE_ADDR'] && (in_array($_SERVER['REMOTE_ADDR'], $whitelist, true) || $user->ip === $_SERVER['REMOTE_ADDR']));
In this case you can get rid of the whitelist of addresses, just set ip for every whitelisted user.
Much like the question asked in Detect if cookies are enabled in PHP and Check if cookies are enabled I'd like to know if cookies are enabled.
I am, however, trying to make this as transparent as possible for the user and as such I'm not interested in having some "cookietest=1" parameter appended to my URL.
I know I can just redirect back to the page the user originally entered, unset "cookietest=1" GET parameter and just tell the original page if cookies are disabled or not through sessions, but...
I'm currently using CodeIgniter and don't want to mess up CodeIgniter sessions, hence not using PHP sessions to store the cookie enabled/disabled state.
I'm actually not sure if using PHP sessions will mess up CodeIgniter sessions, but even if it doesn't I'm still interested in knowing if there is some ingenious solution out there, that can do the cookie check without setting a GET parameter or using sessions (redirect are fine, however)?
Update
Seems I need to clarify a little bit:
I want to know if cookies are enabled client side. I've already tried the method described in the questions I linked to, i.e.:
Set cookie.
Redirect to either a check cookie PHP page or the same page with a "cookietest=1" GET parameter.
See if the cookie is still set: If yes => Hooray, cookies are working!, otherwise => Boo, cookies are disabled.
The thing I'm asking is whether or not it's possible to do this without setting the GET parameter (because this becomes visible in the URL). The answer to that question is "Yes, if you use PHP sessions".
My next question is then: Is it possible to do without setting the GET parameter AND without using PHP sessions?
Basics: You can't know if a user has or not enabled cookies until you send one cookie to the client and you recive the same from him.
So the flow:
Client Request
Server Response (+ cookie)
Client Request (+ cookie)
can't be avoided from any way
You can track if cookies are enable using some test request (ajax, image, etc)
For example you can use a simple 1px image or any logo image served from your php script and you can track if cookies are enabled or not.
So the flow is now:
Client Request
Server Response HTML (+ cookie)
Client Request remote page resources (js, img, css) (+ cookie)
Server Response with page resource requested
Something like
<?php
// domain.com/some.js
if (isset($_COOKIE['test']))
$_SESSION['cookies_enabled'] = true;
echo <<JS
<someJS code or nothing>
JS;
?>
I looked into this a LOT a while ago and it seemed every way had its flaws. One thing I took into account as well was how the cookie check would work if the user were to update the page or go back to the page via the back button.
Apparently, for the server to see whether the client accepts cookies or not the client has to send an additional HTTP request after the server has attempted to set a cookie, in which the server looks for a cookie header indicating that the client accepts cookies (no header = cookies not accepted). This additional request can be a redirect to another page (see the usual method with a $_GET parameter acting as a flag saying if an attempt to set the cookie has taken place or not) but the important thing is really that it's just another HTTP request. What I ended up doing was wrapping my entire page in a HTML FRAMESET:
<?php
setcookie('test', 1, time() + 3600);
echo '<HTML>
<HEAD>
<TITLE>
</TITLE>
</HEAD>
<frameset rows="100%" cols="100%">
<frame src="next.php">
</frameset>
</HTML>';
?>
...then in the additional HTTP request for next.php I know that there will be a cookie header included in the request if the client has accepted cookies and therefore I don't have to use a $_GET parameter as a flag indicating this. Next.php thus looks like:
<?php
if(count($_COOKIE) > 0){
//Set some variable that indicates to the rest of the script that cookies
//are enabled.
}
else {
//Set some variable indicating that cookies are disabled
}
//Output the rest of the script and HTML code to be displayed
?>
I thought about doing the same thing but sending the additional HTTP request from an IMG tag instead of a FRAMESET but I ran into trouble as to how I would indicate to the parent script via an image whether cookies were set or not and therefore I ended up doing it this way. The ONLY flaw I see in this method is that if the user right-clicks inside the frame and choose to update only the frame (not the entire page) then the frame will falsely claim that cookies are disabled but compared to the downsides of all the other ways, I thought that was acceptable.
EDIT: I should also add that I made a point out of doing this without Javascript as well.
I would use php.ini settings to find such things out.
Maybe like this:
if (ini_get("session.use_cookies") == 1) {
print "cookies enabled";
}
While you could just check if php has cookies enabled a very simple test would be to just set a cookie and then try to read it.
If you successful read it, it worked.
This would also inform you if the client disallows cookies.
I am having an issue that I hope you can help with. Let's say I work for a hypothetical company called "Blammo", and we have a hypothetical product called "Log". I am trying to set up a system where someone could log in to logfromblammo.com and order some of our products, and then when they are ready to purchase go to checkout.blammo.com to pay for their order. Eventually I want to allow for Blammo to launch a new hypothetical product with it's own website: rockfromblammo.com, and have that site also able to share a session with checkout.blammo.com so that users can have a single shopping cart across both product websites.
Naturally the hypothetical scenario described above is not how my company actually works, but it is a fair example of what I need to do. We have an existing user database, and we have ways to authenticate any of our users on any of our sites, but the goal I have is to allow users to cross seamlessly from one site to another without having to re-authenticate. This would also allow for us to seamlessly transfer data such as a shopping cart to the checkout site.
I have (briefly) looked at solutions such as OpenID, but I need to be able to integrate whatever solution we have with our existing authentication method, which is not terribly robust. Is there any good way to do this through PHP alone?
What you could do is create "cross-over" links between the sites to carry the session over.
The simplest way is to pass the session id via the query string; e.g.
http://whateverblammo.com/?sessid=XXYYZZ
Before you start thinking that anyone can trap that information, think about how your cookies are transferred; assuming you're not using SSL, there's not much difference for someone who taps the network.
That doesn't mean it's safe; for one, users could accidentally copy/paste the address bar and thus leaking out their session. To limit this exposure, you could immediately redirect to a page without the session id after receiving it.
Note that using mcrypt() on the session id won't help much, because it's not the visibility of the value that's the problem; session hijacking doesn't care about the underlying value, only its reproducibility of the url.
You have to make sure the id can be used only once; this can be done by creating a session variable that keeps track of the use count:
$_SESSION['extids'] = array();
$ext = md5(uniqid(mt_rand(), true)); // just a semi random diddy
$_SESSION['extids'][$ext] = 1;
$link = 'http://othersite/?' . http_build_query('sessid' => session_id() . '-' . $ext);
When received:
list($sid, $ext) = explode('-', $_GET['sessid']);
session_id($sid);
session_start();
if (isset($_SESSION['extids'][$ext])) {
// okay, make sure it can't be used again
unset($_SESSION['extids'][$ext]);
}
You need these links every time a boundary is crossed, because the session may have gotten regenerated since the last time.
It can be done, but not with simple cookies and it is not trivial. What you are after is a single sign on (SSO) solution, similar to Google's which share's login's across i.google.com, gmail.com, youtube.com etc.
I have used OpenID to implement this in the past.
The basic idea is to have a single authentication domain (Provider), whenever one of the sites (Consumer) wants to authenticate the user, they redirect them to the authentication domain. If they aren't signed in, they can log in using whatever details you require.
If they are already logged in (even from a different target site), they don't need to log in again.
The user is then sent back to the target site with the addition of a token in the url. This token is used by the target site's server to verify the user is authenticated with the authentication server.
This is an extremely simple explanation. Doing this is not difficult, doing it securely is much more so. The details of generating and authenticating the tokens securely is the challenging part. Which is why I suggest building upon a well designed system such as OpenID.
In cross domain Ajax, you may find that cookie and, followingly, session are lost for cross domain requests. In case you'll be making ajax calls from you site example.com to your subdomain s2.example.com you will need to use properties in headers for PHP:
header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Credentials: true');
and in JS you going to add
xhrFields: { withCredentials: true }
Otherwise cookies are not passed and you can't make use of session on your subdomain.
Full JS request to subdomain will be without lost of session:
$.ajax({
url: "https://s2.example.com/api.php?foo=1&bar=2",
xhrFields: { withCredentials: true },
success:function(e){
jsn=$.parseJSON(e);
if(jsn.status=="success") {
alert('OK!');
} else {
alert('Error!');
}
}
})
You need to set the session cookie domain like so:
session_set_cookie_params($lifetime,$path,'.site.com')
This will only work if the sites are on the same domain name including TLD (Top Level Domain).
See here for more info
Alternatively, if you are looking at trying to access sessions cross domains, as in from site1.net to site2.com, then this cannot be done.
If it's OK for your site to rely on Javascript to function, you could presumably do something like the following:
Say you have a session on blammo.com and you want to access it from rockblammo.com. On the rockblammo.com page you could load a <script> from blammo.com/get-session.js, this will (from the server side) return the session-id. Once that returns, you insert a new <script> tag in the page, pointing to rockblammo.com/set-session.js?sessionId=XXX, where XXX is the session-id you just got from blammo.com. Now, on the server side of rockblammo.com, the session cookie is updated and set to this session-id. Going forward, the two pages will now share the same session-id, and assuming they have access to the same session store on the backend, they would be in sync.
E.g. the output from blammo.com/get-session.js would be:
var sessionId = "XXX";
var s = document.createElement("script");
s.src = "/set-session.js?sessionId=" + escape(sessionId);
document.body.appendChild(s);
The output from rockblammo.com/set-session.js would be blank, but would include a http header, such as:
Set-Cookie: sessionId=XXX
If you prefer not to rely on Javascript, you could probably do the same by redirecting forth and back between the two sites and passing the sessionId in a query-string parameter (GET param).
i've a jquery script which post/get data to .php script. but i wanna prevent direct access to the php script. for example if the user look at the html source code,they will be able to access the php script directly by copying the url from the js file and i dont want that. how do i prevent users from doing that?? i want the user to use it via the html UI. i've google but found no link on this. however, i did notice that some popular websites are able to do that. how should i go about doing this??
It seems like a simple redirect is what you're looking for here.
Add something like this to the top of your php file. This will prevent the page from being accessed if the proper post has not been made. Of course you'll have to change the post and redirect to content more relevant to your project.
if (!isset($_POST['data'])) {
header('Location: your-redirect-location');
}
You may also be able to redirect based on the $_SERVER['HTTP_REFERER'] variable.
EDIT: I was going to explain this in a comment but it's too long. I should note that this is a simple solution. It will keep people from accidentally accessing your script. It's really difficult to create a 100% secure solution for your issue, and if somebody really wants to access it, they will be able to. If you don't have anything secure in the script in question, this will be fine. Otherwise, you'll have to look for an alternative.
Here is one solution:
<?php
if(isset($_POST["post_var]))
{
//to the code you want to do when the post is made
}
else
{
//do what you want to do when the user views the post page
}
?>
how do i prevent users from doing that?
You can't - all you can do is mitigate the risk people can fiddle with your script. Making sure you have the right HTTP_REFERER and/or POST data are both useful in that regard: a "malicious" user would need more than pointing her browser to the URL.
More techniques can be used here:
using session variables: you might not want users that are not logged in - if applicable - to use the URL.
using a one-time challenge (token): you can place a value in the HTML page and have the JS code send this value along with the POST request. You store this value in the session when it is generated. Checking the POSTed token against the session token guarantees the user has at least "seen" the HTML page before submitting data - this can also be useful to prevent duplicate submissions.
However, remember that anything a browser can do, people can do it as well. All these techniques can prevent the curious from doing harm, but not the malicious.
All you can do is making sure nobody can really harm you, and in this regard, your Ajax URL is no different than any other URL of your site: if it's publicly reachable, it has to be secured using whatever technique you already use elsewhere - sessions, user rights, etc.
After all, why should you care that users use this URL not using a browser ? You might want to think of it in terms of an API call that, incidentally, your page happens to use.
Your problem is similar to and has the same problems as a cross site request forgery.
To reduce your risk, you can check the request method, check the referrer, and check the origin if set. The best way is to have a secret token that was generated on the server that the client transmits back in every request. Since you're dealing with friendly users who have access to your live code, they may be able to debug the script and find the value, but it would only be for one session and would be a real hassle.
Background: I have a website, which we'll call AwesomeSite.com; it handles all of my traffic. Additionally, for the purposes of marketing I have a second domain, which we'll call PromoForAwesomeSite.com; it redirects all traffic straight to AwesomeSite. Both sites are built using PHP, MySQL, and Apache.
Problem: I want to serve up different content to users based on how they came to my site. Specifically, I want to show promos if the user was redirected from PromoForAwesomeSite.
Question: How can I detect that a user came from PromoForAwesomeSite and thus create a different session state for them?
p.s. I am well aware of the shortcomings of this approach, in that once a session cookie is deleted promo users cannot see the promo content unless they revisit the redirect site (not likely). Unfortunately, this cannot be helped.
You can utilize the $_SERVER['HTTP_REFERER'] and see if contains the PromoForAwesomeSite.com in the referrer string. For instance something like this:
session_start();
if(substr_count($_SERVER['HTTP_REFERER'] , 'PromoForAwesomeSite.com')){
$_SESSION['from_promo'] = 1;
}
As referrers can be blocked by the browsers, so you might look into the possibility of sending a GET param in the redirect string from the promo site. Not sure how you are redirecting from your promo site but if its PHP you can do something like this , if not you will get the idea what I mean :)
HEADER('Location: http://AwesomeSite.com/index.php?from=promo');
So instead of (or in additional to) checking the referrer you can also check for this string and save in the session.
In your case the referrer won't be carried on if you do an automatic redirect at the landing time. Thus, If I were you, I would handle it like this:
1. On PromoForAwesomeSite.com
header('Location: http://www.awesomesite.com/promo.php');
2. On AwesomeSite.com
a. Create a promo.php gateway page
b. On the gateway page
setcookie('Promo', '1', time()+(5 * (24 * 3600))); // five days promotion cookie - adjust it
header('Location: http://www.awesomesite.com/index.php');
c. On the index.php
if($_COOKIE['Promo']){
// show promotion
}
This way you will solve the issue with the session as well.