使用不带引号的键安全地解析 JSON 字符串

发布于 2024-10-03 18:09:09 字数 1055 浏览 5 评论 0原文

json2.js 严格要求所有对象键都用双引号引起来。然而,在 Javascript 语法中 {"foo":"bar"} 相当于 {foo:"bar"}

我有一个文本区域,它接受用户的 JSON 输入,并希望“放松”对双引号键的限制。我研究了 json2.js 在评估 JSON 字符串之前如何分四个阶段验证它。我能够添加第五阶段以允许未加引号的密钥,并且想知道此逻辑是否存在任何安全隐患。

var data = '{name:"hello", age:"23"}';

// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
     .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
     .replace(/(?:^|:|,)(?:\s*\[)+/g, ":") // EDITED: allow key:[array] by replacing with safe char ":"
     /** everything up to this point is json2.js **/

     /** this is the 5th stage where it accepts unquoted keys **/         
     .replace(/\w+\s*\:/g, ":")) ) { // EDITED: allow any alphanumeric key

  console.log( (new Function("return " + data))() );
}
else {
  throw( "Invalid JSON: " + data );
}

json2.js is strict requiring all object keys be double-quoted. However, in Javascript syntax {"foo":"bar"} is equivalent to {foo:"bar"}.

I have a textarea that accepts JSON input from the user and would like to "ease" the restriction on double quoting the keys. I've looked at how json2.js validates a JSON string in four stages before it evals it. I was able to add a 5th stage to allow unquoted keys and would like to know if there are any security implications to this logic.

var data = '{name:"hello", age:"23"}';

// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
     .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
     .replace(/(?:^|:|,)(?:\s*\[)+/g, ":") // EDITED: allow key:[array] by replacing with safe char ":"
     /** everything up to this point is json2.js **/

     /** this is the 5th stage where it accepts unquoted keys **/         
     .replace(/\w+\s*\:/g, ":")) ) { // EDITED: allow any alphanumeric key

  console.log( (new Function("return " + data))() );
}
else {
  throw( "Invalid JSON: " + data );
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

子栖 2024-10-10 18:09:09
data.replace(/(['"])?([a-zA-Z0-9]+)(['"])?:/g, '"$2":');

这将替换参数名称上的任何单引号,并添加任何缺少的单引号。

data.replace(/(['"])?([a-zA-Z0-9]+)(['"])?:/g, '"$2":');

That will replace any single quotes on the parameter name, and add any that are missing.

帝王念 2024-10-10 18:09:09

使用 JSON5.parse

JSON5 是 JSON 的超集,允许 ES5 语法,包括 不带引号的属性键。 JSON5 参考实现 (json5 npm package) 提供了一个 JSON5 对象,其方法与内置 JSON 对象具有相同的参数和语义。

许多知名项目都使用 JSON5:

JSON5 于 2012 年启动,截至 2022 年,每周下载量超过 6500 万次,在 npm 上最依赖的软件包中排名前 0.1%,并已被 Chromium、Next 等主要项目采用.js、Babel、Retool、WebStorm 等。 MacOS 和 iOS 等 Apple 平台也原生支持它。

~ json5.org 主页

Use JSON5.parse

JSON5 is a superset of JSON that allows ES5 syntax, including unquoted property keys. The JSON5 reference implementation (json5 npm package) provides a JSON5 object that has the same methods with the same args and semantics as the built-in JSON object.

JSON5 is used by many high profile projects:

JSON5 was started in 2012, and as of 2022, now gets >65M downloads/week, ranks in the top 0.1% of the most depended-upon packages on npm, and has been adopted by major projects like Chromium, Next.js, Babel, Retool, WebStorm, and more. It's also natively supported on Apple platforms like MacOS and iOS.

~ json5.org homepage

同展鸳鸯锦 2024-10-10 18:09:09

JSON 不允许不带引号的键。 JSON 是 JavaScript 表示法的子集,不包括不带引号的键。将不带引号的键传递给几乎任何 JSON 解析器都可能会抛出错误或返回“意外”结果。

希望这有帮助

JSON does not allow unquoted keys. JSON is a subset of JavaScript notation, and that does not include unquoted keys. Passing unquoted keys to just about any JSON parser will likely throw an error or return "unexpected" results.

Hope this helps

℡寂寞咖啡 2024-10-10 18:09:09

也许你可以使用:

更换钥匙&价值

str.replace(/([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)/g, "\"$1\":\" $2\"");

仅更换钥匙

str.replace(/([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)/g, "\"$1\":$2" );

仅替换值

str.replace(/([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)/g, "\$1\:\"$2\ “”);

Maybe you can use:

Replace Key & Value

str.replace(/([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)/g, "\"$1\":\"$2\"");

Replace Key only

str.replace(/([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)/g, "\"$1\":$2");

Replace Value only

str.replace(/([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)/g, "\$1\:\"$2\"");
梦巷 2024-10-10 18:09:09

下面的代码片段会将大部分类似 js 的结构转换为有效的 JSON 语法。虽然比其他答案更扩展一些,但回报是更简洁的结果。在解析无法应用的情况下,我们将优雅地失败,将错误打印到控制台并返回原始输入。

Example

Flems Playground

< strong>功能

  ✓ 省略任何尾随逗号。
  ✓ 转义字符串中的换行符。
  ✓ 处理数组 [] 条目,将出现的字母数字转换为字符串。
  ✓ 保留 truefalse 的布尔值。
  ✓ 保留 numberfloat 出现。

代码

function toJSON (input) {

  try {

    const json = input
       .replace(/\\'|'/g, m => m[0] === '\\' ? m : '"')
       .replace(/"(?:\\.|[^\"])*"/g, m => m.replace(/\n/g, '\\n'))
       .replace(/\[|[^[\]]*|\]/g, m => /[[\]]/.test(m)  
          ? m 
          : m.split(',').map(value => value
             .replace(/^(\w+)$/, '"$1"')
             .replace(/^"([\d.]+)"$/g, '$1')).join(','))
      .replace(/([a-zA-Z0-9_-]+)\s*:/g,  '"$1":')
      .replace(/:\s*([$\w-]+)\s*([,\]}])/g, ':"$1"$2')
      .replace(/,(\s*[\]}])/g, '$1')
      .replace(/([a-zA-Z_-]+)\s*,/g,  '"$1",')
      .replace(/([\]},\s]+)?"(true|false)"([\s,{}\]]+)/g, '$1$2$3')

    return JSON.parse(json);

  } catch (e) {
    
    console.error(e, JSON.stringify(input, null, 2));
    return input;
    
  }
}

分解

以下是所发生情况的简要概述:

  1. 我们将任何单引号转换为双引号。
  2. 我们转义字符串中出现的任何换行符。
  3. 我们处理不同类型的所有数组项条目。
  4. 我们将所有出现的关键属性用双引号括起来。
  5. 我们将所有出现的非数字值用双引号括起来。
  6. 我们删除所有出现的尾随逗号。
  7. 我们从布尔类型 truefalse 中删除双引号
  8. 我们将替换的字符串传递给 JSON.parse
  9. 我们返回结果。

免责声明

通常应该避免使用正则表达式转换字符串 json~类似结构,并始终选择专为该工作设计的工具,因为结构可能是不可预测的,并且您永远无法确定使用我提供的“hack”得到的结果。 但是,并不是所有事情都是黑白分明的,因此在不要求完全准确的情况下,上述就足够了。

The below snippet will covert majority of js~like structures into valid JSON syntax. While a little more extended than other answers the payoff is a more concise result. In situations where the parse cannot apply, we will fail gracefully, print the error to console and return the original input.

Example

Flems Playground

Capabilities

  ✓ Omits any trailing commas.
  ✓ Escapes newlines within strings.
  ✓ Handles array [] entries, converting alphanumeric occurences to strings.
  ✓ Preserves boolean occurrences of true or false.
  ✓ Preserves number or float occurrences.

Code

function toJSON (input) {

  try {

    const json = input
       .replace(/\\'|'/g, m => m[0] === '\\' ? m : '"')
       .replace(/"(?:\\.|[^\"])*"/g, m => m.replace(/\n/g, '\\n'))
       .replace(/\[|[^[\]]*|\]/g, m => /[[\]]/.test(m)  
          ? m 
          : m.split(',').map(value => value
             .replace(/^(\w+)$/, '"$1"')
             .replace(/^"([\d.]+)"$/g, '$1')).join(','))
      .replace(/([a-zA-Z0-9_-]+)\s*:/g,  '"$1":')
      .replace(/:\s*([$\w-]+)\s*([,\]}])/g, ':"$1"$2')
      .replace(/,(\s*[\]}])/g, '$1')
      .replace(/([a-zA-Z_-]+)\s*,/g,  '"$1",')
      .replace(/([\]},\s]+)?"(true|false)"([\s,{}\]]+)/g, '$1$2$3')

    return JSON.parse(json);

  } catch (e) {
    
    console.error(e, JSON.stringify(input, null, 2));
    return input;
    
  }
}

Breakdown

Here is brief overview of what is happening:

  1. We convert any single quotations into double quotations.
  2. We escape any newline occurrences within strings.
  3. We handle all array item entries of varying types.
  4. We wrap all key property occurrences in double quotations.
  5. We wrap all non digit value occurrences in double quotation.
  6. We remove any trailing comma occurrences.
  7. We remove double quotations from boolean types true and false
  8. We pass the replaced string to JSON.parse
  9. We return the result.

Disclaimer

One should typically avoid converting string json~like structures with regex and always opt for a tool designed for the job because the structures can be unpredictable and you can never be certain of the result using the "hack" I've provided. However, not everything is black and white, as such in cases where complete accuracy is not demanded, the above will suffice.

风流物 2024-10-10 18:09:09

“带有注释的 JSON”实际上是一个有效的 javascript,因此在 javascript 环境中,解析它的最简单的本机方法就是像这样评估它,

function evalJs(js) {
    let fn = new Function("return (" + js + ")"),
        res = fn()
    return res;
}

let json5 = "{\n" +
    "//////\n" +
    "key: 5," +
    "}"

let data = evalJs(json5)
console.info(data)

输出是

{ key: 5 }

"JSON with comments" is actually a valid javascript, therefore in javascript environment the simplest native way to parse it is just evaluate it like this

function evalJs(js) {
    let fn = new Function("return (" + js + ")"),
        res = fn()
    return res;
}

let json5 = "{\n" +
    "//////\n" +
    "key: 5," +
    "}"

let data = evalJs(json5)
console.info(data)

Output is

{ key: 5 }
烙印 2024-10-10 18:09:09

!!!考虑安全风险——不要盲目评估

Object.defineProperty(String.prototype, "eval", {
    get: function() {
       let v;
       eval('v =' + this);
       return v;
    }
 });

var object = '{A:1,"B":[1,\'alfa\',"beta"]}'.eval 

!!! considering security risks -- do not eval blindly

Object.defineProperty(String.prototype, "eval", {
    get: function() {
       let v;
       eval('v =' + this);
       return v;
    }
 });

var object = '{A:1,"B":[1,\'alfa\',"beta"]}'.eval 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文