当通过Javascript输出JSON内容时,我应该在服务器端还是客户端转义HTML?

当通过Javascript输出JSON内容时,我应该在服务器端还是客户端转义HTML?,javascript,json,escaping,xss,Javascript,Json,Escaping,Xss,我有一个应用程序,它由一个用PHP编写的服务器端RESTAPI和一些使用该API并使用它生成的JSON呈现页面的客户端Javascript组成。这是一个非常典型的设置 RESTAPI提供的数据是“不可信的”,即它从数据库获取用户提供的内容。因此,例如,它可能会获取如下内容: { "message": "<script>alert("Gotcha!")</script>" } 已经逃走了 一方面,客户端似乎不必担心来自服务器的不安全数据。另一方面,有人可能会争辩说

我有一个应用程序,它由一个用PHP编写的服务器端RESTAPI和一些使用该API并使用它生成的JSON呈现页面的客户端Javascript组成。这是一个非常典型的设置

RESTAPI提供的数据是“不可信的”,即它从数据库获取用户提供的内容。因此,例如,它可能会获取如下内容:

{
    "message": "<script>alert("Gotcha!")</script>"
}
已经逃走了

一方面,客户端似乎不必担心来自服务器的不安全数据。另一方面,有人可能会争辩说,当我们确切地知道数据将如何被消费时,输出应该总是在可能的最后一分钟转义

哪种方法是正确的

注意:关于处理输入有很多问题,是的,我知道客户端代码总是可以被操纵的。这个问题是关于从我的服务器输出可能不可信的数据

更新:我调查了其他人在做什么,确实有些RESTAPI似乎倾向于发送“不安全”的JSON。Gitter的API实际上同时发送这两种数据,这是一个有趣的想法:

[
    {
        "id":"560ab5d0081f3a9c044d709e",
        "text":"testing the API: <script>alert('hey')</script>",
        "html":"testing the API: &lt;script&gt;alert(&#39;hey&#39;)&lt;/script&gt;",
        "sent":"2015-09-29T16:01:19.999Z",
        "fromUser":{
            ...
        },"unread":false,
        "readBy":0,
        "urls":[],
        "mentions":[],
        "issues":[],
        "meta":[],
        "v":1
    }
]
[
{
“id”:“560ab5d0081f3a9c044d709e”,
“文本”:“测试API:alert('hey')”,
“html”:“测试API:scriptalert(';嘿';)/script”,
“已发送”:“2015-09-29T16:01:19.999Z”,
“fromUser”:{
...
},“未读”:假,
“读取者”:0,
“URL”:[],
“提及”:[],
“问题”:[…],
“元”:[],
“v”:1
}
]
注意,它们在
text
键中发送原始内容,然后在
HTML
键中发送HTML转义版本。在我看来,这主意不错


我已经接受了答案,但我不认为这是一个简单的问题。我想鼓励进一步讨论这个话题。

关于输出转义:

我建议读一下这个

为了防止用户正确使用,您最好不仅转义,而且在转义之前使用适当的反XSS库对其进行过滤。喜欢,或者,或者任何来自

无论何时在web项目中显示用户输入的数据,都应该对其执行IMHO

我应该在服务器端还是客户端转义内容?也就是说,我的API应该返回原始内容,然后让客户端Javascript代码负责转义特殊字符,还是应该返回“安全”内容:

最好返回已转义的、xss已净化的内容,因此:

  • 从服务器上的xss获取原始数据并净化if
  • 逃避它
  • 返回到JavaScript
  • 此外,您还应该注意一件重要的事情,比如站点负载和读/写平衡:例如,如果您的客户机只输入一次数据,而您打算向100万用户显示这些数据,您更喜欢什么:每次读取(输出时保护)100万次,在写入之前运行一次保护逻辑(输入时保护)


    如果你打算在一个页面上显示1K条帖子,然后在客户端上转义,那么它在客户端的手机上的效果如何?最后一个选项将帮助您选择在客户端或服务器上保护数据的位置。

    这个答案更侧重于争论是否进行客户端转义还是服务器端转义,因为OP似乎知道反对输入转义还是输出转义的论点

    为什么不避开客户端呢? 我认为在javascript级别转义不是一个好主意。我脑子里的一个问题是,如果消毒脚本中有错误,它将不会运行,然后危险的脚本将被允许运行。因此,您引入了一个向量,攻击者可以在该向量中尝试手工输入以破坏JS Sanitarizer,从而允许其纯脚本运行。我也不知道在JS中运行的任何内置AntiXSS库。我确信有人已经制作了一个,或者可以制作一个,但是有一些已经建立的服务器端示例更值得信任。还值得一提的是,在JS中编写一个适用于所有浏览器的消毒剂并不是一项简单的任务

    好吧,如果你两个都逃跑了怎么办? 转义服务器端和客户端对我来说有点混乱,不应该提供任何额外的安全性。你提到了双重逃跑的困难,我以前也经历过这种痛苦

    为什么服务器端足够好?
    转义服务器端就足够了。你关于尽可能晚做这件事的观点是有道理的,但我认为逃避客户端的缺点被你通过做这件事可能获得的微小好处所抵消。威胁在哪里?如果您的站点和客户端之间存在攻击者,则客户端已经受到攻击,因为如果需要,他们可以发送一个带有脚本的空白html文件。您需要尽最大努力发送安全的信息,而不仅仅是发送处理危险数据的工具。

    仅在客户端转义

    在客户端转义的原因是安全性:服务器的输出是客户端的输入,因此客户端不应该信任它。如果您假设输入已经转义,那么您可能会通过恶意反向代理等方式受到客户端攻击。这与您应该始终在服务器端验证输入的原因没有太大区别,即使您还包括客户端验证


    不在服务器端转义的原因是关注点分离:服务器不应假定客户端打算将数据呈现为HTML。服务器的输出应尽可能与媒体无关(当然,考虑到JSON和数据结构的限制),以便客户端可以最轻松地将其转换为所需的任何格式。

    TLDR如果您的API要传递格式信息,它应该输出HTML编码
    [
        {
            "id":"560ab5d0081f3a9c044d709e",
            "text":"testing the API: <script>alert('hey')</script>",
            "html":"testing the API: &lt;script&gt;alert(&#39;hey&#39;)&lt;/script&gt;",
            "sent":"2015-09-29T16:01:19.999Z",
            "fromUser":{
                ...
            },"unread":false,
            "readBy":0,
            "urls":[],
            "mentions":[],
            "issues":[],
            "meta":[],
            "v":1
        }
    ]
    
    <%
    payload = "[{ foo: '" + foo + "'}]"
    %>
        <script><%= payload %></script>
    
    [
        {
            "id":"560ab5d0081f3a9c044d709e",
            "text":"testing the API: <script>alert('hey')</script>",
            "html":"testing the API: &lt;script&gt;alert(&#39;hey&#39;)&lt;/script&gt;",
            "sent":"2015-09-29T16:01:19.999Z",
    
    "html":"<u>Underlined</u>"
    
    "text":"Underlined"
    
    "text":"Thank you Alice for signing up."
    
    [{ "name", "alice",
    "messageType": "thank_you" }]
    
    "text":"Thank you Alice for signing up."