http: decode username and password before encoding#31450
http: decode username and password before encoding#31450addaleax wants to merge 4 commits intonodejs:mainfrom
Conversation
|
I think this PR should also change the docs: https://nodejs.org/api/url.html#url_url_username. Technically this can be seen as a patch or semver-major change, and I'm uncertain between the two. |
Done, PTAL |
|
semver-patch in my opinion. |
| - version: REPLACEME | ||
| pr-url: https://github.com/nodejs/node/pull/31450 | ||
| description: When used with `http.request()`, this field will now be | ||
| percent-decoded. |
There was a problem hiding this comment.
The section on http.request() in http.md seems like a more logical place to me for this note.
| - version: REPLACEME | ||
| pr-url: https://github.com/nodejs/node/pull/31450 | ||
| description: When used with `http.request()`, this field will now be | ||
| percent-decoded. |
There was a problem hiding this comment.
The section on http.request() in http.md seems like a more logical place to me for this note.
lib/internal/url.js
Outdated
| if (url.username || url.password) { | ||
| options.auth = `${url.username}:${url.password}`; | ||
| options.auth = | ||
| `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`; |
There was a problem hiding this comment.
This introduces a spec violation. From RFC 7617, section 2:
Furthermore, a user-id containing a colon character is invalid, as
the first colon in a user-pass string separates user-id and password
from one another; text after the first colon is part of the password.
User-ids containing colons cannot be encoded in user-pass strings.
With this change, I can now write something like:
https.get('https://not%3Agood:god@example.com')And the server will interpret that as user="not" and pass="good:god" instead of pass="god" (which is the password we still all use for our root accounts, right? Right!)
There was a problem hiding this comment.
User-ids containing colons cannot be encoded in user-pass strings.
But what would be proper behavior in case of passing : in username? Encoding username - the wrong way for the computation Authorization header. Pre-validation with throwing an exception? Maybe...
There was a problem hiding this comment.
I don’t really want to introduce new exceptions here … maybe just leave it encoded?
The same question also pops up for e.g. %2F for / in the password.
There was a problem hiding this comment.
Yes. I think : and / are the only problematic ones; for @ it's "last at-sign wins."
It's probably okay to leave those encoded, that's no worse from how it was before.
|
I'm generally unconvinced we should do this; and if we do make any changes here, it should be done in a manner that is consistent with the WHATWG URL parser. For instance, given the input: URL {
href: 'https://not%3Agood:g%3aod@example.com/',
origin: 'https://example.com',
protocol: 'https:',
username: 'not%3Agood',
password: 'g%3aod',
host: 'example.com',
hostname: 'example.com',
port: '',
pathname: '/',
search: '',
searchParams: URLSearchParams {},
hash: ''
}(note that |
@jasnell I’m not 100 % sure what “consistent with the WHATWG URL parser” means here – the parser itself is not changed in any way. If you do pass in special characters as part of the original URL that are not > url = new URL('https://not"good:g^od@example.com')
URL {
href: 'https://not%22good:g%5Eod@example.com/',
origin: 'https://example.com',
protocol: 'https:',
username: 'not%22good',
password: 'g%5Eod',
host: 'example.com',
hostname: 'example.com',
port: '',
pathname: '/',
search: '',
searchParams: URLSearchParams {},
hash: ''
}
> urlToOptions(url)
{
protocol: 'https:',
hostname: 'example.com',
hash: '',
search: '',
pathname: '/',
path: '/',
href: 'https://not%22good:g%5Eod@example.com/',
auth: 'not"good:g^od'
} |
|
(needs a rebase) |
|
@addaleax, what I mean is that any decoding/encoding that happens with the username and password here should be identical to what is produced when parsing/serializing with the WHATWG URL parser. This is close, the URL standard does specify some specifics for username and password that are not covered by decodeURIComponent and encodeURIComponent. |
|
Any updates on the MR? |
| } | ||
|
|
||
| function decodeAuth(str) { | ||
| return decodeURIComponent(str).replace(':', '%3A').replace('/', '%2F'); |
There was a problem hiding this comment.
i think you need to use global replace at least ;)
There was a problem hiding this comment.
@addaleax This is still unaddressed (and, apparently, untested.)
| } | ||
|
|
||
| function decodeAuth(str) { | ||
| return decodeURIComponent(str).replace(':', '%3A').replace('/', '%2F'); |
There was a problem hiding this comment.
it would be unexpected outcome with undefined url.password or url.username
There was a problem hiding this comment.
Is it possible for url.username to be undefined? Isn't it an empty string instead?
|
@addaleax ... I took another look at this and I want to remove any objection I have on this. The change looks good |
8ae28ff to
2935f72
Compare
|
@addaleax: Is this still blocked? |
|
@ronag No, I don’t think so, although it’s not ready to land either (there’s a few valid comments above and this needs a rebase) |
| } | ||
|
|
||
| function decodeAuth(str) { | ||
| return decodeURIComponent(str).replace(':', '%3A').replace('/', '%2F'); |
There was a problem hiding this comment.
const forwardSlashRegEx = /\//g;
+const colonRegEx = /:/g;| return decodeURIComponent(str).replace(':', '%3A').replace('/', '%2F'); | |
| return decodeURIComponent(str).replace(colonRegEx, '%3A').replace(forwardSlashRegEx, '%2F'); |
There was a problem hiding this comment.
This is the only thing todo here I think...
|
Friendly ping :) |
|
Conflicts |
|
I'm removing the blocked label as I suspect it might make collaborators less likely to engage.
|
ronag
left a comment
There was a problem hiding this comment.
Adding a request changes to avoid landing while there are still things to address.
|
@addaleax Would you be able to address the remaining feedback? 🙏 |
|
@addaleax friendly reminder |
|
Friendly ping :) |
|
Do you think we can close this old PR, as the underlying issue was closed by you few years ago? |

@nodejs/url @nodejs/security I’m marking this as blocked, because I’d like somebody to take a look at this who is somewhat familiar with the security implications of this – there are no obvious downsides to me here, but I know this can be quite a sensitive area.
Fixes: #31439
/cc @izonder
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes