414 - 请求的 uri 太长 - url 长度为 70 个字符

Posted

技术标签:

【中文标题】414 - 请求的 uri 太长 - url 长度为 70 个字符【英文标题】:414 - The requested uri is too long - the url is 70 characters long 【发布时间】:2019-12-11 22:56:22 【问题描述】:

我尝试使用 C# 进行身份验证。 它在尝试登录时发送 HTTP GET 请求,如下所示:

https://example.com/clogin.php?name=abc&password=abc

这只是几个字符。但我收到“Request-URI too long”错误。

模拟请求似乎有效,但通过 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");
    
?>

【问题讨论】:

是否有任何重定向?那些很容易变成这个巨大的。也可能是登录表单和登录表单目标是两个不同的文件,并且登录表单只是在获得任何参数时抛出异常(几个 HTTP 参数太长了)。 服务器是否还没有准备好接受请求?我试过了,页面看起来非常“即将推出”。 @Christopher nope,它只是用于登录的 api 页面。不是真正的登录页面。 @Christopher 并且没有重定向。 curl 和这个 URL 没有出现这样的错误。而且您没有提供代码,因此不清楚您的请求与我尝试的有何不同。除此之外:请不要将文本(错误)包含在图像中 - 将文本粘贴到问题中。 【参考方案1】:

您的模拟没有反映您在非模拟情况下向服务器发送的内容,这就是您无法在模拟中重现错误的原因。

在模拟的情况下,您读取了一行,字符串 cmd 的长度就是该行的长度。在非模拟情况下,您改为读取大小为 8192 字节的缓冲区 data。这意味着如果您阅读auth|abc|abc的响应,data的内容将是auth|abc|abc\0\0\0\0....,即服务器发送的内容,然后是8180(8192-12)个字符\0(即\x00,@ 987654328@ 或者这可以用 C# 编写)。

在您的responseData.Split 之后,密码(即args[2])因此不会像您预期的那样是abc,而是abc\0\0\0\0....。这再次意味着应该是https://....?user=abc&amp;pass=abc 的URL 实际上是https://....?user=abc&amp;pass=abc\0\0\0....\0 需要使用 URL 编码编码为 %00 导致 https://....?user=abc&amp;pass=abc%00%00%00....

而且所有这些 8180 %00 将在 URL 中单独产生 24540 个字符,这解释了为什么服务器会抱怨 URL 太大。查看服务器访问日志或错误日志也可能会显示此类问题。

【讨论】:

我该如何解决这个问题? @GoldenretriverYT:bytes 已经包含已读取的字节数。确保只对这种大小的字符串进行操作(例如使用Substring)。 感谢您的回答,解决了问题。【参考方案2】:

该消息来自服务器,而不是客户端。有关可能的解决方案,请参阅此答案: How do I resolve a HTTP 414 "Request URI too long" error?

【讨论】:

不,不能是服务器。尝试使用您的网络浏览器访问此站点 - 没有问题。 @GoldenretriverYT:“不,不能成为服务器。” - 确实如此。仔细查看您得到的错误,它清楚地显示远程服务器返回错误:(414)” @SteffenUllrich 再次 - 你可以使用你的网络浏览器 - 没有问题 - 那么它到底应该是服务器吗? @GoldenretriverYT:您的请求很可能与浏览器请求不同,也与我尝试使用curl 的请求不同。服务器可以对不同的请求发送不同的响应。不幸的是,您没有提供任何类型的代码,因此无法说出您所做的不同以及可能导致服务器响应不同的原因。 @GoldenretriverYT:欢迎来到互联网。您需要了解为什么我们会欺骗用户代理字符串。 en.wikipedia.org/wiki/User_agent#User_agent_spoofing

以上是关于414 - 请求的 uri 太长 - url 长度为 70 个字符的主要内容,如果未能解决你的问题,请参考以下文章

如何为 nginx 请求设置允许的 url 长度(错误代码:414,uri 太大)

414(请求 URI 太长)

aws 弹性豆茎“请求 URI 太长”

是否可以增加 HttpClient 中的最大 URL 长度? (无效的 URI:uri 字符串太长)

nginx 414 Request-URI Too Large

GET请求的长度限制?