414 - 请求的 uri 太长 - url 的长度为 70 个字符
414 - The requested uri is too long - the url is 70 characters long
我尝试使用 C# 进行身份验证。
它在尝试登录时发送一个 HTTP GET
请求,看起来像:
https://example.com/clogin.php?name=abc&password=abc
而且这只是几个字符。但是我收到“请求 URI 太长”错误。
模拟请求似乎有效,但在通过 TcpClient 发送时无效。
ClientManager.cs:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
public class ClientManager
{
private static TcpListener listener;
public static void Main()
{
ServicePointManager.ServerCertificateValidationCallback = RemoteCertificateValidationCallback;
listener = new TcpListener(IPAddress.Any, 10250);
listener.Start();
Console.WriteLine("*** ClientManager started ***");
Console.WriteLine("Listening to port 10250, make sure not used.");
StartAccept();
while (true)
{
System.Threading.Thread.Sleep(1000);
string cmd = Console.ReadLine();
if (cmd.Equals("exit"))
{
Console.WriteLine("*** Stopping cman... ***");
Environment.Exit(0);
}
if (cmd.StartsWith("auth"))
{
Console.WriteLine("[" + "simulate" + "] Using auth cred to authenticate.");
char c = '|';
string[] args = cmd.Split(c);
if (!(args.Length > 2))
{
Console.WriteLine("INVALID_DATA_GIVEN");
}
string username = args[1];
string password = args[2];
Console.WriteLine("[" + "simulate" + "] Using credentials: " + username + " and " + password);
string response = GetAsync(("https://example.com/clogin.php?name=" + username + "&password=" + password + "&test=1"));
Console.WriteLine("https://example.com/clogin.php?name=" + username + "&password=" + password);
Console.WriteLine(response);
if (response.Equals("ERROR_FAILED_CONNECTION"))
{
Console.WriteLine("ERROR_SERVERERROR");
}
else if (response.Equals("INVALID_USERNAME"))
{
Console.WriteLine("INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_PASSWORD"))
{
Console.WriteLine("INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_CRED"))
{
Console.WriteLine("ERROR_AUTH_INVALID_CRED");
}
else if (response.Equals("IS_BANNED"))
{
Console.WriteLine("ERROR_AUTH_BANNED");
}
else
{
Console.WriteLine("[" + "simulate" + "] LOGIN OK | RESPONSE: " + response);
}
}
}
}
private static void StartAccept()
{
listener.BeginAcceptSocket(HandleAsyncConnection, listener);
}
private static void HandleAsyncConnection(IAsyncResult res)
{
StartAccept();
TcpClient client = listener.EndAcceptTcpClient(res);
string clientSession = "NULL";
string ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
Console.WriteLine("[" + ip + "] Incoming connection.");
while (true)
{
try
{
System.Threading.Thread.Sleep(250);
Console.WriteLine("Trying to read data from " + ip);
NetworkStream stream = null;
Byte[] data = new Byte[8192];
String responseData = String.Empty;
Int32 bytes = 0;
stream = client.GetStream();
bytes = stream.Read(data, 0, data.Length);
Console.WriteLine("Bytes: " + bytes + " Data: " + System.Text.Encoding.ASCII.GetString(data));
responseData = System.Text.Encoding.ASCII.GetString(data);
Console.WriteLine("[" + ip + "] " + responseData);
if (responseData.StartsWith("close"))
{
Console.WriteLine("[" + ip + "] Connection closed.");
break;
}else if (responseData.StartsWith("useauthtoken"))
{
Console.WriteLine("[" + ip + "] Using auth token to authenticate.");
char c = '|';
string[] args = responseData.Split(c);
if (!(args.Length > 1))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
string response = GetAsync("http://example.com/cauthtokencheck.php?auth=" + args[1]);
if (response.Equals("yes"))
{
clientSession = args[1];
SendMessage(stream, "OK");
}
else
{
SendMessage(stream, "ERROR_AUTH_INVALID");
}
}
else if (responseData.StartsWith("auth"))
{
Console.WriteLine("[" + ip + "] Using auth cred to authenticate.");
char c = '|';
string[] args = responseData.Split(c);
Console.WriteLine("Data splitted");
if (!(args.Length > 2))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
string username = args[1];
string password = args[2];
Console.WriteLine("[" + ip + "] Using credentials: " + username + " and " + password);
Console.WriteLine("Logging in...");
string response = GetAsync(("https://example.com/clogin.php?name=" + username + "&password=" + password + ""));
Console.WriteLine("Login attempt completed, with " + response);
if (response.Equals("ERROR_FAILED_CONNECTION"))
{
SendMessage(stream, "ERROR_SERVERERROR");
}
else if (response.Equals("INVALID_USERNAME"))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_PASSWORD"))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_CRED"))
{
SendMessage(stream, "ERROR_AUTH_INVALID_CRED");
}
else if (response.Equals("IS_BANNED"))
{
SendMessage(stream, "ERROR_AUTH_BANNED");
}
else
{
Console.WriteLine("[" + ip + "] LOGIN OK | RESPONSE: " + response);
if (response == null) response = "Response was null?";
SendMessage(stream, response);
Console.WriteLine("Sended message...");
clientSession = response;
}
}
if ((!responseData.StartsWith("auth") || !responseData.StartsWith("useauthtoken") || !responseData.StartsWith("close")) && clientSession.Equals("NULL"))
{
SendMessage(stream, "ERROR_AUTH_MISSING");
}
if (responseData.Equals("endGame"))
{
char c = '|';
string[] args = responseData.Split(c);
if (!(args.Length > 3))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
string won = args[0];
string kills = args[1];
string singleplayer = args[2];
string response = GetAsync("https://example.com/cgameend.php?session=" + client + "&won=" + won + "&kills=" + kills + "&singleplayer=" + singleplayer);
if (response.Equals("ERROR_FAILED_CONNECTION"))
{
SendMessage(stream, "ERROR_SERVERERROR");
}
else if (response.Equals("SESSION_INVALID"))
{
SendMessage(stream, "ERROR_AUTH_MISSING");
}
else if (response.Equals("SUCCESS"))
{
SendMessage(stream, "SUCCESS");
}
}
}
catch (Exception e)
{
Console.WriteLine("[" + ip + "] Connection closed: " + e.Message);
break;
}
}
}
public static string GetAsync(string uri, Action<WebHeaderCollection> headers = null)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
//request.Headers.Set(HttpRequestHeader.ContentLocation, uri);
//request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
Console.WriteLine(request.RequestUri);
using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
public static void SendMessage(NetworkStream stream, string msg)
{
Byte[] sendBytes = Encoding.ASCII.GetBytes(msg);
stream.Write(sendBytes, 0, sendBytes.Length);
}
public static bool RemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
bool isOk = true;
return isOk;
}
}
clogin.php:
<?php
$conn = new mysqli("localhost", "root", "not-the-real-password", "topdown");
if ($conn->connect_error) {
die("ERROR_FAILED_CONNECTION");
}
$name = $_GET["name"];
$password = $_GET["password"];
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
function endsWith($haystack, $needle)
{
$length = strlen($needle);
if ($length == 0) {
return true;
}
return (substr($haystack, -$length) === $needle);
}
$sql = "SELECT `password`,`banned` FROM `accounts` WHERE `name`='".$name."';";
$result = $conn->query($sql);
if ($result->num_rows < 1) {
die("INVALID_CRED");
die();
}
$found = "";
$banned = false;
while($row = $result->fetch_assoc()) {
$found = $row["password"];
$banned = $row["banned"];
}
if($found == $password){
if($banned==1){
die("IS_BANNED");
}
$session = generateRandomString(16);
$sql = "UPDATE `accounts` SET `clientsession`='".$session."' WHERE `name`='".$name."'";
if ($conn->query($sql) === TRUE) {
die($session);
}else{
die("ERROR_FAILED_CONNECTION");
}
}else{
die("INVALID_CRED");
}
?>
该消息来自服务器,而不是客户端。请参阅此答案以获取可能的解决方案:
您的模拟没有反映您在非模拟情况下发送到服务器的内容,这就是您无法在模拟中重现错误的原因。
在模拟的情况下你读了一行,字符串的长度cmd
就是行的长度。在非模拟情况下,您改为读入大小为 8192 字节的缓冲区 data
。这意味着,如果您读取 auth|abc|abc
的响应, data
的内容将是 auth|abc|abc[=14=][=14=][=14=][=14=]....
,即服务器发送的内容,然后是 8180(8192-12)个字符 [=15= ]
(即 \x00
、[=17=]0
或者可以用 C# 编写)。
在您 responseData.Split
之后,密码(即 args[2]
)将不会像您预期的那样是 abc
,而是 abc[=21=][=21=][=21=][=21=]....
。这再次意味着应该是 https://....?user=abc&pass=abc
的 URL 实际上是 https://....?user=abc&pass=abc[=23=][=23=][=23=]....
。 [=15=]
需要用 URL 编码为 %00
进行编码,这会导致 https://....?user=abc&pass=abc%00%00%00....
并且已经所有这些 8180 %00
将在 URL 中产生 24540 个字符,这解释了为什么服务器抱怨 URL 太大。查看服务器访问日志或错误日志也可能会显示此类问题。
我尝试使用 C# 进行身份验证。
它在尝试登录时发送一个 HTTP GET
请求,看起来像:
https://example.com/clogin.php?name=abc&password=abc
而且这只是几个字符。但是我收到“请求 URI 太长”错误。
模拟请求似乎有效,但在通过 TcpClient 发送时无效。
ClientManager.cs:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
public class ClientManager
{
private static TcpListener listener;
public static void Main()
{
ServicePointManager.ServerCertificateValidationCallback = RemoteCertificateValidationCallback;
listener = new TcpListener(IPAddress.Any, 10250);
listener.Start();
Console.WriteLine("*** ClientManager started ***");
Console.WriteLine("Listening to port 10250, make sure not used.");
StartAccept();
while (true)
{
System.Threading.Thread.Sleep(1000);
string cmd = Console.ReadLine();
if (cmd.Equals("exit"))
{
Console.WriteLine("*** Stopping cman... ***");
Environment.Exit(0);
}
if (cmd.StartsWith("auth"))
{
Console.WriteLine("[" + "simulate" + "] Using auth cred to authenticate.");
char c = '|';
string[] args = cmd.Split(c);
if (!(args.Length > 2))
{
Console.WriteLine("INVALID_DATA_GIVEN");
}
string username = args[1];
string password = args[2];
Console.WriteLine("[" + "simulate" + "] Using credentials: " + username + " and " + password);
string response = GetAsync(("https://example.com/clogin.php?name=" + username + "&password=" + password + "&test=1"));
Console.WriteLine("https://example.com/clogin.php?name=" + username + "&password=" + password);
Console.WriteLine(response);
if (response.Equals("ERROR_FAILED_CONNECTION"))
{
Console.WriteLine("ERROR_SERVERERROR");
}
else if (response.Equals("INVALID_USERNAME"))
{
Console.WriteLine("INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_PASSWORD"))
{
Console.WriteLine("INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_CRED"))
{
Console.WriteLine("ERROR_AUTH_INVALID_CRED");
}
else if (response.Equals("IS_BANNED"))
{
Console.WriteLine("ERROR_AUTH_BANNED");
}
else
{
Console.WriteLine("[" + "simulate" + "] LOGIN OK | RESPONSE: " + response);
}
}
}
}
private static void StartAccept()
{
listener.BeginAcceptSocket(HandleAsyncConnection, listener);
}
private static void HandleAsyncConnection(IAsyncResult res)
{
StartAccept();
TcpClient client = listener.EndAcceptTcpClient(res);
string clientSession = "NULL";
string ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
Console.WriteLine("[" + ip + "] Incoming connection.");
while (true)
{
try
{
System.Threading.Thread.Sleep(250);
Console.WriteLine("Trying to read data from " + ip);
NetworkStream stream = null;
Byte[] data = new Byte[8192];
String responseData = String.Empty;
Int32 bytes = 0;
stream = client.GetStream();
bytes = stream.Read(data, 0, data.Length);
Console.WriteLine("Bytes: " + bytes + " Data: " + System.Text.Encoding.ASCII.GetString(data));
responseData = System.Text.Encoding.ASCII.GetString(data);
Console.WriteLine("[" + ip + "] " + responseData);
if (responseData.StartsWith("close"))
{
Console.WriteLine("[" + ip + "] Connection closed.");
break;
}else if (responseData.StartsWith("useauthtoken"))
{
Console.WriteLine("[" + ip + "] Using auth token to authenticate.");
char c = '|';
string[] args = responseData.Split(c);
if (!(args.Length > 1))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
string response = GetAsync("http://example.com/cauthtokencheck.php?auth=" + args[1]);
if (response.Equals("yes"))
{
clientSession = args[1];
SendMessage(stream, "OK");
}
else
{
SendMessage(stream, "ERROR_AUTH_INVALID");
}
}
else if (responseData.StartsWith("auth"))
{
Console.WriteLine("[" + ip + "] Using auth cred to authenticate.");
char c = '|';
string[] args = responseData.Split(c);
Console.WriteLine("Data splitted");
if (!(args.Length > 2))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
string username = args[1];
string password = args[2];
Console.WriteLine("[" + ip + "] Using credentials: " + username + " and " + password);
Console.WriteLine("Logging in...");
string response = GetAsync(("https://example.com/clogin.php?name=" + username + "&password=" + password + ""));
Console.WriteLine("Login attempt completed, with " + response);
if (response.Equals("ERROR_FAILED_CONNECTION"))
{
SendMessage(stream, "ERROR_SERVERERROR");
}
else if (response.Equals("INVALID_USERNAME"))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_PASSWORD"))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
else if (response.Equals("INVALID_CRED"))
{
SendMessage(stream, "ERROR_AUTH_INVALID_CRED");
}
else if (response.Equals("IS_BANNED"))
{
SendMessage(stream, "ERROR_AUTH_BANNED");
}
else
{
Console.WriteLine("[" + ip + "] LOGIN OK | RESPONSE: " + response);
if (response == null) response = "Response was null?";
SendMessage(stream, response);
Console.WriteLine("Sended message...");
clientSession = response;
}
}
if ((!responseData.StartsWith("auth") || !responseData.StartsWith("useauthtoken") || !responseData.StartsWith("close")) && clientSession.Equals("NULL"))
{
SendMessage(stream, "ERROR_AUTH_MISSING");
}
if (responseData.Equals("endGame"))
{
char c = '|';
string[] args = responseData.Split(c);
if (!(args.Length > 3))
{
SendMessage(stream, "INVALID_DATA_GIVEN");
}
string won = args[0];
string kills = args[1];
string singleplayer = args[2];
string response = GetAsync("https://example.com/cgameend.php?session=" + client + "&won=" + won + "&kills=" + kills + "&singleplayer=" + singleplayer);
if (response.Equals("ERROR_FAILED_CONNECTION"))
{
SendMessage(stream, "ERROR_SERVERERROR");
}
else if (response.Equals("SESSION_INVALID"))
{
SendMessage(stream, "ERROR_AUTH_MISSING");
}
else if (response.Equals("SUCCESS"))
{
SendMessage(stream, "SUCCESS");
}
}
}
catch (Exception e)
{
Console.WriteLine("[" + ip + "] Connection closed: " + e.Message);
break;
}
}
}
public static string GetAsync(string uri, Action<WebHeaderCollection> headers = null)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
//request.Headers.Set(HttpRequestHeader.ContentLocation, uri);
//request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
Console.WriteLine(request.RequestUri);
using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
public static void SendMessage(NetworkStream stream, string msg)
{
Byte[] sendBytes = Encoding.ASCII.GetBytes(msg);
stream.Write(sendBytes, 0, sendBytes.Length);
}
public static bool RemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
bool isOk = true;
return isOk;
}
}
clogin.php:
<?php
$conn = new mysqli("localhost", "root", "not-the-real-password", "topdown");
if ($conn->connect_error) {
die("ERROR_FAILED_CONNECTION");
}
$name = $_GET["name"];
$password = $_GET["password"];
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
function endsWith($haystack, $needle)
{
$length = strlen($needle);
if ($length == 0) {
return true;
}
return (substr($haystack, -$length) === $needle);
}
$sql = "SELECT `password`,`banned` FROM `accounts` WHERE `name`='".$name."';";
$result = $conn->query($sql);
if ($result->num_rows < 1) {
die("INVALID_CRED");
die();
}
$found = "";
$banned = false;
while($row = $result->fetch_assoc()) {
$found = $row["password"];
$banned = $row["banned"];
}
if($found == $password){
if($banned==1){
die("IS_BANNED");
}
$session = generateRandomString(16);
$sql = "UPDATE `accounts` SET `clientsession`='".$session."' WHERE `name`='".$name."'";
if ($conn->query($sql) === TRUE) {
die($session);
}else{
die("ERROR_FAILED_CONNECTION");
}
}else{
die("INVALID_CRED");
}
?>
该消息来自服务器,而不是客户端。请参阅此答案以获取可能的解决方案:
您的模拟没有反映您在非模拟情况下发送到服务器的内容,这就是您无法在模拟中重现错误的原因。
在模拟的情况下你读了一行,字符串的长度cmd
就是行的长度。在非模拟情况下,您改为读入大小为 8192 字节的缓冲区 data
。这意味着,如果您读取 auth|abc|abc
的响应, data
的内容将是 auth|abc|abc[=14=][=14=][=14=][=14=]....
,即服务器发送的内容,然后是 8180(8192-12)个字符 [=15= ]
(即 \x00
、[=17=]0
或者可以用 C# 编写)。
在您 responseData.Split
之后,密码(即 args[2]
)将不会像您预期的那样是 abc
,而是 abc[=21=][=21=][=21=][=21=]....
。这再次意味着应该是 https://....?user=abc&pass=abc
的 URL 实际上是 https://....?user=abc&pass=abc[=23=][=23=][=23=]....
。 [=15=]
需要用 URL 编码为 %00
进行编码,这会导致 https://....?user=abc&pass=abc%00%00%00....
并且已经所有这些 8180 %00
将在 URL 中产生 24540 个字符,这解释了为什么服务器抱怨 URL 太大。查看服务器访问日志或错误日志也可能会显示此类问题。