rails json.parse“意想不到的令牌”错误

发布于 2025-02-14 00:37:48 字数 980 浏览 2 评论 0原文

我有一个由Webhooks击中的简单控制器。我需要将发送的所有数据存储在模型的元数据中,该数据是text列以供以后消耗。

class NotificationsController < ApplicationController
  def create
    notification = Notification.new(
      metadata: params,
    )
    if notification.save
      head :ok
    end
  end
end

当我在任何控制器操作中检查params.class时,我会得到actionController ::参数像哈希一样的对象。

但是,当在元数据中存储params,如上所示,我得到了一个看起来像这样的字符串:

"{\"SmsSid\"=>\"ID\", \"SmsStatus\"=>\"STATUS\",\"controller\"=>\"notifications\", \"action\"=>\"create\"}"

我尝试将该字符串转换回Hash,但是执行json.parse(params)引发以下错误:

JSON::ParserError: 783: unexpected token at "{\"SmsSid\"=>\"ID\", \"SmsStatus\"=>\"STATUS\",\"controller\"=>\"notifications\", \"action\"=>\"create\"}"

这是因为列类型是text而不是jsonb?如果是这样,是否有任何不涉及数据库迁移以更改列类型的解决方法?

I have a simple controller that is hit by webhooks. I need to store all data sent in a model's metadata which is a text column for later consumption.

class NotificationsController < ApplicationController
  def create
    notification = Notification.new(
      metadata: params,
    )
    if notification.save
      head :ok
    end
  end
end

When I inspect params.class inside any controller action, I get an ActionController::Parameters object that acts like a hash.

However, when storing params in the metadata as shown above, I get a string that looks like this:

"{\"SmsSid\"=>\"ID\", \"SmsStatus\"=>\"STATUS\",\"controller\"=>\"notifications\", \"action\"=>\"create\"}"

I tried converting that string back to a hash, but doing JSON.parse(params) throws the following error:

JSON::ParserError: 783: unexpected token at "{\"SmsSid\"=>\"ID\", \"SmsStatus\"=>\"STATUS\",\"controller\"=>\"notifications\", \"action\"=>\"create\"}"

Is this because the column type is text and not jsonb? If so, is there any workaround that does not involve a DB migration to change the column type?

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

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

发布评论

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

评论(2

海夕 2025-02-21 00:37:49

当我在任何控制器操作中检查参数时,我会得到一个actioncontroller ::参数对象,就像哈希一样。

是的,这是params是什么。这是一个对象,在大多数方面都像hash

但是,当在元数据中存储参数时,如上所示,我会得到一个看起来像这样的字符串:

 “ {{\” smssid \“ =&gt; \” id \“,\” smsstatus \“ =&gt; \ \” status \ \“ status \”,\“ controller \” =&gt; \ \ \“ notifications \”, \“ action \” =&gt; \“ create \”}“}”
 

是的,因为您要栏杆采取不像字符串的东西,然后将其转换为字符串,以便可以将其存储在文本列。 Rails通过在对象上调用.to_s来完成此操作,该对象返回您在此处看到的字符串表示。

我尝试将该字符串转换回哈希,但是执行json.parse(params)会引发以下错误:

该字符串不是JSON。如果要序列化params hash to json,则可以使用序列化api 定义列应如何序列化,然后保存 params的一些安全子集

class Notification < ActiveRecord::Base
  serialize :metadata, JSON
end

...

notification = Notification.new(
  metadata: params.permit(:SmsSid, :SmsStatus),
)

之后,您可以访问notification.params,它将为您透明地进行序列化。

请注意,只需将整个不动力的参数对象丢入数据库中,这是允许攻击者用千兆字节的垃圾文本淹没数据库的好方法。您绝不应该假设params是安全的;仅处理您期望的参数的子集,并确保关联的值符合您的期望。只需使用params.parmit(...),就像我上面所做的那样,仍然不够,因为值可能很长,或包含任意垃圾。您应该在模型上使用验证器来执行长度和格式限制。

When I inspect params.class inside any controller action, I get an ActionController::Parameters object that acts like a hash.

Yes, this is what params is. It's an object, that acts like a hash in most respects.

However, when storing params in the metadata as shown above, I get a string that looks like this:

"{\"SmsSid\"=>\"ID\", \"SmsStatus\"=>\"STATUS\",\"controller\"=>\"notifications\", \"action\"=>\"create\"}"

Yes, because you're asking Rails to take something that is not a string, and convert it to a string so it can be stored in a text column. Rails does this by calling .to_s on the object, which returns the string representation you're seeing here.

I tried converting that string back to a hash, but doing JSON.parse(params) throws the following error:

That string isn't JSON. If you want to serialize the params hash to JSON, you can use the serialization API to define how the column should be serialized, and then save some safe subset of params:

class Notification < ActiveRecord::Base
  serialize :metadata, JSON
end

...

notification = Notification.new(
  metadata: params.permit(:SmsSid, :SmsStatus),
)

Afterwards, you can access notification.params and it will be transparently deserialized for you.

Note that simply dumping the entire unsanitized params object into your database is a great way to allow attackers to flood your database with gigabytes of garbage text. You should never assume that params is safe; only work with a subset of params that you expect, and make sure the associated values conform to your expectations. Simply using params.permit(...) as I've done above is still not sufficient, as the values could be extremely long, or contain arbitrary garbage. You should use validators on your model to enforce length and format restrictions.

£冰雨忧蓝° 2025-02-21 00:37:49

我认为将数据存储为JSON字符串可以解决问题。这样:

class NotificationsController < ApplicationController
  def create
    notification = Notification.new(
      metadata: params.to_json,
    )
    if notification.save
      head :ok
    end
  end
end

然后:

JSON.parse(Notification.first.metadata)

或者,如果不想在任何地方写上述代码,您可以覆盖元数据getter方法

class Notification < ApplicationRecord

  def metadata
    JSON.parse(self[:metadata])
  end
end

I think storing the data as json string can solve the problem. like this:

class NotificationsController < ApplicationController
  def create
    notification = Notification.new(
      metadata: params.to_json,
    )
    if notification.save
      head :ok
    end
  end
end

Then:

JSON.parse(Notification.first.metadata)

or you can override the metadata getter method if don't want to write the above code everywhere

class Notification < ApplicationRecord

  def metadata
    JSON.parse(self[:metadata])
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文