Problem/Motivation
The module does not properly authenticate or protect REST endpoints.
If you have no patches applied: If you don't pass the expected Accept
header, you immediately bypass IP-based authentication, because the IPConsumerAuth::applies
function returns FALSE
.
If you have a patch applied from #2942049: Prevents normal website access by restricting all routes by default or #3367992: Apply changes to apply security coverage: the IPConsumerAuth::applies
improperly returns FALSE
on the first call after sending a request, because the request doesn't find a matching route, and so the _format parameter won't be found or the header won't be checked, depending on which version you have.
Basically, if IPConsumerAuth::applies
returns FALSE
at all, then the IP address is not checked, meaning anyone can access the endpoint.
Steps to reproduce
- composer require drupal/restui
- drush en rest restui ip_consumer_auth
- Either apply the changes at #3367992: Apply changes to apply security coverage, or modify the cURL commands below to use an Accept header instead of a _format param.
- Create a custom REST endpoint, or enable a built-in one. I made a custom one. See attached CustomResource.php file.
- Enable GET method, JSON response, IP Consumer Auth for authentication provider. Save.
- Go to /admin/config/services/ip_consumer_auth
- Enable for all formats. Use blacklist. Enter a bogus IP in the Blacklist, so that it should be enabled for all other IPs. Save.
- Use xdebug and set a breakpoint in
IPConsumerAuth::authenticate
, or put something else so you know when it's called or not. - Clear cache with
drush cr
- Make sure the route works over cURL from the CLI.
curl -o - -I -X "GET" http://my_site.ddev.site/api/custom-resource?_format=json
- Go to /admin/config/services/ip_consumer_auth
- Change to use Whitelist. Keep the bogus IP, or try leaving the IP whitelist blank. So your IP should not be allowed. Save.
curl -o - -I -X "GET" http://my_site.ddev.site/api/custom-resource?_format=json
- Are you still able to access it? You shouldn't be.
Also, if you applied the patch that lets you use _format in the URL query params, try this (leave _format blank):
curl -o - -I -X "GET" http://my_site.ddev.site/api/custom-resource?_format=
Doing this bypasses IP auth altogether, and IPConsumerAuth::authenticate
is never called.
During all the steps above, make sure IPConsumerAuth::authenticate
is called each time you run a cURL command.
It's super obvious if you have no patches installed, just look at the function:
public function applies(Request $request) {
// Only apply this validation if request has a valid accept value.
$formats = $this->config->get('format');
foreach ($formats as $format) {
if (strstr($request->headers->get('Accept'), $format)) {
return TRUE;
}
}
return FALSE;
}
Only apply this validation if request has a valid accept value.
All you have to do to bypass the IP-based authentication is to not include a valid accept value. That's no security at all.
Proposed resolution
Honestly, after a couple of days trying to get the module to work as-is or with patches, it just doesn't work. It doesn't properly prevent IPs from accessing the endpoint. Basically, the module has no tests included in the code, and it seems it hasn't been coded to work properly in all cases.
I think the proper solution is to have the module on for all "formats", putting return TRUE;
in the IPConsumerAuth::applies
function, and remove the settings to select the format.
public function applies(Request $request) {
return TRUE;
}
And in ip_consumer_auth.services.yml, set priority to -100 (See #2942049: Prevents normal website access by restricting all routes by default):
tags:
- { name: authentication_provider, provider_id: ip_consumer_auth, priority: -100 }
If you're restricting an IP address's access to an endpoint, it's probably not because you care about the format; it's because you don't want anyone else to have access to the data at all. If an IP can access data in CSV but not JSON, they can just convert the data to JSON after accessing it.
Remaining tasks
- Figure out everything that isn't working.
- Determine whether the format checking code should be in the authenticate function, or removed completely.
- Secure the module.
- Write tests.
Add tests to the module in a follow-up issue, making sure that functionality works as described, blocking IPs and allowing others. Blocking for formats, allowing others. Making sure that access cannot be bypassed by using an empty format or some other trick. An authentication/access control module should be robust and tested and secure.
I don't have experience writing tests, so this would probably require some other users to step up.
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#16 | ip_consumer_auth-3407083-15.patch | 4.06 KB | solideogloria |
#5 | CustomResource.php_.txt | 905 bytes | solideogloria |
Issue fork ip_consumer_auth-3407083
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
solideogloria CreditAttribution: solideogloria commentedComment #3
solideogloria CreditAttribution: solideogloria commentedComment #4
solideogloria CreditAttribution: solideogloria commentedComment #5
solideogloria CreditAttribution: solideogloria commentedComment #6
solideogloria CreditAttribution: solideogloria commentedComment #7
solideogloria CreditAttribution: solideogloria commentedComment #8
solideogloria CreditAttribution: solideogloria commentedComment #9
solideogloria CreditAttribution: solideogloria commentedComment #10
solideogloria CreditAttribution: solideogloria commentedComment #11
solideogloria CreditAttribution: solideogloria commentedComment #12
solideogloria CreditAttribution: solideogloria commentedComment #13
solideogloria CreditAttribution: solideogloria commentedComment #14
solideogloria CreditAttribution: solideogloria commentedComment #16
solideogloria CreditAttribution: solideogloria commentedPatch of the MR changes for ease of testing.
Notice that I included all the changes for #2942049: Prevents normal website access by restricting all routes by default and #3367992: Apply changes to apply security coverage as well.
Please review the changes so I know if it works for more than just me.
Comment #17
solideogloria CreditAttribution: solideogloria commentedPer #2942049: Prevents normal website access by restricting all routes by default, I have verified that the suggested changes allow normal website access, and batches work.
Comment #18
solideogloria CreditAttribution: solideogloria commentedI also verified that I cannot access an endpoint as a blacklisted IP, nor as an IP not listed in an active whitelist.
Comment #19
solideogloria CreditAttribution: solideogloria commentedUpdate target branch.
Comment #20
tguerineau CreditAttribution: tguerineau at weKnow Inc commentedI have conducted further testing of the
ip_consumer_auth
module with the applied patch in a Drupal 9 environment. Here are my updated findings:172.22.0.1
was whitelisted, anonymous users received a200 OK
status with no output, which is expected behavior indicating access is allowed.111.2.3.1
in the whitelist resulted in the error{"message":"The used authentication method is not allowed on this route."}
for both anonymous and authenticated users. This was unexpected, as the response suggests an authentication method issue rather than an IP restriction response.Behavior without the Module:
ip_consumer_auth
module was disabled, accessing the endpoint did not yield any authentication errors, suggesting that the REST endpoint itself is configured correctly to allow access without additional authentication methods.Conclusion and Queries:
ip_consumer_auth
module works correctly for anonymous users, there are issues when IPs are blacklisted or not included in an active whitelist. Specifically, the consistent authentication method error is perplexing, given that it does not occur when the module is disabled.ip_consumer_auth
module that might be causing this behavior, or could this indicate a deeper issue with the patch or the module's interaction with Drupal's authentication system?Comment #21
solideogloria CreditAttribution: solideogloria commented@tguerineau: Yeah, I think my assumption of how the
AuthenticationProviderInterface::applies
function works was wrong. I realized that anonymous users would be unable to log in with my patch applied.However, it wasn't correct without a patch, either, or with any of the other patches suggested so far.
The main issue with how it worked before, with the vanilla module, was that the applies function returned FALSE when it shouldn't, and whenever it did return FALSE, the IP authentication was completely ignored.
Are you able to reproduce any issues with the vanilla module and the patch from #2942049: Prevents normal website access by restricting all routes by default applied, or just with the singular change below? The module doesn't work at all unless the services file has
... so you'll have to patch that much, at least.
Comment #22
solideogloria CreditAttribution: solideogloria commentedBasically, I can't find a configuration and patch combination that gets the module to work in all cases...
Comment #23
solideogloria CreditAttribution: solideogloria commentedDo you have any experience writing tests? Maybe we should write tests first, to make sure we know what should happen.
Comment #24
tguerineau CreditAttribution: tguerineau at weKnow Inc commentedHi @solideogloria,
Yes, I have some experience writing tests.
I can work on adding the tests to cover all possible cases, to ensure that the module functions as it should.
Comment #25
solideogloria CreditAttribution: solideogloria commentedThanks. Should we open a separate branch or issue for that?
Comment #26
solideogloria CreditAttribution: solideogloria commentedI created a separate issue.
#3408601: Write tests for use-cases