atomx / nginx-http-auth-digest

Digest Authentication for Nginx
Other
44 stars 14 forks source link

RFC 2617 obsoleted #14

Closed RicardoSette closed 6 years ago

RicardoSette commented 6 years ago

The RFC2617(1999) used in the module is obsolete, generating incompatibilities with plugins that follow the new RFC7616(2015).

For example, the Android plugin https://github.com/rburgst/okhttp-digest follows the rules of the new RFC7616.

The error occurs when the "algorithm" attribute for this NGINX module waits for it to be quoted, but according to the new RFC the algorithm/qop/nc attributes can not contain quotation marks in the passed value.

RFC7616 Example:

Authorization: Digest username="Mufasa", realm="http-auth@example.org", uri="/dir/index.html", algorithm=MD5, nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", nc=00000001, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", qop=auth, response="8ca523f5e9506fed4657c9700eebdbec", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS"

erikdubbelboer commented 6 years ago

This module already supports RFC7616, it's just not documented.

Firefox for example sends the following which works perfectly fine:

Authorization: Digest 
  username="test",
  realm="example.com",
  nonce="4fe1ba135a366f0a",
  uri="/favicon.ico",
  algorithm=MD5,
  response="2awcad58xcd0x62a915a64ab6f2dw01c",
  qop=auth,
  nc=00000006,
  cnonce="723add2b39e8cxa1"

As you can see it the parser it looks for either quoted or none-quoted values: https://github.com/atomx/nginx-http-auth-digest/blob/519dc2a4907bc6d9c48f95b3cf6a0151aaf44b40/ngx_http_auth_digest_module.c#L445-L458

Do you have an example where it doesn't work?

RicardoSette commented 6 years ago

I will not be able to post actual data when the fault has occurred, so I am changing some values, but I assure you that the only change was in the quotation marks.

The examples below are obtained with logs in NGINX:

In the example below, you see that the algorithm attribute is without quotation marks, so it always fails to return 401:

"Digest username=\x22abcabcabcabc\x22, realm=\x22xyzxyzxyzxyzxyz\x22, nonce=\xcccccccccccccc\x22, uri=\x22/Sites/TESTE.API/api/App/GetConfig\x22, response=\xaaaaaaaaaaaaa\x22, qop=auth, nc=00000001, cnonce=\xbbbbbbbbbbbbbbbbb\x22, algorithm=MD5"

In this example, the same works because MD5 is surrounded by quotation marks returning 200:

"Digest username=\x22abcabcabcabc\x22, realm=\x22xyzxyzxyzxyzxyz\x22, nonce=\xcccccccccccccc\x22, uri=\x22/Sites/TESTE.API/api/App/GetConfig\x22, response=\xaaaaaaaaaaaaa\x22, qop=auth, nc=00000001, cnonce=\xbbbbbbbbbbbbbbbbb\x22, algorithm=\x22MD5\x22"

Note that the only difference in the NGINX logs was the quotation marks in MD5, and when I changed the plugin https://github.com/rburgst/okhttp-digest to always put quotation marks the NGINX worked correctly for Digest.

erikdubbelboer commented 6 years ago

I'm pretty sure something else is going wrong.

I just modified https://github.com/bnjjj/node-request-digest to send with and without quotes and both work fine (see debug output below). Like I already showed Firefox also sends without quotes which works fine.

I also showed you the code which works with both quotes and unquoted values.

I'm unable to reproduce this in any way so it must either be something weird in your test environment or something else is going wrong.

Are you sure you are using the correct password and realm for example?

REQUEST { url: 'https://admin.example.com:443/',
  method: 'GET',
  port: 443,
  path: '/',
  host: 'https://admin.example.com',
  callback: [Function] }
REQUEST response end https://admin.example.com:443/ 401 { server: 'nginx',
  date: 'Mon, 18 Dec 2017 05:42:06 GMT',
  'content-type': 'text/html',
  'content-length': '188',
  connection: 'close',
  'www-authenticate': 'Digest algorithm="MD5", qop="auth", realm="example.com", nonce="aaaaaaaaaaa"' }
REQUEST { headers: { Authorization: 'Digest username="example",realm="example.com",nonce="aaaaaaaaaaa",uri="/",qop=auth,algorithm="MD5",response="bbbbbbbbbbbbb",nc=00000001,cnonce="cccccccc"' },
  url: 'https://admin.example.com:443/',
  method: 'GET',
  port: 443,
  path: '/',
  host: 'https://admin.example.com',
  callback: [Function] }
REQUEST response end https://admin.example.com:443/ 200 { server: 'nginx',
  date: 'Mon, 18 Dec 2017 05:42:06 GMT',
  'content-type': 'text/html',
  'content-length': '321',
  'last-modified': 'Tue, 22 Nov 2016 04:19:54 GMT',
  connection: 'close',
  'authentication-info': 'qop="auth", rspauth="ddddddddddddddd", cnonce="cccccccc", nc=00000001',
  'accept-ranges': 'bytes' }

REQUEST { url: 'https://admin.example.com:443/',
  method: 'GET',
  port: 443,
  path: '/',
  host: 'https://admin.example.com',
  callback: [Function] }
REQUEST response end https://admin.example.com:443/ 401 { server: 'nginx',
  date: 'Mon, 18 Dec 2017 05:46:01 GMT',
  'content-type': 'text/html',
  'content-length': '188',
  connection: 'close',
  'www-authenticate': 'Digest algorithm="MD5", qop="auth", realm="example.com", nonce="eeeeeeeeeeeee"' }
REQUEST { headers: { Authorization: 'Digest username="example",realm="example.com",nonce="eeeeeeeeeeeee",uri="/",qop=auth,algorithm=MD5,response="fffffffffffffff",nc=00000001,cnonce="gggggggggg"' },
  url: 'https://admin.example.com:443/',
  method: 'GET',
  port: 443,
  path: '/',
  host: 'https://admin.example.com',
  callback: [Function] }
REQUEST response end https://admin.example.com:443/ 200 { server: 'nginx',
  date: 'Mon, 18 Dec 2017 05:46:01 GMT',
  'content-type': 'text/html',
  'content-length': '321',
  'last-modified': 'Tue, 22 Nov 2016 04:19:54 GMT',
  connection: 'close',
  'authentication-info': 'qop="auth", rspauth="hhhhhhhhhhhhhhh", cnonce="gggggggggg", nc=00000001',
  'accept-ranges': 'bytes' }
RicardoSette commented 6 years ago

I found the bug, just the last attribute of "Authorization" is without quotation marks that the plugin always returns 401.

See functional example:

REQUEST: GET /test.php HTTP/1.1 Host: 127.0.0.1 Authorization: Digest username="test", type="digest", algorithm="MD5", realm="test nginx-http-auth-digest", nonce="175380975a37bead", uri="/test.php", cnonce="f3765808c2e4d5f00e786d275318c0c9", nc="1", response="c4dde2a62eee9129be2fb8a9eb1a6cfa", qop="auth" Content-type: application/json

RESPONSE: HTTP/1.1 200 OK Date: Mon, 18 Dec 2017 13:12:13 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: close Authentication-Info: qop="auth", rspauth="7d9cb9279475535f1b96b981d78ee2cc", cnonce="f3765808c2e4d5f00e786d275318c0c9", nc=1 Strict-Transport-Security: max-age=15768000

See Bug example:

REQUEST: GET /test.php HTTP/1.1 Host: 127.0.0.1 Authorization: Digest username="test", type="digest", algorithm="MD5", realm="test nginx-http-auth-digest", nonce="4f81cd585a37bf36", uri="/test.php", cnonce="e2fe622091593b85a866987daae37d63", nc="1", response="57790a9ecff2bd82a45bd15df5cdfedf", qop=auth Content-type: application/json

RESPONSE: HTTP/1.1 401 Unauthorized Date: Mon, 18 Dec 2017 13:14:30 GMT Content-Type: text/html; charset=utf-8 Content-Length: 190 Connection: close WWW-Authenticate: Digest algorithm="MD5", qop="auth", realm="test nginx-http-auth-digest", nonce="768d08da5a37bf36"

The Bug happens with the 'qop' attribute, because the Bug's characteristic happens when the last attribute of the 'Authorization' Header is without quotation marks.

Make a test where the last attribute of the 'Authorization' Header is 'algorithm|nc|qoq' but without the quotation marks and you can see the request being denied with 401.

erikdubbelboer commented 6 years ago

Good find 👍 It's fixed in https://github.com/atomx/nginx-http-auth-digest/commit/53140c54a5697aa9f2a396e86d36975ad1c380af