-
Notifications
You must be signed in to change notification settings - Fork 11.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Encoded forward slash (%2F) in parameter not routing correctly #22125
Comments
This is the intended behaviour, check #4338 |
This shouldn't be intended behaviour |
I don't understand how this is intended. This is contradictory to the entire purpose of encoding a parameter. |
Please read https://tools.ietf.org/html/rfc3986 and explain why this should be the intended behavior. I understand that the RFC specifically introduces the percentage-encoding like
Following this argumentation, you could also say: Don't use spaces, % or ?, ... in your URLs. Use something else. Then we would not need percentage-encoding at all. Plus, it is not always the laravel-developer who designs how URLs look like. For example, it might be that laravel replaces a legacy system that used URLs like this and the new system should be capable of working with the same URLs, as otherwise all links would be broken. "Use something else" is not possible if already thousands of links point to URLs like this. |
To piggy-back off crazy4chrissi's comments: In our case, we don't really have a choice, since certain strings that compose our URL segments come from data provided by our vendors. The best we can do is URL-encode them, but we shouldn't have to dream up our own encoding to get around this issue, when the intention of encoding the slashes in the first place is to prevent them being treated as URI segments. |
https://tools.ietf.org/html/rfc3986#section-2.4
|
Just ran into this problem. Someone placed an '/' which got encoded in their post title and the website goes down. |
The current behaviour is highly unintuitive. Consider the following example of a Route::get('search/{query}', 'SearchController@search')
->where('params', '.*'); Expected behaviour:
Actual behaviour (exact opposite):
|
@d-luk Search is a pretty good example use case where the argument "Just don't use slashes in the URLs. Use something else." would mean you cannot use the GET method for search-forms, as you cannot forbid users to enter slashes. But using POST is just not the right way and creates other problems. And its not like search is a rare use case. |
I work in a school and we want to integrate with our third party calendar. The calendars IDs are of the form 2018/2019 etc. so this problem rears its head for me too. |
Major facepalm on this one. Come on, guys. Please fix this ASAP. This should NOT be intended behavior. |
Looking through github issues for all major php frameworks, this "issue" isn't just a laravel problem. I have to assume its not as simple as some think. There's also security concerns and web server considerations. |
I analyzed a couple of presumably Laravel based websites. Laravel.com uses POST for searches (via JavaScript). Without JavaScript, laravel.com does not have search functionality at all. All other sites I analyzed that have search functionality use urls like this: Of course, this works in Laravel, as it does not put the parameter into the route. Still I see no reason why this should be the intended behavior. |
@devcircus That's interesting. Can you provide links to issues from other php frameworks? Edit: I found these for Symfony: Regarding "security considerations" you mean this? I'd say if your app has a "security check filter that refuses requests containing characters like “../”.", i.e. you filter based on a black-list, then your filter is bad. There is stuff like Regarding web server considerations: Just because a few web servers (mainly Apache) don't support all RFC compliant URLs by default, this does not mean laravel needs to add another layer that is RFC incompatible. Moreover, Apache can be configured to allow such URLs, and the Apache documentation does not mention any security implications. It may be that there are good reasons why this is considered the intended behavior, but I have not read any so far. Just saying doing otherwise is not good practice is not enough, you would need to explain why it is bad practice. |
Apache does support / in the URL's you just need to turn it on in the Vhosts section 0 AllowEncodedSlashes On This is absurd that Laravel doesn't honour the escaped part of the URL - its feels wrong to need to have to escape these with something different to handle "data" in the URL when there is already an escape sequence for data. Double encoding is not related - this is not what were doing here. The confusion is that this is part of the path parameter not the data component so therefore does the RFC apply? Surely the best approach would be to allow a route definition to accept encoded slashes at the point it is defined, thereby enabling the current behaviour and giving the option of handling URL's in this way should the developer wish to. |
Hey everyone. I did some research on this
This is the same way as Symfony works: https://symfony.com/doc/current/routing/slash_in_parameter.html I'll send a note to the docs which will make this more clear if anyone else is looking for this but going to close this as this is possible if you want to. I hope you understand why we don't want this as a default behavior. |
@driesvints Sorry, but that solution does not help for encoded slashes. Route::get('/band/{band}/{song?}', function ($band, $song = null) {
if ($song) {
return "Show song '$song' of band '$band'";
}
return "List all songs of band '$band'";
})->where('band', '.*')->where('song', '.*'); Now access http://localhost:8000/band/AC%2fDC/T.N.T. I would expect to see: The typical use case: The user enters his favorite band name into a text field and the request is generated using javascript something like So please reopen this issue. |
Hey @SebastianS90 you're right. I've removed my example above to prevent confusion. I did some further testing and I've come to the following conclusion: it's only possible to allow encoded slashes in the last parameter. It's also impossible in Symfony (as far as I can see) to allow this for multiple segments after each other. I've added a note to my PR to the docs about this: laravel/docs#4785 |
@SDekkers The |
@SebastianS90 Ah, that's what I did wrong then. Looking at the regex it generates to obtain the parameters from the route: "#^/band/(?P<band>.*)/(?P<song>.*)$#sDu" I would say you should try to only use forward slashes in the last parameter as @driesvints mentioned, as the regex does not use the Ungreedy |
I think the status quo makes sense, haven't had a chance to test tha approach yet, as long as we have a prefix the remaining data should be encoded / decoded within your application with any symbol on the url using @driesvints example - I can kinda see how it needs to be one thing or the other, the frustration for me was that i wanted "/" in may parameter value - if your route then parses everything to the right then surely this is optimal? What I missed was to look at the symphony docs that underpin Laravel, I think trying to escape the URL otherwise is a mistake, the URL should work regardless of escaping however and that I'd like to test when I get the chance. I ended up replacing the / for an alternate character in my code (this was to generate a perma link) but it would have been better if that link was not limited to the characters it could contain. |
So as for now we're going the same way as Symfony. We've updated the docs to indicate that it's possible for the last route segment. This also reflects the Symfony docs. |
Very confusing to me how this is an intended behavior... any plans for changing this behavior? |
@iget-master no |
So let me get this straight: If i have product codes with slashes in them (a real-world example "19NA-MSD/NAV+44XG"), I can't make a RESTful GET route where one parameter is the url encoded product code? |
Yes. @mvaljento this bug hunts us for years. The router should resolve the route and after that decode parameters, but it does decode the url parameters before resolving the route. Therefore we must use parameters that may have "%2F" after the ? You can't do myroute/xxx%2Fzzz it will try to find a route decoded to myroute/xxx/zzz Like @renlok said in 2017
|
@mvaljento I changed the Artistan/Urlencode to work on Laravel 5. You can get it here https://github.com/alcaitiff/laravel-urlencode |
For anyone still interested, I've solved this in the past by double encoding the |
@andrewfinnell this saved my day |
Amazing @andrewfinnell, finally, this has worked!!! Thanks!! |
Hey, And now everything works fine :) |
I just want to chime in as a fellow developer, but I'm not a Laravel user myself. I found this issue while doing research about this very problem, because I want to make sure we address this in the Caddy web server as best as possible. So don't mind me, I'm kind of writing this post to work out my own thoughts on the matter. My understanding is: the complaint is that servers/server-side apps/routers (Laravel) treat I have spent days in the weeds on this very topic, and let me assure you, it is really quite tricky in the general case (and Laravel is a general-purpose framework) to strike a proper balance between:
If we have all put down our pitchforks by now... I will say I can understand why the Laravel authors would rather keep Laravel's URI handling in normalized space. And I can also understand why there hasn't been much explanation given or response from them, because frankly this issue is exhausting. It should be noted that this problem is not Laravel's. It is not Caddy's. It is the result of a complex mixture of legacy software, bugs, evolving Internet standards, and application requirements. A server has it tough these days. I'd like to offer one server developer's perspective for a moment if I may. People use Caddy to both match/route requests, and manipulate them too. Any given URI has multiple valid encoded forms. RFC 9110, "HTTP Semantics," has a section on HTTP URI normalization, which says:
In other words, Note that several RFCs, notably RFCs 3986 and 9110, continually repeat that URI parsing is dependent upon scheme. That's one other problem: we all use the To clarify, it is definitely possible for a URI path such as The proposed solution by others above to double-encode URIs is definitely a hack, and it violates spec:
I mean, if it works for you, cool, but just beware of the non-conforming behavior and highlight it very prominently in documentation so you can avoid bugs. One other interesting comment, by @alcaitiff:
I can't speak for Laravel or what it's doing, but the Go standard library (what Caddy uses), for example, does do URL parsing correctly and still has this problem. Go does exactly what you and the spec recommend: it splits the URI into its components and then decodes reserved characters after parsing. It preserves the original, "raw" path in the We can write our own logic, though, that uses RFC 3986 section 2 states:
The So I understand the frustration of everyone here. On my end I feel like I need to write a server that can read people's minds: is this slash data or is it a delimiter? The router needs more information, because both are very valid ways of interpreting a URI! I'm currently thinking of ways that maybe Caddy can be configured by the developer to route requests with some "hint" that certain slashes should be treated as data, not delimiters. If the HTTP server or router has a clue as to your business logic, that can help solve the routing problem, but I'm still stumped on how to let users configure rewrites. Maybe I'll save that for another day. |
Hey, |
@AsafMazuz1 Indeed, good thoughts! That's pretty close to what I'm experimenting with already. 😃 If you want to match a route like But maybe if we could write a matcher pattern like I'm still testing this approach to see if it's viable/feasible, since I don't know what kinds of parsing ambiguities or security problems may arise, but if it works maybe it's something that other frameworks or servers like Laravel could benefit from too. |
It works. I have a solution that works, that doesn't resort to "compare all URIs in the normalized space only". It allows the developer to convey their intention for certain parts of the path. If the route pattern is crafted correctly, I believe this solution is even robust against common path attacks and security flaws -- though I need to test this more, I'm probably overlooking something stupid. In Caddy, you might have configured something like this:
This would send all band song pages to your PHP backend (i.e. However, while I was writing my first post above I was already hacking on a solution similar to @AsafMazuz1's idea, since what we need is a way for the developer to tell the HTTP server or router to leave some part of the path escaped. It obviously can't know this without some help. I now have tests passing for an input path like
which tells Caddy's router ("path matcher") to leave those parts of the path URI-encoded. The hint is the What other servers/libraries let you give it a hint as to which specific parts of the path should be treated as data (versus delimiters)? I only know of solutions like AllowEncodedSlashes that turn on/off normalization entirely, rather than normalizing only the desired parts of the path. Anyway I'm excited to test this more and I hope it can be a source of inspiration for Laravel and other frameworks/libraries that may want to do the same. |
Glad to hear that you manage to work this out. |
The current routes with a pattern like `subjects/byname/{name}' don't work for names that include encoded characters, for example `foo%2Fbar` (i.e. `foo/bar`). laravel/framework#22125 Use a query parameter `...?name=...` instead.
This is crazy... "Intended"... |
Description:
Passing an encoded forward slash as a route parameter will be interpreted by the router as a decoded forward slash.
Steps To Reproduce:
Example Code:
Using the route:
/api/search/10%2F22/param1/5
Will result in the output of
10
instead of the expected10%2F22
.Dying on $params shows
22/param1/5
confirming that the slash is being interpreted as a real one.The text was updated successfully, but these errors were encountered: