Decode string with plus sign
Asked Answered
C

1

7

Using ASP.NET Core I am receiving the following token (simplified):

String token = "Z%2F3+3Q==";

The / is encoded using %2F. I received the token from a URL called on the client using Angular.

I tried to decode the token using the following:

HttpUtility.UrlDecode(token)

Or

WebUtility.UrlDecode(token)

In both cases %2F is replaced by /, which I want, but the + is replaced by a space, which I do not want.

How do I decode the string?

Update

The client application is sending the token encoded:

Z%2F3%2B3Q%3D%3D;

But, somehow, it seems the token in the following action:

[HttpPut("account/verify/{userId}/{token}")]
public async Task<IActionResult> VerityEmailAddress([FromRoute]AccountEmailVerifyModel model) {
}

Is transformed to:

Z%2F3+3Q==

So the + and = are decoded, but the / is not.

Credential answered 6/1, 2020 at 20:14 Comment(9)
If you want the end result to include the plus-sign, the sending application will need to escape it properly. If you can't change the sending tool, then you may need to do it yourself before calling UrlDecode(...).Betroth
Try URL-encoding a string that looks like "this/that+other space". The result will be "this%2fthat%2bother+space". The space gets encoded at + and the plus sign as %2bCrocket
@Betroth The client application is encoding correctly the +, the = and the /. But somehow the model binder decodes everything but not the /. I added an update to my question.Credential
What does the original token supposed to be?Castle
@Çöđěxěŕ The original token should be Z/3+3Q== ... And it is a token generated by ASP.NET Core Identity to verify an email address.Credential
@MiguelMoura ok I may have something shortly.Castle
@MiguelMoura the actual reason for the problem is the actual encoded token, the + should be %2B, the token is malformed. This worked for me HttpUtility.UrlDecode(Encoding.UTF8.GetBytes(token), Encoding.UTF8).Replace(" ", "+"); You can also confirm by having your token to Z%2F3%2B3Q== and then do HttpUtility.UrlDecode(Encoding.UTF8.GetBytes(token), Encoding.UTF8) without the replace and it works just fine.Castle
My comment above is based on the main issue you were having with Z%2F3+3Q==Castle
since normally a + should be encoded as %2B it is strange that that is not how it's being sent to you... but if the plus sign is an exception with whatever encoding then you must handle the + sign separately: either replacing space with + afterwards as @Çöđěxěŕ mentioned or by replacing + with the correct "%2B" code before you put it through the decode function.Transformism
I
11

The key here is that this is being passed as a route parameter and the specific character that’s not being properly decoded is a /. As you might imagine, there's some potential problems when dealing with a / in route parameters, since they're normally treated as route delimiters.

Background

Ultimately, this is a known limitation with both the ASP.NET MVC Framework and now ASP.NET Core. There are quite a few discussions about this on the ASP.NET GitHub site. On #4599 (from 2016), there's a lengthy debate about whether or not this is the correct behavior. On #4445 (from 2019), a contributor from Microsoft committed to providing an option to allow decoding of these parameters.

Unfortunately, however, this didn't make it into either ASP.NET Core 3.1 or ASP.NET Core 5.0—and while it remains open, it has been demoted from a PRI: 1 - Required issue to a severity-minor issue.

Workaround

Until Microsoft provides a solution to this, there are three options for working around the behavior:

  1. Pass the {token} parameter via the query string instead, where it will be fully decoded.
  2. Add code to your application that explicitly decodes the %2F after binding has occurred.
  3. Create your own IModelBinder to modify the model binding behavior itself.

Update: Contributor @Celluj34 has provided a sample implementation of a custom model binder to solve this on the GitHub issue.

None of these are especially satisfying. But this at least confirms that the behavior you're seeing is known and expected, even if it's arguably incorrect.

Illusionary answered 6/1, 2020 at 21:2 Comment(1)
I faced the same issue, read all articles. What it means is never use route segment strings that can take any value/character. If you want to accept any value, use FromQuery or other type of string. Microsoft won't change this as they consider it normal (at least consistent) behavior. May it is.Hussite

© 2022 - 2024 — McMap. All rights reserved.