Passing an encoded forward slash as a route parameter will be interpreted by the router as a decoded forward slash.
Example Code:
Route::get('search/{search}/{params?}', 'SearchController@searchItems')
->where('params', '.*');
class SearchController extends Controller
{
public function searchItems(string $search, string $params = null)
{
dd($search);
}
}
Using the route:
/api/search/10%2F22/param1/5
Will result in the output of 10
instead of the expected 10%2F22
.
Dying on $params shows 22/param1/5
confirming that the slash is being interpreted as a real one.
This is the intended behaviour, check https://github.com/laravel/framework/pull/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 %2F
for this kind of problem.
Just don't use slashes in the URLs. Use something else.
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
When a URI is dereferenced, the components and subcomponents significant to the scheme-specific dereferencing process (if any) must be parsed and separated before the percent-encoded octets within those components can be safely decoded, as otherwise the data may be mistaken for component delimiters. The only exception is for percent-encoded octets corresponding to characters in the unreserved set, which can be decoded at any time.
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 GET /search
route:
Route::get('search/{query}', 'SearchController@search')
->where('params', '.*');
Expected behaviour:
/search/a%2Fb
will go to the SearchController
./search/a/b
will return a 404.Actual behaviour (exact opposite):
/search/a%2Fb
returns a 404./search/a/b
will go to the SearchController
.@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.
@taylorotwell @themsaid Please comment on this.
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:
/search/?q=search%2Fslash
Of course, this works in Laravel, as it does not put the parameter into the route.
For search, this may even be good practice, as q
is kind of a standard parameter for search.
So you might consider this option when you define new URLs with parameters that might contain slashes.
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:
https://github.com/symfony/symfony/issues/13017
https://github.com/symfony/symfony/issues/6442
Regarding "security considerations" you mean this?
https://www.owasp.org/index.php/Double_Encoding
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 realpath
in PHP to handle such problems properly.
Moreover, if you have another security filter that refuses <
to avoid XSS, this would mean you should not allow %3C
. So why does %3C
work but %2F
doesn't? Because we cannot disallow everything on the URL level that might be bad on some level below if not handled there correctly.
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 ~and this is already possible atm.~
~@Ben-Russell in your example you also need to define a wildcard character for the search
parameter which would allow forward slashes. If you change your route to the following it will pass:~
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.
Try the following Route in a new Laraval application:
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: Show song 'T.N.T.' of band 'AC/DC'
.
But the result is: List all songs of band 'AC/DC/T.N.T.'
The typical use case: The user enters his favorite band name into a text field and the request is generated using javascript something like '/band/' + encodeURIComponent(userInput) + '/'
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: https://github.com/laravel/docs/pull/4785
When using Laravel 5.7.13
Route::get('/band/{band}/{song}',
function ($band, $song = null) {
dd($band, $song);
});
And visiting laravel.local/band/ac%7Fdc/c%7Fd
, I get the below:
"ac\x7Fdc"
"c\x7Fd"
Also includes the \x7Fd
for more than 2 segments, so it should be possible ?
@SDekkers The %7F
from your example is ascii character 128, i.e. the DEL
control character.
But we are looking for %2F
which is the encoded forward slash /
. The problem ist that Laravel/Symphony treats %2F
the same as /
. That should not happen.
@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 U
modifier, and even if it would, it would be guessing what belongs to which parameter.
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
But you can do myroute?parameter=xxx%2Fzzz it will find the route myroute and send the parameter decoded just right
Like @renlok said in 2017
This shouldn't be intended behaviour
@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 /
. Encode it to %2F
and then encode it again. This will result in the string %2F
being in the URL, which you can then fix in a pre-processor for the web server.
@andrewfinnell this saved my day
Amazing @andrewfinnell, finally, this has worked!!! Thanks!!
Most helpful comment
This shouldn't be intended behaviour