5

Located here, RFC 7231 is all about the HTTP protocol (status codes)

I'm developing an in-house web application for my university's department and I'm at a (probably unimportant) impasse.

I'm reducing users' access to resources based on role (student, faculty, staff) and have it working just fine. However, when a user attempts to access a resource and is denied access, the HTTP specification says that I should use a 403: Forbidden, marking that the user does not have access to the resource, or "hide" the resource by returning a 404: Not Found.

I could do that, but I want to make the site a bit more user-friendly and instead redirect them to their previous page with a flash (NOTE that I did not say 'Adobe Flash'; see comments) message instead of having a 403/4 page with links back to different parts of the site. I think it's a pretty UX-friendly feature.

However, that would involve redirecting via a 303: See Other, which more or less violates the standard.

Obviously it really doesn't matter as this is an in-house project for a lab at my university and nobody here is going to be complaining, but imagine this site were under the Google umbrella.

So, is it bad practice to deviate from the [HTTP] standard like this?

Edit: There seems to be a lot of confusion, so let me give an example:

User with id = 1 attempts to access mydomain.com/projects/2 through their profile (by clicking a link). User 1 is the owner of project 2, so the server accepts the requests and renders the page with the project.

User 1 then types mydomain.com/projects/3 into the URL. User 1 is not the owner of project 3. Therefore, when the request gets to the server, the server denies it.

Now, there are two options:

1.) The server redirects the user to /projects/2 (their previously visited page, which would of course always be a valid one) with a helpful notification at the top such as "Sorry, but you do not have permission to access XXXX" or

2.) The server renders a 403: Forbidden page, or a 404: Not Found page, which would probably have that same text (if it was a 403), and maybe even a JavaScript-enabled "back" link

Personally, the redirect seems way more user friendly to me, and eliminates seeing an unfriendly page like 403: Forbidden and having to click a "back" button. However, doing the redirect goes against the HTTP protocol standard.

  • 5
    Whatever you do, ditch the flash. – whatsisname Aug 18 '15 at 14:08
  • 1
    @whatsisname Why ditch the flash? Is there some flash-hating flash-mob that plagues the web? I'm not talking about Adobe Flash, if that's what you were thinking. One-off messages at the top of the page under the navbar that are closeable, like this. – Chris Cirefice Aug 18 '15 at 14:14
  • 3
    I've never seen those called flash anything. There is definitely an Adobe Flash haters club though. – whatsisname Aug 18 '15 at 15:12
  • 1
    @whatsisname Rails, Nodejs, PHP, ASP.NET. They're basically just session data that is set by the server after a request and rendered with the next page (e.g. after a redirect). Nothing to do with Adobe. – Chris Cirefice Aug 18 '15 at 15:18
  • 3
    Could you modify your application so that it does not generate links to resources the user does not have access to? – Moby Disk Aug 18 '15 at 16:52
16

The protocol exists, there is a clear answer for what to do. This isn't anything super secret - it is an "in-house web application for my university's department." Do the thing that confuses your users and applications the least in the way that allows for proper expansion in a way that is also consistent.

Consider the proposed page flow:

  1. User goes to /foo/1
    • User lacks access to /foo/1 but has access to /foo/2
  2. User is redirected to /foo/2 (30X response)

and the page flow of

  1. User goes to /foo/1
    • User lacks access to /foo/1 but has access to /foo/2 and /foo/3
  2. ???

and the page flow of

  1. User goes to foo/1
    • User lacks access to any resources
  2. ???

The problem is that the page flow for the first case cannot be applied to the page flow to the second one. This means that you are either going to have separate logic handling each case, or you are going to have something that surprises a user ("yesterday I was able to access my page via /foo/1, but today it doesn't work after I created a new project").

"Dumb" code is easier to understand both for the user and the coder. While trying to anticipate your users' needs is good thing, providing them something that they really aren't asking for is the wrong thing.

Consider the situation:

  1. Professor A sends Professor B a link to the project that Professor A has and asks for feedback from Professor B on it.
  2. Professor B clicks the link, gets automagically redirected to their own project.
  3. Confusion results.

Following the http specification makes it clear what you should do. Serve a 403 page.

So, serve up the 403 page. This doesn't mean you need to make it an ugly 403 page that the server has as the default. State that the user is asking for a resource that they don't have access to. Show them what resources they do have access to. Allow them to click those links to get to where they may want to go.

Don't try to make the software too smart or clever. It is important for the users of the software to be able to enter the mental model of how it works. If there are too many edge and corner cases for how the software works, or it works in surprising ways sometimes - this isn't good. Clever is not something to strive for in code, and it isn't something to strive for in UX either.

7

Don't be consumed by UX in these situations. A 403 or 404 code is extremely useful information to have. Some users may not understand it, but translating it into something else in a misguided attempt to make it user friendly is a bad idea. You need to show any 400 or 500 level error to users so the users can report them to you.

If a user gets a 404, that's a problem that needs to be fixed. If you are the help desk and someone calls in saying, "I clicked on a link and something weird happened and I got this strange message...," now you have to waste time trying to figure out what is going on. If they call in and say, "I got a 404 error...," now you know exactly what is happening, and you can respond much more quickly and precisely. Users should not see these errors often, so there is no need to pretty up the page or obscure the meaning.

You should also be logging 400 and 500 errors, too. Because sometimes users won't report the errors. Log them, and check the logs often. It can be shocking to see how many errors occur that you never hear about.

  • 1
    a 404 or 403 are also extremely useful tools for the person running automated intrusion probes, and for that reason you may not want to issue them. Issue your own pages instead, with a 200 series (or a 300 series at worst) code. The smarter intruder might notice and write his tools to detect them, but most are script kiddies who don't know how to modify their tools they downloaded from some p2p network. – jwenting Aug 18 '15 at 18:14
  • @jwenting That's understandable, but it's also a little paranoid. As the OP suggested, "imagine this site were under the Google umbrella." Google returns 404 errors. – Mohair Aug 18 '15 at 20:29
  • yes, it's paranoid. Comes from working at places like banks, stock exchanges, and government agencies for nearly 20 years. – jwenting Aug 19 '15 at 5:40
  • @jwenting Are you proposing subverting HTTP in exchange for (allegedly) increasing security? The same argument can be used like this: "don't follow any internet standards, because standards can be exploited by attackers." – Andres F. Sep 3 '15 at 21:09
  • @AndresF. no, we return a proper page rather than a generic error page in case of for example missing credentials. Don't care what http code the end user gets, it's probably a 300 series rather than a 400 series. And any 404 would automatically mean missing credentials as nobody is authorised to see any page he's not explicitly authorised for, including no page at all. – jwenting Sep 4 '15 at 5:32
6

You might generate the error page (for 403: Forbidden HTTP error code). That error page might contain links to appropriate (context dependent) pages.

I'm doing a similar thing on http://gcc-melt.org/ using my lev404cgi CGI program (free software GPL3+ licensed). Try opening http://gcc-melt.org/docu or http://gcc-melt.org/foobar; these are 404: Not found error pages, but the user is given a few links to possible replacements or suggestions.

Of course, I suggest to use standard compliant HTML5 content (in particular for a University web page, since at least the CS department would have a lot of Linux systems) and avoid Adobe Flash content (it is a proprietary format; on my Linux systems, I often have to start a non Firefox browser for them -which I rarely do-, so I won't start it for some "error-reporting" content).

  • 1
    He's not talking about Adobe Flash but about something like a toast notification. – Sebastian Negraszus Aug 18 '15 at 15:00
  • @SebastianNegraszus Correct, and because it seems that everyone thinks that what I meant, I edited my question. I know that I can render a 403/4 error page, the problem is that it's not UX-friendly in my opinion. 'Flash' messages are session-based messages that only last for a page change/render which are rendered in the view (generally using a partial). The point is that I want to avoid generating a 403/4 page because the user then has to press the back button. A lot of our faculty are not very experienced with computers. But this kind of violates the HTTP spec, thus my question. – Chris Cirefice Aug 18 '15 at 15:23
  • The user does not have to press the back button. Give them a link that does the same thing. IMHO, "flash" messages as you describe are not UX-friendly. Avoid messages that magically vanish. – Moby Disk Aug 18 '15 at 16:51
  • @MobyDisk I'm going to totally disagree, and say that most web service I've encountered have these types of notifications, usually near the top of the screen (Facebook is an exception, where they are often at the bottom-left). Sometimes the notifications are triggered by client-side validation, or AJAX requests where the server encountered an error, etc. Google uses these extensively throughout their services, as do many other services, especially with AJAX requests and server failures (e.g. a failed file upload) as I mentioned. – Chris Cirefice Aug 18 '15 at 17:46
  • 2
    @ChrisCirefice - There's a significant difference between a failed AJAX request (the user performs an action, expects some response), and a failed navigation between pages. I've been assuming this case is more like the latter, but if your "pages" are (trying to be) more like JS-heavy web apps, the answer could change. I don't have a good model for distinguishing between the two, but I can feel the difference between say, navigating between pages on SO (where a 404 might be reasonable), and performing an interactive request (like flagging) where a flash message would be natural. – Useless Aug 19 '15 at 16:45
3

Consider that returning a 403 may itself be leaking privileged information, i.e. how many total projects exist and how frequently they are being added.

I agree with the suggestions to redirect the user to a helpful list of what they are allowed to see. I would not reveal that they have located something that they are not allowed to access.

This may seem paranoid, since the leaked information may indeed be trivial. However, this actually makes the problem worse. People tend to value what they can measure. If they have poor metrics, they will make poorly informed decisions.

Consider scenarios such as:

  1. A department's productivity being measured by the number of projects that exist or the rate they are being added.

  2. A faculty member attempting to evaluate their position in the pecking order based on the percentage of total projects that they are allowed to access.

  3. A faculty member attempting to determine if they are being sandbagged by the rest of the department based on a proliferation of new projects that are being initiated without their involvement.

Too paranoid for an academic setting?

Moreover, I think it is good to make a habit of never allowing a system to reveal information that people do not need to know. If there is a way to abuse it (and there usually is), some clever person will figure it out.

In the same vein, it would be wise to identify the projects by something other than a predictably incrementing number.

2

tl;dr - I second Basile's solution, and don't believe you've thought through the implications of your preferred 303 redirect.

1.) The server redirects the user to /projects/2 (their previously visited page, which would of course always be a valid one) with a helpful notification at the top such as "Sorry, but you do not have permission to access XXXX"

What happens when someone follows a link from an email or IM message? Then there is no previously-visited page, valid or otherwise.

What happens when someone writes a wget or crawler script to report the status of a list of projects? Some projects will silently and mysteriously report the status of the last permitted project instead.

What happens when someone doesn't see the "helpful" notification at the top? They'll be looking where they expect to see the project's content.

I just don't see how an informative 403 page - which can explain what happened, be as pretty as you like, include a list of projects the user can view and/or a way to request access to the denied project and still be actually correct - I just don't see how this is less friendly.

  • +1 - good points. Granted, this really doesn't apply to my project particularly, but probably to most public-facing web apps. – Chris Cirefice Aug 19 '15 at 15:55
  • Another potential problem you've missed: what happens if the user has two browsers open, and the last one with a successful page load is not the one that's being used now. You can't achieve what you're trying to do in the general case. It might work in the most common situation, but if anything is unusual about what the user's doing, it will just make things worse. – Jules Aug 19 '15 at 17:49
2

It sounds like your use case is:

  1. When the user indicates they want to do XYZ, the browser sends a request to the server, which sends back a nice friendly reply page saying the user isn't allowed to do that.

In that case, from the HTTP server's point of view, the request isn't forbidden. You can have the server reply with status 200 without feeling guilty or thinking that you're deviating from anything.

Another common use case is:

  1. When the user isn't allowed to do XYZ, the XYZ button in the browser is disabled (or alternatively, not shown at all.)

In that case, you still need to check permission at the server, because people can use F12 tools to enable the button, or synthesize the kind of request a more privileged user might do. In that situation, replying with a 403 is common.(Though you want to limit how much information you expose.)

I think it's worth mentioning that the permission model assumed by the HTTP spec is fairly simple. If the user is sometimes allowed to do XYZ and sometimes not, or if the logic for determining whether XYZ is allowed is complicated, then the use case I think you're doing (number 1 above) is perfectly sensible.

Update: (in response to updated question)

What I was trying to say above was that you should distinguish between (a) users who are trying to bypass your access controls and (b) users who generally have permission do to the sort of thing they're trying to do, but need to make an adjustment in order for the request to be permitted under the current circumstances. You should not do a friendly UX for group (a), in fact the less information you give them the better. Group (b) might need a friendly UX, if the permission rules are complicated and there might be a way for them to fix the request to make it permitted.

In your updated question you say:

User 1 then types mydomain.com/projects/3 into the URL. User 1 is not the owner of project 3. Therefore, when the request gets to the server, the server denies it.

The question I encourage you to ask yourself is, why does the server deny it? If the very existence of project 3 is a secret from user 1, then pretend it doesn't exist and return a 404. More likely (especially in an academic setting) they're allowed to know the name and owner of the project, so return them a page that has that information, but not everything the project owner gets. If they're not logged in, or their role doesn't provide access to any projects, then returning a 403 might makes sense for any URL starting with mydomain.com/projects/. But it's all about who's allowed to see what and why.

When the user chooses not to click one of the links you provided, and instead hovers over them to see the format, then types in a URL that sort of looks like those links to see what it does, your responsibility to give good UX has ended and the user is straying towards group (a).

On the flip side, if it's something they're allowed to see, you should have a better UX for it than expecting the user to guess the URL. For example, if one of the features of the site is the ability to join a project the user didn't create themselves, then the user needs an easy way to see projects they might want to join.

0

I'm going to give a second answer, because looking back at what you wrote I think you might be asking a more basic question than I thought, one that deserves a very simple answer:

Is it okay to return the “wrong” HTTP status code in order to show a more user-friendly error page?

Yes.

The HTTP specification does not say that web applications (such as yours or Stack Exchange) are supposed to return a 403 status whenever the user steps out of bounds. Rather, it says that 403 exists and has a meaning, and that HTTP clients are supposed to react in certain ways when they see it. (It also implies that forbidding access is a common enough thing that it deserves its own 400 series status code.)

So unless you're writing an HTTP client or server (which should strictly follow the spec) or doing something like a REST API (the REST client being a special case of HTTP client) you have total freedom to never return a 403 status at all. You are not violating the spec by not returning 403.

My other answer gives examples of when returning 403 makes sense and when it might not.

0

403, 404 and 401 which you didn't mention have specific meanings.

403 (forbidden) doesn't mean that this particular user isn't allowed to access the resource, but that nobody is. It has nothing to do with that particular user, and there is nothing the user can do to be allowed access.

401 (unauthorized) means that the user wasn't authorized to access the resource, but that is a problem that could be fixed by the user getting authorization. Typically this is used when a user isn't logged in, or a password needs to be provided, or if different users have different rights, and this user doesn't have enough rights.

404 (not found) means the resource doesn't exist. Which is a perfectly normal thing to happen. Could happen if the user mistyped a URL, for example. But you should never give an error 404 if this particular user doesn't have the right to access the resource. 404 is meant for exactly the case that a resource doesn't exist.

410 (gone) is sometimes used; it means a resource was there in the past, but now it's gone, and it's very unlikely to come back. The difference to 404 is that this isn't due to something being spelled wrong. Last week the request might have been fine, but now the resource is gone.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.