Kong / kong

🦍 The Cloud-Native API Gateway and AI Gateway.
https://konghq.com/install/#kong-community
Apache License 2.0
38.78k stars 4.77k forks source link

Key authentication plugin is not protecting the APIs from Apache-HttpClient 4 #408

Closed Hughiegao closed 9 years ago

Hughiegao commented 9 years ago

I set up a kong server with keyauth following the http://getkong.org/plugins/key-authentication

then I use Jmeter to make a Apache-HttpClient 4.* HTTP request to call the Kong APIs with a wrong apikey or not apikey. And it pass the keyauth check like there is no protection.

Protected User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36 Apache-HttpClient 3.*

Not Protected User-Agent: Apache-HttpClient 4.*

thibaultcha commented 9 years ago

Hi @Hughiegao,

This is what I just ran on my local Kong.

Adding an API:

$ http :8001/apis name=mockbin public_dns=mockbin.com target_url=http://mockbin.com
HTTP/1.1 201 Created
Date: Fri, 17 Jul 2015 22:12:52 GMT
Server: kong/0.4.0

{
    "created_at": 1437171172000,
    "id": "1905e78b-2e92-4160-c143-571915f0cac9",
    "name": "mockbin",
    "public_dns": "mockbin.com",
    "target_url": "http://mockbin.com/"
}

Adding a plugin on top of it (keyauth):

$ http :8001/apis/mockbin/plugins name=keyauth
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Date: Fri, 17 Jul 2015 22:13:13 GMT
Server: kong/0.4.0

{
    "api_id": "1905e78b-2e92-4160-c143-571915f0cac9",
    "created_at": 1437171193000,
    "enabled": true,
    "id": "26aff2ea-0c21-48a2-c8f3-52ca949b4b29",
    "name": "keyauth",
    "value": {
        "hide_credentials": false,
        "key_names": [
            "apikey"
        ]
    }
}

Making a request through Kong:

$ http :8000 Host:mockbin.com
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Date: Fri, 17 Jul 2015 22:13:42 GMT
Server: kong/0.4.0

{
    "message": "No API key found in headers or querystring"
}

And with a wrong credential:

http ':8000/?apikey=foo' Host:mockbin.com
HTTP/1.1 403 Forbidden
Content-Type: application/json; charset=utf-8
Date: Fri, 17 Jul 2015 22:17:15 GMT
Server: kong/0.4.0

{
    "message": "Invalid authentication credentials"
}

As you can see the request doesn't go through. Could you share your setup and/or compare it with the one I just posted so we can know what's happening, fix it or eventually clear the confusion if there was any?

Thanks

Note: if you run this on Kong 0.3.2, the request without credentials will respond 403 instead of 401.

Hughiegao commented 9 years ago

It is running at a CentOS 6 VM

Adding an API:

curl –X POST: http://localhost:8001/apis/ --data "name=mockbin" --data "path=/mockbin/" --data "strip_path=false" --data "target_url=http://mockbin.com"

curl: (6) Couldn't resolve host '–X'
curl: (6) Couldn't resolve host 'POST:'
{"path":"\/mockbin\/","target_url":"http:\/\/mockbin.com\/","id":"e1bd7009-6da7-4886-c8cf-ed7452f3bf66","created_at":1437486095000,"name":"mockbin","strip_path":false}

Adding a plugin on top of it (keyauth):

curl -X POST http://localhost:8001/apis/mockbin/plugins  --data "name=keyauth"  --data "value.key_names=apikey"

{"api_id":"e1bd7009-6da7-4886-c8cf-ed7452f3bf66","value":{"key_names":["apikey"],"hide_credentials":false},"id":"79f323a3-d9af-4468-c467-f524dabc51bb","enabled":true,"created_at":1437486116000,"name":"keyauth"}

Making a request through Kong:

curl localhost:8000/mockbin/123
{"message":"Invalid authentication credentials"}

Making a request through Kong with right key: I believe "Cannot GET /mockbin/123 is the right message"

curl localhost:8000/mockbin/123 -H "apikey:123"
Cannot GET /mockbin/123

Making a request through Kong with header of "Expect:100-continue" HERE IS THE ISSUES, I can reproduce with curl

curl localhost:8000/mockbin/123 -H "Expect:100-continue"
Cannot GET /mockbin/123

I believe the "Expect:100-continue" is the problem, what it did is allow a client to send a request message with a request body to determine if the origin server is willing to accept the request (based on the request headers) before the client sends the request body. Maybe that is how it pass the Kong keyauth check? more doc with http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html 8.2.3 Use of the 100 (Continue) Status.

Thanks you to look it up for me.

subnetmarco commented 9 years ago

I do confirm that this has been implemented as a feature (https://github.com/Mashape/kong/blob/master/kong/plugins/keyauth/access.lua#L14-L17) because it was breaking a specific use case, which it should be client-side HTML forms for file uploads.

Let me investigate.

Hughiegao commented 9 years ago

skip_authentication? It looks like security breach for me. :)

Thank you for the investigation.

subnetmarco commented 9 years ago

Okay, I remember now why this check has been implemented.

The Expect: 100-continue request is sent when the client is trying to validate the prerequisites of the request. This is primarily used when making POST requests with multipart/form-data bodies, to validate that the size of the payload can be accepted by the final API.

Because of the nature of 100-continue, the body is not being sent (otherwise it would defeat it's purpose). The Key Authentication plugin accepts the authentication key to be in the header, in the querystring or in the body. In the latter case when credentials are being sent in the body, because 100-continue doesn't send any body, the request will always fail because the authentication parameter will never be submitted.

So Kong skips the authentication for Expect: 100-continue requests, but it will properly enforce it on the subsequent request is the server doesn't deny the prerequisites.

Hughiegao commented 9 years ago

The problem is with this "Expect: 100-continue", the KONG don't seem to deny the subsequent request. I can't show the target_url that I have. But, It doesn't need the apikey when you do "Expect: 100-continue" request. Maybe "POST requests with multipart/form-data" bodies is working, BUT post requests with application/json and Expect: 100-continue skipping authentication on both Expect: 100-continue requests and subsequent request! Can you run a test with it? Thanks

Hughiegao commented 9 years ago

To prove what I mean, I create a mockbin for it.

http://mockbin.com/bin/14d814d0-ff59-4072-bbd3-c4f43720423a All it did is return a message of "You pass the Key Authentication check!"

It is running at a CentOS 6 VM

Adding an API:

curl –X POST: http://localhost:8001/apis/ --data "name=mockbin" --data "path=/mockbin/" --data "strip_path=false" --data "target_url=http://mockbin.com/bin/14d814d0-ff59-4072-bbd3-c4f43720423a"

{"path":"\/mockbin\/","target_url":"http:\/\/mockbin.com\/","id":"e1bd7009-6da7-4886-c8cf-ed7452f3bf66","created_at":1437486095000,"name":"mockbin","strip_path":false}

Adding a plugin on top of it (keyauth):

curl -X POST http://localhost:8001/apis/mockbin/plugins  --data "name=keyauth"  --data "value.key_names=apikey"

{"api_id":"e1bd7009-6da7-4886-c8cf-ed7452f3bf66","value":{"key_names":["apikey"],"hide_credentials":false},"id":"79f323a3-d9af-4468-c467-f524dabc51bb","enabled":true,"created_at":1437486116000,"name":"keyauth"}

Adding a consumer

curl -X POST http://localhost:8001/consumers --data "username=hughiegao"

{"username":"hughiegao","created_at":1439845929000,"id":"bebe228d-f536-4251-c215-356cb7af522b"}

Adding a key=123 to this consumer

curl -X POST http://localhost:8001/consumers/bebe228d-f536-4251-c215-356cb7af522b/keyauth --data "key=123"

{"created_at":1439846176000,"consumer_id":"bebe228d-f536-4251-c215-356cb7af522b","key":"123","id":"c773224f-fc2d-4985-c929-7c21b7ce9c02"}

Making a request through Kong:

curl localhost:8000/mockbin/123
{"message":"No API key found in headers or querystring"}

Making a request through Kong with right key:

curl localhost:8000/mockbin/123 -H "apikey:123"
You pass the Key Authentication check!

Making a request through Kong with header of "Expect:100-continue" HERE IS THE ISSUES, I can reproduce with curl

I DON'T NEED A API KEY FOR IT!

curl localhost:8000/mockbin/123 -H "Expect:100-continue"
You pass the Key Authentication check!

The problem is if I have a "Expect:100-continue" inside my header, it will always skip the key authentication.

subnetmarco commented 9 years ago

@Hughiegao I decided to go ahead and remove that exception, so that the authentication parameter is always required. This will be effective starting from the next version.

tyiss commented 9 years ago

@thefosk Isn't this should be applied for basicauth plugin aswell ? ( i can prepare a pr for it).

Hughiegao commented 9 years ago

@thefosk Thank you for the update.

subnetmarco commented 9 years ago

This has been fixed on both key auth and basic auth.