将任意 UTF8 字节块解码为字符串是否安全?

Is it safe to decode an arbitrary UTF8-byte-chunk to string?

将被破解成任意字节块的 UTF8 字符串解码为字符串(按块)安全吗?

此外,任意编码怎么样?

上下文是这个方法:

async getFileAsync(fileName: string, encoding: string):string
{
    const textDecoder = new TextDecoder(encoding);
    const response = await fetch(fileName);
    
    console.log(response.ok);
    console.log(response.status);
    console.log(response.statusText);
    
    // let responseBuffer:ArrayBuffer = await response.arrayBuffer();
    // let text:string = textDecoder.decode(responseBuffer);
    
    // https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/getReader
    const reader = response.body.getReader();
    let result:ReadableStreamReadResult<Uint8Array>;
    let chunks:Uint8Array[] = [];
    
    // due to done, this is unlike C#:
    // byte[] buffer = new byte[32768];
    // int read;
    // while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
    // {
    //     output.Write (buffer, 0, read);
    // }

    do
    {
        result = await reader.read();
        chunks.push(result.value);

        // would this be safe ? 
        let partN = textDecoder.decode(result.value);
        // chunks.push(partN);

        console.log("result: ", result.value, partN);
    } while(!result.done)

    let chunkLength:number = chunks.reduce(
        function(a, b)
        {
            return a + (b||[]).length;
        }
        , 0
    );
    
    let mergedArray = new Uint8Array(chunkLength);
    let currentPosition = 0;
    for(let i = 0; i < chunks.length; ++i)
    {
        mergedArray.set(chunks[i],currentPosition);
        currentPosition += (chunks[i]||[]).length;
    } // Next i 

    let file:string = textDecoder.decode(mergedArray);
    
    // let file:string = chunks.join('');
    return file;
} // End Function getFileAsync

现在我想知道的是,考虑任意编码是否安全,这样做:

result = await reader.read();
// would this be safe ? 
chunks.push(textDecoder.decode(result.value));

我所说的“安全”是指它会导致整个字符串被正确解码吗?

我猜不是,但我想我只是想让别人确认一下。

我想当我不得不等到最后合并分块数组时,我也可以调用

let responseBuffer:ArrayBuffer = await response.arrayBuffer();
let text:string = textDecoder.decode(responseBuffer);

相反。

这取决于你所说的安全。

你知道原始字符串的大小,所以你有最大的解码字符串大小。所以这减少了很多现代 DoS(放大攻击)。

算法很简单。但是对于如何使用数据有很多安全隐患:UTF-8 可能会隐藏不必要的长序列。好的解码器应该丢弃它们,但也许在需要 U+0000 时(长编码有助于保持 C 字符串快乐,但也可以使用所有 Unicode 字符(U+0000)。你应该测试这个。你不希望该字符串有一个 0x00 值,并且一些函数将使用一个长度,一些函数将使用另一个长度的字符串,因此可能会出现缓冲区溢出。

UCS 使用 UTF-8 的泛化,允许编码更多位(最多 31 位),但因此占用更多字节。有些 UTF-8 解码器允许,有些则不允许。一般来说,这应该是一个错误,因为许多操作函数对高于当前 Unicode 限制的代码点不满意。

规范化有很多含义,例如删除不必要的代码点:Unicode(以及其他库)可能会遇到字符编码过多的问题(超过 16 或 32 个代码点,我不记得确切的最低要求)。

显然代码点的排序,composing/decomposing 也有自己的安全问题,但这似乎不在你的问题之列,比如一些字符可能看起来像(或完全像)其他[非个性化]。

好的解码器应该检测 UTF8 中的无效字节 (0xC0)、UTF-8 的超长序列(使用更多字节来获取代码点)以及 Unicode 之外的代码点(因此超过 4 个字节,UCS 允许)。但是一些解码器更加宽松,所以程序应该能够处理它。还有无效序列,但这些是不可解码的,所以解码器通常会做正确的事情(但有些插入错误符号,有些只是丢弃无效字节,并尝试恢复。