How to accept "#" as part of a GET query parameter

I am consuming an sso login request that looks like:

http://<domain>/<path>/?a=<AuthToken>#*<UserID>#*<AccessToken>

where the domain is my app.

The problem is in the controller when I try to access the “a” parameter to make the exchanges. Rather than returning:

a = <AuthToken>#*<UserID>#*<AccessToken>

it returns

a = <AuthToken>

which seems fair, as the “#” is a delimiter.

Is there any workaround to consume this URL? Even inspecting conn shows the lopped version.

Thanks!

The character # is a reserved character in URLs and needs to be URL-encoded as %23. Phoenix will URL-decode the content before it gets to your controller and your should be good to go.

4 Likes

yeah… i totally get that this needs to be escaped, but this is the API specification from another team, and I am afraid they will not change this.

I think you misunderstand. If you escape it then it should work exactly as it is specified. Escaping it is the means of it arriving in the form of # to the API.

The underlying issue isn’t something that Phoenix can control - the browser is responsible for handling # in URLs. For instance:

  • server A sends a Location: http://example.com/some_path/thing?a=value#*this#*part_will_not_be_transmitted header with unescaped #
  • client browser parses that URL and sends a request to example.com with path /some_path/thing?a=value
  • example.com server receives a request with params [a: "value"]

The issue is, have no control over the creation of the url. the consumer of the api sends that link to our site. we have to process whatever is sent over.

OH! I had no idea that this was on the browser level. This makes sense, as these are delimiters.
I thought there was some way we could work around this.
Just as a tests of the concept without phoenix in the mix, I sent a random request with:

?a=value#*this#*part_will_not_be_transmitted

through plain old apache, and apache saw:

a=value

Thanks!

Browsers these days will do both url encoding for you, and also idna encoding.

When testing and you control the URL, you might consider using URI.encode/2 as well as the raw URL to confirm that it can be parsed as you expect.

The key point here is that the SSO server does not intend for the part behind the # character to be sent to the server: the AuthToken value really is just the part the controller sees. The rest is available in the browser to any client-side code that may need to extract the User ID or Access Token values.

2 Likes

in this case, they send over three pieces of info that I need to make a call to another API function. these three pieces of information are separated by “#*”. Each piece of info is required in the call. manually replacing “#” with “%23” allows the call to be made correctly…

There’s a standard way to send multiple pieces of data in a query string: use multiple parameters delimited with & (?a=123&b=456&c=789). Why is the calling system making up its own data format?

1 Like

It looks to me like the SSO server is trying to deliver the AuthToken to your server, and the other two parts to the client. This sounds a bit like a combination of the OAuth “authorization code” flow (which delivers a code that the server can exchange for an access token though a server-to-server API) and the “implicit” flow (which delivers the access token to the browser for use by front-end code). See rfc6749

There is a reason why the access token should not be sent to a server through redirection. You can read about that in rfc6819.

I can’t speak for this particular SSO server and their intentions, but I strongly suspect they did not intend for all three parts to be delivered to the server. They may offer an API to exchange the AuthToken for a user ID and access token, the way OAuth 2 does.

1 Like

@voltone - they indeed want us to read all of the data into the server for user verification. Their solution to this, “rewrite your application in angular.”

@al2o3cr - They used to send the data over as three separate params. someone on their team decided that someone might figure out what these variables are by their names, so they concatenated them all into a variable called “a”, and separated each piece by “#*”. They consistently make up their own formats.

Step 1 if you’re concerned about security: DON’T WRITE YOUR OWN SSO

Step 2: SEE STEP 1

Edit: this is particularly silly security theater since the URL is formatted in a way that’s only accessible to Javascript; a malicious attacker could surely discern more from how the values are used (in the JS that is deliberately served to them) than from keys like a, b, or c in the URL.

3 Likes

I understand this, but the issue is, we’re writing an application for company A. Company A also contracts with Company B. Company B wrote the SSO implementation in question. They also wrote their own hashing algorithm for encryption. As Company C, our only job is to implement the requirements.

But, in good faith, I have outlined where the security flaws are. Their response: rewrite your application in Angular. None of our Angular developers are complaining.

And I’d take them to court for being unprofessional and allowing themselves to say how I do business. :smiley:

What does your managers say?

Just write a few lines of javascript to parse the url fragment then do a fetch() itself. It is silly but you got to make do with what you get.

1 Like

@dimitarvp management is informing that contractor that they are to provide us a correct solution…

@derek-zhou this is actually what i did in the meantime… but this is another point of failure that we don’t want to have on our hands if something else breaks.

If you absolutely need to work with this hacktacular scheme, I’d recommend the same thing as others - write a landing page with some JS that collects the client-side parameters and forwards them to the server.

One other point of advice: unless UserID in that <AuthToken>#*<UserID>#*<AccessToken> payload is somehow cryptographically-signed, I wouldn’t recommend storing or using it for anything locally. The value is not trustworthy - a malicious user could edit it in the URL to be whatever they want. This is why SSO protocols designed by people who know security take one of two different approaches:

  • Oauth2’s authorisation code flow solves this by having the value that’s exposed to the client (the code) only used to get the real information (the access token) via an authenticated server-to-server call. All that manipulating the value in the client browser will do is produce a login failure.
  • SAML’s IDP-initiated flow uses public-key cryptography to produce a tamper-proof payload describing the user. The client can’t change the supplied data without knowledge of the server’s private key.
4 Likes

This is still being worked out on the provider’s end. Our side is saying “make it correct”… their side is saying “stop saying that!”… oh, and the userid is being sent totally ion the clear. it’s not really an id, it’s a plaintext username like “fflintstone”…