Estou postando uma nova resposta porque acho que a resposta do zneak não tem exemplos suficientes, não mostra o manuseio de HTML e URI como aspectos e padrões diferentes e tem algumas pequenas coisas ausentes.
Você tem dois padrões sobre URLs nos links ( <a href).
O primeiro padrão é o RFC 1866 (HTML 2.0), onde em "3.2.1. Caracteres de Dados" você pode ler os caracteres que precisam ser escapados quando usados como valor para um atributo HTML. (Os atributos em si não permitem caracteres especiais, por exemplo, <a hr&ef="http://...não é permitido nem é <a hr&ef="http://....)
Posteriormente, isso passou para o padrão HTML 4 , os caracteres dos quais você precisa escapar são:
< to <
> to >
& to &
" to "e;
' to '
O outro padrão é o RFC 3986 "Padrão de URI genérico", em que os URLs são manipulados (isso acontece quando o navegador está prestes a seguir um link porque o usuário clicou no elemento HTML).
reserved = gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
É importante escapar desses caracteres para que o cliente saiba se eles representam dados ou um delimitador.
Exemplo sem escape:
https://example.com/?user=test&password&te&st&goto=https://google.com
Exemplo, URL totalmente legítimo
https://example.com/?user=test&password&te%26st&goto=https%3A%2F%2Fgoogle.com
Exemplo de URL totalmente legítimo no valor do atributo HTML:
https://example.com/?user=test&password&te%26st&goto=https%3A%2F%2Fgoogle.com
Também cenários importantes:
Javascript como um valor:
<img src="..." onclick="window.location.href = "https://example.com/?user=test&password&te%26st&goto=https%3A%2F%2Fgoogle.com";">...</a>(Sim, ;;está correto.)
JSON como um valor:
<a href="..." data-analytics="{"event": "click"}">...</a>
Escapou coisas dentro de coisas escapadas, codificação dupla, URL dentro de URL dentro de parâmetro etc, ...
http://x.com/?passwordUrl=http%3A%2F%2Fy.com%2F%3Fuser%3Dtest&password=""123