Say I have a website like foo.com and have the user be able to enter the username, and gives you back your view count. So if the username is bar, the url would look something like foo.com/index.php?username="bar".
Now if I had another user john, what could prevent bar from entering foo.com/index.php?username="bar" and getting their view count?
what could prevent bar from entering foo.com/index.php?username="bar" and getting their view count?
The short answer is nothing. Cause there are no safe (and consistent along different browsers and systems) ways to prevent user from editing the url. But...
You can check if the current logged in user has the permissions to view that data.
If you're using a framework, probably there's already an implementation for RBAC (Role Based Access Control).
So you should check the documentation for that.
If you're not and you're on a project with plain PHP, you have to implement that. And it can get quite complicated.
This is one of the downsides of PHP, user can always alter the request (and the forms contents) and you should always check that the data is correct. (that's one of the reasons most PHP programmers use frameworks).
Related
I have a dynamic page where it should take data from a db. So the approach I thought of was to create the dynamic page with this php code at the top
<?php $pid = $_GET["pid"]; ?>
Then later in the file it connects to the database and shows the correct content according to the page ID ($pid). So on the home page, I want to add the links to display the correct pages. For example, the data for the "Advertise" page is saved in the database in the row where the pid is 100. So I added the link to the "Advertise" page on the homepage like this:
Advertise</li>
So my question is, anyone can see the value that's send on the link and play around by changing the pid. Is there an easy way to mask this value, or a safer method to send the value to the page.php?
The general concept you're looking for is Access Control. You have a resource (in this case, a page and its content), and you want to control who can access it (users, groups, etc), and probably how they can access it as well (for example, read-only, read-and-write, write-but-only-on-the-first-Monday-of-the-month, etc).
Defining the problem
The first thing you need to decide is which resources you need access control for, and which you don't. It sounds to me like some of these pages are supposed to be "public access" (thus they are listed on some kind of index page), while others are supposed to be restricted in some way.
Secondly, you need to come up with an access policy - this can be informally described for a small project, but larger projects usually have some structured system for defining this policy. For each resource, your policy should answer questions like:
Do you have some kind of user account system, and you only want account holders (or certain types of account holders) to access it? Or, are you going to send links to email addresses, and want to limit access to just those people who have the link?
What kind of access should each user have? Read-only? Should they be able to change the content as well (if your system supports that)?
Are there any other types of restrictions on a users' access? Group membership? Do they need to pay before they get access? Are they only allowed access at specific times?
Implementing your policy
Once you've answered these questions, you can start to think about implementation. As it stands, I think you are mixing up access control with identification. Your pid identifies a page (page 100, for example), but it doesn't do anything to limit access. If your pages are identified with a predictable numbering scheme, anyone can easily modify the number in the request (this is true for both GET requests, such as when you type a URL into an address bar, and POST requests, such as when you submit a form).
To securely control access there needs to be a key, usually a string that is very difficult to guess, which is required before access is granted. In very simple systems, it is perfectly fine for this key to be directly inserted in the URL, provided you can still keep the key secret from unauthorized users. This is exactly how Google Drive's "get a link to share" feature works. More complex systems will use either a server-side session or an API key to control access - but in the end, it's still a secret, difficult-to-guess string that the client (user or user's browser) sends to the server along with their request for the resource.
You can think of identification like your street address, which uniquely identifies your house but is not, and is not meant to be, secret. Access control is the key to your house. Only you and the people you've given a key to can actually get inside your house. If your lock is high quality, it will be difficult to pick the lock.
Bringing it together
Writing code is easy, designing software is hard. Before you can determine the solution best for you, you need to think ahead about the ramifications of what you decide. For example, do you anticipate needing to "change the keys" to these pages in the future? If so, you'll have to give your authorized users (the ones that are still supposed to have access) the new key when that happens. A user-account system decouples page access control from page identification, so you can remove one user's access without affecting everyone else.
On the other hand, you also need to think about the nature of your audience. Maybe your users don't want to have to make accounts? This is something that is going to be very specific to your audience.
I get the sense that you're still fairly new to web development, and that you're learning on your own. The hardest part of learning on one's own is "learning what to learn" - Stack Overflow is too specific, and textbooks are too general. So, I'm going to leave you with a short glossary of concepts that seem most relevant to your current problem:
Access control. This is the name of the general problem that you're trying to solve with this question.
Secrecy vs obscurity. When it comes to security, secrecy == good, obscurity == bad.
Web content management system. You've probably heard of Wordpress, but there are tons of others. I'm not sure what your system is supposed to do, but a content management system might solve these problems for you.
Reinventing the wheel. Good in the classroom, bad in the real world.
How does HTTP work. Short but to the point. A lot of questions I see on SO stem from a fundamental misunderstanding of how websites actually work. A website isn't so much a single piece of software, as a conversation between two players - the client (e.g. the user and their browser), and the server. The client can only say something to the server via a request, and the server can only say something to the client via a response. Usually, this conversation consists of the client asking for some resource (an HTML web page, a Javascript file, etc), to which the server responds. The server can either say "here you go, I got it for you", or respond with some kind of error ("I can't find it", "you're not allowed to see that", "I'm too busy right now", "I'm not working properly right now", etc).
PHP The Right Way. Something I wish I had found when I first started learning web development and PHP, not seven years later ;-)
It is always safer to $_POST when you can, but if you have to use something in the query string, it is safer to use a hash or GUID rather than something that is so obviously an auto-incremental value. It makes it harder to guess what the IDs would be. There are other ways values can be past between pages ($_SESSIONs, cookies etc), but it is really about what you want to achieve.
Sending it to php is not an issue, should be fine.
What php does with it afterwards... that's how you secure.
First thing I'd do is make sure it's an integer.
$pid=(is_int($_GET['pid']))? $_GET['pid'] : 1; //1 is the default pid, change this to whatever you want.
Now that you know you're dealing with an integer, use $pid after that and you should be good to go.
A number of my pages are produced from results pulled from MySQL using $_Get. It means the urls end like this /park.php?park_id=1. Is this a security issue and would it be better to hide the query string from the URL? If so how do I go about doing it?
Also I have read somewhere that Google doesn't index URLs with a ?, this would be a problem as these are the main pages of my site. Any truth in this?
Thanks
It's only a security concern if this is sensitive information. For example, you send a user to this URL:
/park.php?park_id=1
Now the user knows that the park currently being viewed has a system identifier of "1" in the database. What happens if the user then manually requests this?:
/park.php?park_id=2
Have they compromised your security? If they're not allowed to view park ID 2 then this request should fail appropriately. But is it a problem is they happen to know that there's an ID of 1 or 2?
In either case, all the user is doing is making a request. The server-side code is responsible for appropriately handling that request. If the user is not permitted to view that data, deny the request. Don't try to stop the user from making the request, because they can always find a way. (They can just manually type it in. Even without ever having visited your site in the first place.) The security takes place in responding to the request, not in making it.
There is some data they're not allowed to know. But an ID probably isn't that data. (Or at least shouldn't be, because numeric IDs are very easy to guess.)
No, there is absolutely no truth to it.
ANY data that comes from a client is subject to spoofing. No matter if it's in a query string, or a POST form or URL. It's as simple as that...
As far as "Google doesn't index URLs with a ?", who-ever told you that has no clue what they are talking about. There are "SEO" best practices, but they have nothing to do with "google doesn't index". It's MUCH more fine grained than that. And yes, Google will index you just fine.
#David does show one potential issue with using an identifier in a URL. In fact, this has a very specific name: A4: Insecure Direct Object Reference.
Note that it's not that using the ID is bad. It's that you need to authorize the user for the URL. So doing permissions soley by the links you show the user is BAD. But if you also authorize them when hitting the URL, you should be fine.
So no, in short, you're fine. You can go with "pretty urls", but don't feel that you have to because of anything you posted here...
I'm trying to come up with a good way to avoid directly using ID's in URL's to look up table entries. The main reason is that for privacy reasons, I don't want my users to be able to simply change, say, /?unique_id=10 to /?unique_id=11 and see someone else's information.
I've noticed many sites use randomly generated strings, but what's the best structural implementation of something like this?
Thanks!
Oh, and I doubt it matters, but I'm using PHP.
EDIT:
The information contained on the pages is public information. That is, anyone with the link should be able to access the page without trouble. What I want to prevent is people simply iterating through IDs and seeing everything in the database. I prefer that only people that have been given a link access the page. That said, it's not a huge problem if a random person stumbles across it.
Also, I don't want people looking at the ID to figure out how many total entries there are.
You probably need some kind of user check to make sure people arent seeing other peoples records anyway, but using a GUID for this is a good start.
You could use a hash of something like record1, record2 etc, but a determinted hacker could easily do this.
Another option is to use record aliases so each record has a string that represents it which you then use as the key. You often see this in wordpress or other CMS systems.
So if your id refers to a post maybe take the title and replace spaces with -
eg. www.example.com/article.php?id=Summer-is-the-best-time-of-year
You shouldn't had to deal with that at url level. You just take care of that at the session so if user 123 tries to access yoursite.com/unique_id=456 the session checking will prevent him from doing it. I mean you're talking about private pages isn't it ?
Even if you encode it (the user id) it will be accessible as a hash or something which would be nothing more than obfuscation which is not as good as preventing access on your own (with a session)
I have used MySQL's UUID() function for this, but you should definitely use permission checking to ensure that users aren't able to view data for other users.
This answer shows simply how to create a unique identifer.
you can encrypt them use md5(id) and search for the record the has the same md5(id)
ie
select * from table where md5(id) = '$encrypted'
Why not use AJAX calls for any queries to the DB rather than including them in the URL $_GET.
I have an PHP Application. If I have logged in that application I am trying to pass the parameter as querystring through an iframe to the asp.net page.
Is there any other way to implement other than using an iframe?
Instead of having the PHP application submit data to your ASP application, it would be better if they could natively and securely share some of the data.
How?
Well, your goal is having one script tell the other that the user has been logged in, right? In PHP, this is usually done by putting something in the $_SESSION. Your ASP application can't read $_SESSION, though. You'll need to use something else.
When the user logs in, create a unique value. Maybe the result of hash_hmac over some interesting data? Whatever it is, it should be unique every time it's created and unguessable. Don't throw in things like the user's IP address or the current time.
Save the unique value to a database table that both applications can read. Also store other information that will help identify the user, such as her identifier (user_id or whatever you have on hand).
So, the PHP code that logs the user in has created this unique value and stuck it in a shared database table. Now, the PHP application should forward the user to your ASP application. Include the unique value in the request.
When the ASP application receives the request, it will look for the unique value. If it's found, it can look in the shared table. If the value is found in the table, it can then take whatever measures it needs to in order to mark the user as logged in.
Once the ASP application has logged the user in, then it should delete the unique value from the shared table. The user can be forwarded to wherever she was going in the first place.
By making the key usable only one time, and only after a successful login in the PHP application, you'll reduce the possibilities of abuse by malicious or curious users. All of the important information will be hidden in the shared database table.
Be warned that this is an overly simplistic implementation of "single sign on" and is full of caveats and edge cases. While it might work for you, it might not be the best solution. Given your question history, it looks like you've been struggling with similar issues for quite some time. You might want to give some thought into using a slightly more "industry standard" SSO mechanism. SAML is the 800 pound gorilla of SSO standards. I normally wouldn't wish it on my worst enemy, but maybe it's the thing you're really looking for here.
Also, don't use iframes, they're cookie eating disasters in some browsers.
I have an HTML menu with links like <a href="input&db=<some database>" There are multiple menu items and multiple databases, so I am using GET as my form method and using it in my menu item links.
However, that shows up in the browser's address bar (as /inout&db=mydatabase) & might lead the user to start guessing as to database names.
How can I have a list of links to the same page, in which only the database varies, using $_POST ?
EDIT: oops, my bad Shoulda said server-side only, so no JS
POST values will be just as obvious to anyone who would be savvy enough to do anything with this information. Unless you're building something like phpMyAdmin, you should never pass such internal information to the client side to begin with. And if you are, where's the harm? You do have proper authentication in place, don't you?
I think the only way to send request via post using links is to use JavaScript. But sending it via post is not secure at all; anyone can install FireBug to see the request.
Instead, I'll suggest a change to your design. Databases are usually at the bottom tier in an application hierarchy, and coupling page details with database sounds unnecessary. Maybe you should try to encapsulate pages so that they don't need to know which database they are reading from?
Granted, I have no idea of the scope of your application (you may be doing something like phpmyadmin). Then it may be unavoidable, and I will just suggest the usual combination of verification and sanctification all users' input and their rights.
Or you can just encrypt your database names. Still I would prefer a change to design.
Use the onclick event of the anchors to submit a hidden POST form, or to perform AJAX POST actions.
No. There are a few narrow and dangerous solutions you can apply:
Use an iframe : everything will work as before, but the actual address will not appear in the browser address bar.
Use AJAX to fetch data.
Replace the link with a form-submitting button or javascript: link.
These all solve the "database name appears in address bar" issue, however:
Anyone with even basic technical skills and appropriate tools (chrome, firebug) can determine the database name anyway by looking at the requests being sent out.
Not using GET can mess up the browser's back and refresh buttons, and prevent deep linking.
My suggestion would be to keep using GET as you currently are, but add a secret token to the URL (such as HMAC(db_name,secret_key)) that cannot be guessed by the user but can be easily checked for validity by the server. This way, unless you give the user a link to the database (with both database name and secret token), all the guessing in the world will not let them access it.
Neither GET or POST will hide your database name.
Even you using POST, view source will reveal the HTML.
In the first place, you should not expose your database name.
Or replace it with some fuzzy mapping
such as
input&db=A
input&db=B
Internally, do string matching and convertA to actual database name