Should phx.gen.auth return 401s

This code assumes that get_organization_by_slug!/2 raises anEcto.NoResultsError which would be automatically converted to 404 , but you could also handle the error explicitly and, for example, set an error flash and redirect to another page, like a dashboard.

The get_organization_by_slug!/2 function should also rely on the current scope to filter the organizations to those the user has access to.

Would love for Phoenix to ship with “Login to gain access” screen. This is quite common and a far better UX than throwing a 404.

Also having no result due to scope should throw a
401 Unauthorized imho.

Although the HTTP standard specifies “unauthorized”, semantically this response means “unauthenticated”. That is, the client must authenticate itself to get the requested response

Or (when a user or org is known but no access)
403 Forbidden

The client does not have access rights to the content; that is, it is unauthorized, so the server is refusing to give the requested resource. Unlike 401 Unauthorized, the client’s identity is known to the server

Having these available and correct would help developers doing “the right thing”.

1 Like

There’s a security aspect to this between 404 and 401. 401 does acknowledge the existance of a record with the provided id, which might already be interesting information to a bad actor.

6 Likes

I am aware of it. But we have these status codes for a reason and the app should be secure enough for it not to matter.

Security by obscurity has it’s place but should not be the default.

Also: hackers do not really care about what you provide them. They are every well aware of appa faking it. Source: I work with security specialists (hackers)

2 Likes

This is not security by obscurity. A 401 does return real information (a record exists). That’s different to returning no information (404 not found). If that information is something you want to expose is a decision you can make, but phoenix imo should start with the conservative choice.

2 Likes

Not found is also information. It informs that the resource does not exists and there is no way to get it. Authenticated, authorized or any other way. Which is not true. The resource might exist, you just don’t have access to it if it does (which is afaik nog explicitly a requirement. A 401 can be followed by 404 once authenticated)

You call it “security by obscurity”, I call it principle of least privilege. :slight_smile: I typically return 401 when it applies to all users equally. For example, a /dashboard page is available to all users but they must be logged in before, that’s a 401. 403 when the user is logged in and the access is something that can be changed within an organization, such as “ask your manager to give you permission”.

Scopes are mostly about cross-user/cross-organization access, which do not fall in the categories above IMO. To give an example, imagine someone heard that a company is working on a new product called “foo-bar”. If their CMS returned 403 for a marketing page in progress or if their GitHub returned 403 for github.com/company/foo-bar, you could potentially be confirming rumors.

The trade-offs between security and user experience are often debated, some will argue for one or others will argue for the other. Phoenix most often goes on the side of security.

3 Likes

It does not afaik. 403 does though.

I see your point but it really makes me think of: xkcd: Security

Also: all the reasons not to put such info in your prod-systems upfront at the correct URL as most leaks are by ‘accidental publishing’ :slight_smile:

In most companies is see a CMS folder ‘hidden’ or ‘pre-publish’ which are not accessible from outside the company network. In a lesser segmented network configuration they should (inho) throw a 401 (for the whole folder needs being authenticated) or 403 (when junior-editor instead of marketing-manager tries to access a post which he/she’s not cleared for).

So there are layers…
401 → 404 valid
403 → 404 valid
404 → 200 invalid
401 → 403 valid
401 → 403 → 404 invalid
401 → 403 → 200 valid

Mods: split thread?

Anecdote:
Once I battled security scanners by sending the wrong server-header, seeing them taking (a very old) Apache branch of tests instead of the actual up to date (and custom hardened) Nginx.

The pentest team did not like it as it caused much more manual work, so the dev team has a good laugh for a couple of years. After all: it was a friendly “us vs them”. External scanners failed miserably.

After 4 years a customer noticed and complained “you run a very outdated webserver and it is inexcusable for a IT security company.”

Auch…. :sweat_smile:

Removed the fake server header. Result: Less fun. More customer trust. :+1:

——

Edit:
This kinda does proof my points though:

  1. wrong headers or status codes cause confusion also for good actors. Developers for instance who don’t understand the resource does not exists (404) while they just POST-ed it. 401 and 403 explain themselves and guide in debug process.

  2. Pentesters and non-junior hackers can be fooled for a short time but not for long. These are the types of hackers that invest more time to find vulnerabilities as your data is an asset of interest for them.

—-

Also also: 404 when a source is checked? We notice the difference in timing so timing-attacks breaks your fake surface. So unless we buildin a realistic load-simulation. You get the idea….

Back to the proposal: follow the standard. Let devs needing extra’s do it themselves as it is a lot of work anyways and changing 401/403 tot 404 is a micro adjustment in comparison.

To quote a certain dev (slightly adapted, with a wink)

I don’t think this argument holds in the real world. Obscuring resource existence is nowhere close to the hardest part of secure apps. The true complexity lies in the surrounding infrastructure: a system to deceive attackers. Even seemingly simple elements like rate limiting, obfuscation and couter measures against timing-attacks require careful consideration.

:wink:

1 Like

We don’t plan to change it on the Phoenix side but uou can opt-out and define your own. It will be 5 LOC in your app:

Also also: 404 when a source is checked? We notice the difference in timing so timing-attacks breaks your fake surface. So unless we buildin a realistic load-simulation. You get the idea….

The timing difference for a non-existing page and a page that exists but failed because of a database WHERE condition is very tiny. They both fail because the same query returns nothing. This is different from enumeration attacks from say, email+pass auth, where there is a large proof of work after you read from the database.

2 Likes

Status codes aside…guess this won’t happen either?

Add it as part of your app, if you need it, when you need it, how you need it? :slight_smile:

Unfair. Could be said for everything :roll_eyes: Phoenix is a batteries included framework, not a low level lib or language. We get Auth, we get a login screen, we get a reset password screen, we could get….

But it seems settled :slight_smile:

:heart:

Most of the release for v1.8 was exactly in trimming down what we generate because it was clear that it was generating too much, getting in the way of both experienced devs (generating stuff they didn’t want) and new ones (too much to grasp at once). So there is a clear trend we are aiming for when it comes to generated code.

It is already exhausting to be in the forums and getting to constantly hear about everyone’s wish list. And on top of that, getting eye rolls, called weird (another thread) and unfair. When maintainers get characterized negatively for making decisions aligned with project goals or when simply sharing their opinion, it makes it harder to justify spending time in these informal discussions. So overall it is probably best for me to keep some distance and not get involved unless through official proposals.

12 Likes

That’s my fault, please don’t get mad at Bart because I’ll feel extra bad.

I get that. I sometimes just speak open over wishes, but there are no hard feelings when they are not granted. For me this was a “exploring” discussion pushing back a bit. Sorry if it sent the wrong message. Love and respect for the hard work you and the rest of the team do.

Just to be extra clear about my standpoint:

Developers are not obliged to respect anyones wishes, nor to fix bugs, not to fix docs, nor to discuss feats etc etc in software which is free to use and especially when one can contribute (or fork in extremes).

Developers sometimes might feel they are but they are not. Just have to remind ourselves so every once in a while.

2 Likes

Even a tiny timing difference can be surfaced by making enough requests and then comparing on average - similarly, you cannot “hide” a record by adding a random offset because enough requests will reveal the mean (I’m sure you know this, but maybe others don’t).

But when looking up a record in a DB you’re usually hitting an index, which should make it considerably harder to figure out whether the record actually exists. The btree lookup will still make it all the way to the last page before discovering whether the record is there or not, and the timing should be roughly the same I would think.

If there was no index (a full sequential scan) and the record in question was in the middle you would have a much more pronounced difference, of course.

This is nice to hear, running phx.gen.auth was indeed quite overwhelming. I implemented my auth by hand following phx.gen.auth (it’s a great example), so I actually know (knew) how it works exactly line-by-line. It took a while…

1 Like

I feel like this point got lost in the discussion, but it’s very important. Hiding a user’s record from the public internet is not security through obscurity. It’s not even security! It’s privacy! You should avoid exposing any of your users’ private information by default.

Information is only transferred when you deviate from your standard response. If you always return 404 when a page is not found, then returning 404 for a private record confers no information about the record.

Similarly, if you play poker and you always maintain your standard poker face, you confer no information about your hand. But if you have a tell, regardless of what that tell is, you confer information about your hand!

True. But it is still information: that the requested resource does not exist.

Hiding a user’s record from the public internet is not security through obscurity. It’s not even security! It’s privacy! You should avoid exposing any of your users’ private information by default.

This was not the point. The point is if we should fake ‘non existent’ or use the standard response code “403: you are not authorized to access the resource” (or 401: you are not authorized at all, regardless of the specific resource)

My standpoint is “don’t fake it does not exists when it does as it confuses good people too and violates web standards we all value so much since IE ”, the other standpoint is “fake it does not exist so it’s existence is obscured from bad people; good enough reason to violate the webstandard”. To the latter I reply: obscurity won’t matter when a non-junior hacker targets your app for real, according to quite a few hackers in the company I work at.

2 Likes