I'd like to keep track of how many people follow a link sent through an email.
At the moment, I'm thinking of having a separate page which is called through the link with a get variable to indicate this was done from an email.
Obviously, this can be tampered with manually through the address bar of a navigator. What approaches could I use to limit this?
1)Use a completely different URL to redirect to the real one:
http://www.mysite.com/emailOffer -> http://www.mysite.com/specialpage
emailOffer would do the logging, and then send a Location HTTP header pointing to the real page (specialpage)
http://www.mysite.com/emailOffer would look like this:
/* SOME LOGGING CODE GOES HERE - PROBABLY MYSQL STUFFS */
header("Location: http://www.mysite.com/specialpage");
2)Add a GET parameter like: http://www.mysite.com/specialpage?email
then in php, you can do: if(array_key_exists('email',$_GET)) addToCounter();
3)You could log the HTTP Referer header for everyone who hits the page and run a query for referers containing "mail" (e.g., mail.google.com, hotmail.com)
To help prevent tampering, you could make the parameter seem worth while:
http://www.mysite.com/specialpage?secretOffer
Good luck! :)
PS - sorry for the terrible formatting of my answer...
In most cases people probably just use the GET method, in all cases though, it can be tampered with, pretty much every element can be changed if a person really wants to, but as far as telling where someone is going from you're pretty much stuck with GET or checking the referrer, which can be highly suspect, and you'd have to check all sorts of things.
If you aren't concerned with the actual link they're following in the email, you could create a secondary page, that hits your counter, outputs nothing, and just redirects them like
<?php
... do counter stuff ....
header('Location: http://actual.address.com/');
?>
Their navigation bar will change to the new address, and they'll barely even notice the original.
Related
after discovering the unreliability of HTTP_REFERER, I was wondering what was the best alternative to indicate an operation the correct url of origin which then perform an internal redirect.
After reading various topics I seemed to understand that only possible solutions are:
1) specify the referer url directly as a parameter of the operation.
2) create a custom referer storage system using session.
In my opinion, the first solution is logically more correct and free of contraindications.
Using the second solution and storing the referrer in session on every page request is possible that using the site in the various tabs referrer stored does not correspond to the page where we're actually sending the operation; Despite this bug (in my opinion quite ugly) this second solution seemed the most recommended by experts. Why? Did I miss something?
Thank you all for your attention and sorry for my low level of English.
storing the referrer in session on every page request is possible that using the site in the various tabs referrer stored does not correspond to the page where we're actually sending the operation; Despite this bug (in my opinion quite ugly) this second solution seemed the most recommended by experts
You could notify server to update session with current page before leaving - this way next loaded page will know what was the previous one without passing anything in URL.
That said though this solution still have some faults - it will break on any connection failure or if someone loads multiple pages in multiple tabs and requests go out of synch (it's relying on an assumption that between javascript sending request on leaving the page and server receiving request to load a next page nothing else occurs).
Best way would be to use both - this and HTTP_REFERER as a fall-back option.
Other than that mentioned passing of current pages in URL will do, but it's understandable why you try to avoid it.
I wanted to pass a hidden variable at first with $_POST but i have come to realize that users can change $_POST almost as easily as $_GET. Is it possible to somehow restrict this ability or is there another way to do this? Also, it doesnt seem you can use $_POST in this simple example below?:
index.php
<view recipe
test.php
$variable = $_GET['variable'];
$query = $database->query("SELECT name, description, date_added from recipe where recipe_id = $variable");
(EDIT: i do check that the input is indeed an integer although i skipped this above to minimize the code of the example. I should've made this clear earlier).
I guess the only borderline "malicious" things a user could do here is loop through the recipe_id:s to find out how many recipes were in the database or even the first recipe added just by changing the $variable. Not that i care in this particular case, but im sure i will when it comes to other examples. I realize that i want to make the information available, i just want it to go through the "proper channels" i guess. Is this just the way things are or am i missing something vital?
People always write things like "validate your input". And i agree with that. But in this case its just one integer the user "inputs". What can be done besides that validation? Again, im slowly progressing/learning this so please be patient if i seem to make simple mistakes.
(Using PHP, PDO, Mysql)
Thank you!
It's all about HTTP requests. The browser communicates with the server through HTTP requests. These are entirely transparent to the user however you look at it. Open the Web Inspector in your browser (or Firebug, or Fiddler or whatever else) and inspect the raw HTTP requests, live. Anyone can send these requests to your server anytime, containing any data at all.
Understand this concept, it is important. There's no such thing as "secret" information in the communication between your server and the client (from the POV of the client). You do not control the input to your server. Neither can you "hide" any data that is going from the client to your server.
An HTTP request represents a, well, request for data or for some action. The server should, well, serve the request to the best of its abilities. If a request is invalid, the server must reject it. The server should judge the validity of each request independently of every other request, it should be stateless. The server cannot presume the validity of any request without validating it. It should only ever output information which is not security sensitive and treat all incoming data as suspicious. That's the fact of life.
The "variable" isn't restricted to be an integer at all in your code. An evil user could probably change the value of "variable" to "';truncate recipe;--". Execute this and whoops ... all recipes are gone.
always ensure that you use proper validation. search the interwebs for "sql injection" and have a look at functions like mysql_real_escape_string (and it's documentation).
If you don't want users to see an incremental recipe ID that they can easily modify, then generate a unique random string to use to identify recipes. This will stop users from being able to play with the recipe ID in the GET or POST data.
They can still modify it but they will need to obtain the random string in order to pull out a recipe.
The content of request fields, whether $_POST or $_GET, is not typed. It is just plain strings, which means that it's pretty much "open game" on the client side. It's the very reason we keep repeating that client input cannot be trusted, and must be validated on the server side.
Regarding your second question: recall that $_GET will contain the result of a form using the get method, while $_POST will contain data from post method forms. $_GET purpose is to contain url parameters, if they exist (to be precise, a get method form will pass all its parameters via the url).
On a side note, I should also tell you that you shouldn't use one verb for another, each one as a specific purpose. The get method is about getting data, not updating it, while post, put, delete are about updating data. This means that your example is following these rules, and should not try to use a post form instead (although this is technically feasible, you would just need to replace your link tags with forms).
see the HTTP specs on this matter.
you may create a session based permission system that:
when user visited your site, and his/her browser rendered the link, in that php, you set a session variable for that link,
$_SESSION["permitted_recipe_id"] = $row['recipe_id'];
Now you know exacly what recipe id that user can click,
with this check:
$variable = $_GET['variable'];
if($variable != #$_SESSION["permitted_recipe_id"]){
exit("error you do not access to this result");
}
$query = $database->query("SELECT name, description, date_added from recipe where recipe_id = $variable");
This will ensure that user visited the page before it send a request. This solution will prevent consecutive POST requests to fetch all website data,
if you show multiple links that user can click, you must implement $_SESSION["permitted_recipe_id"] as an array containing all ids' that send to user as links.
but beware, multi tab experiences may create bugs for this solution. This is the idea, you have to carefully work it out for a relase enviroment solution.
I am thinking about form security a lot lately. I have been told time and time again to check if form input is a number if you are expecting a number or escape it in case (unless you use proper mysqli formatting) to avoid injection.
After the safety checks are done, should I do additional logic checks? For example, if the user is sending a friend request to them-self for example even if my user interface will not show the form if the user is looking at their own page.
Anything you do in HTML or JavaScript is not sufficient to prevent someone from posting data directly to your HTTP server. So treat anything that is sent by the browser (even cookies!) as "user input" and guard accordingly.
Because even though your form may not allow me to send a friend request to myself, if I'm running Fiddler I can just set a breakpoint, change a POST variable, then resume the request and your server has no idea.
In fact, that's a great eye opening exercise. If you go download Fiddler you can watch everything that the browser sends or receives with your web site. Anything being sent by the browser should not be implicitly trusted.
Yes you should. Haven't we noticed a pattern in some site's URL's and then copied the url but changed some part to get around some restriction in the site bypassing login/access control? Do you want your site to be susceptible to that too?
Of course.
You can't go far enough validating input. Treat it as garbage and plan accordingly. If you want everything to work smoothly make sure that everything checks out.
Of course. The whole point of validation is to properly handle input outside what you're expecting. If users gave you what you expected, you wouldn't have to validate. You need to assume your user could throw absolutely anything at you. As noted, they can bypass the browser entirely using manual HTTP requests. Always code defensively.
A good description I once heard from some famous CS guy (not sure whom, a C writer?) went like "Some time in the early 90's evil on the internet started outgrowing the good on the internet. Any scheme founded upon the idea of enumerating badness is destined to fail (because there's so much of it)".
Don't describe the bad things IE functions like - isSQLcommand(), isJavaScript(), compilesToBinaryandRuns(). This is called Blacklisting and you will exhaust yourself doing it and there is always someone smarter and more evil than you out there.
Instead focus on whitelisting. Enumerate the good, and list only the things you expect to occur. Have a select HTML element with male/female options?
if (selectInput == 'male' || selectInput == 'female'){
//proceed
}
else {
//dump the user data and start over
}
EDIT
It was Marcus Ranum, a security expert:
http://www.ranum.com/security/computer_security/editorials/dumb/
I want to store the page location the user came from (on my site). I want to do that for this example: say someone sent a comment without being logged in. "process_comment.php" will process it and send a header(location:$_GET['prev_page']); Of course I'm gonna filter $_GET before sending it.
Should I use a session instead?
Thanks!
It is actually exactly the same. Both methods imply that the information is passed in the HTTP query, which can easily be forged. So you can't really trust one method more than the other.
That being said, as long as you don't rely on that information for something really important, you can admit that the referer can be trusted, because it's a little bit more complex to forge than a querystring parameter. At least for the average user.
The best solution, if you need to trust that information for something important, would be to store it on the server, as a session variable for instance. Each page would store its URL, after checking what the previous value was.
If you use $_SESSION, there will be trouble if the user has multiple windows/tabs open and does different things at once. There is nothing more annoying than being able to only have window of a site.
You could store the value in a SESSION variable and identify it by a short key. That key goes into the GET string. That way, you can keep your URLs clean, and you don't risk hitting the 1024 byte limit many servers have for GET parameters.
Well, the HTTP_REFERER can be stripped out by some clients.. I seem to remember some Norton Internet security products did that, probably others do too. So it is going to be more reliable for you to set the previous page in a session and use that for redirecting.
If you can use it, session is a safer option. Sending user back from GET or even headers will allow crafty people to possibly abuse any flaws in your code to possibly do nasty things.
The header itself may also be removed by some firewall software.
I don't think there is a problem with using GET in this case. You can't always depend on being able to retrieve the referrer from the browser.
Using PHP.. I have a small app that I built that currently uses a querystring to navigate and grab data from a database. The key in the database is also in the string and that is not acceptable anymore. I need to change it. I would like to hide the db key and use a session in place of it but I'm not sure how to do that. In fact, there are also other variables in the query string that I would like to use sessions for if at all possible.
page.php?var1&var2&id=1
This is what my string looks like. I am looping through the results in the database and have given each row the id so that when the user clicks the row they want, but I'm not sure how I could do this with a session.
Does anyone have any ideas?
Thanks
EDIT:
I'm developing an email type system where senders and recipients are getting and sending mail. Each piece of mail that is stored on the server will have its own unique key. Currently, I am using that number to retreive the message but the problem is that I don't want people to change the number and read other people's mail. I can probably use a GUID for this or even some sort of hash but I really hate long query strings. I was just thinking it would be so much cleaner if there was a way to "hide" the id all together.
UPDATED (Again ... Yeah, I know.)
Allowing access to a particular set of data through a $_GET parameter is much more accessible to any user that happens to be using the application.
UPDATED
For storing a private record key, you are probably going to want to use post data, and if you really want it to look like a link, you can always use CSS for that part.
Honestly, the best way to stop people from reading other people's mail is by having a relationship table that says only X person is able to access Y email (by id). That or have a field that says who is the 'owner' of the email.
The fact is that users can still get access to POST parameters, and can easily forge their own POST parameters. This means that anyone could realistically access anyone else's email if they knew the naming scheme.
In an ideal system, there would be a Sender, and a Recipient (The Recipient could be comma separated values). Only the people that are on one of those columns should be allowed to access the email.
How To Use Sessions (From Earlier)
First start off with calling session_start(), and then after that check for variables from previous scripts. If they aren't present, generate them. If they are, grab them and use them.
session_start();
if(!isset($_SESSION['db_key']))
{
$_SESSION['db_key'] = // generate your database key
}
else
{
$db_key = $_SESSION['db_key'];
}
Sessions are stored in the $_SESSION array. Whenever you want to use $_SESSION, you need to call session_start() FIRST and then you can assign or grab anything you like from it.
When you want to destroy the data, call session_destroy();
Also check out php.net's section on Sessions
Your question isn't too clear to me, but I understand it like this:
You need some variables to decide what is being displayed on the page. These variables are being passed in the URL. So far so good, perfectly normal. Now you want to hide these variables and save them in the session?
Consider this: Right now, every page has a unique URL.
http://mysite.com/page?var1=x&var2=y
displays a unique page. Whenever you visit the above URL, you'll get the same page.
What you're asking for, if I understand correctly, is to use one URL like
http://mysite.com/page
without variables, yet still get different pages?
That's certainly possible, but that means you'll need to keep track of what the user is doing on the server. I.e. "user clicked on 'Next Page', the last time I saw him he was on page X, so he should now be on page Y, so when he's requesting the site the next time, I'll show him page Y."
That's a lot of work to do, and things can get awkward quickly if the user starts to use the back button. I don't think this is a good idea.
If you need to take sensitive information out of the URL, obfuscate them somehow (hashes) or use alternative values that don't have any meaning by themselves.
It completely depends on your application of course, if the user is accumulating data over several pages, Sessions are the way to go obviously. Can you be a bit more descriptive on what your app is doing?
Edit:
but the problem is that I don't want people to change the number and read other people's mail
If your primary concern is security, that's the wrong way to do it anyway. Security through obscurity is not gonna work. You need to explicitly check if a user is allowed to see a certain piece of info before displaying it to him, not just relying on him not guessing the right id.
There are some examples on how to use $_SESSION on php.
Registering a variable with $_SESSION
The issue with using sessions for using it in place of S$_GET or $_POST is that you need some way to read the user's input so that you can store it in the session, and you need a way to trigger a page refresh. Traditional means is via hyperlinks, which defaults to GET (unless you use Javascript) or forms, which defaults to POST.
Maybe ajax will help you here. Once the user has enter info into a form or a checkbox, use JS to send a request to insert the info to the PHP and send info back, whether it is to refresh the page or to fill a with content.
Hope this helps