【问题标题】:Preventing HTML and Script injections in Javascript防止 Javascript 中的 HTML 和脚本注入
【发布时间】:2014-01-18 06:42:46
【问题描述】:

假设我有一个带有输入框的页面。用户在输入框中输入内容并点击按钮。该按钮触发一个函数,该函数获取输入到文本框中的值并将其输出到文本框下方的页面上,无论出于何种原因。

现在很难找到明确的答案,否则我不会问,但你会如何输出这个字符串:

<script>alert("hello")</script> <h1> Hello World </h1>

这样既不执行脚本也不显示 HTML 元素?

我在这里真正要问的是,是否有一种标准方法可以避免 Javascript 中的 HTML 和脚本注入。每个人似乎都有不同的方法(我正在使用 jQuery,所以我知道我可以简单地将字符串输出到 text 元素而不是 html 元素,但这不是重点)。

【问题讨论】:

  • 您要阻止 所有 HTML 注入,还是仅阻止 不安全 注入?
  • 另外,如果用例真的如你所说,而且这只是客户端 JavaScript,你真的不需要防止“注入”。只有当输入没有显示给其他人时,用户才能攻击自己(如果它显示给其他用户,你会在服务器端清理输入)。
  • 所有,这更多是关于防止此类事情发生的概念和方法的解释。什么是不安全的 html 注入而不是安全的?
  • &lt;h1&gt;Hello World&lt;/h1&gt; 是一种安全注入,因为它不会给用户带来安全风险。如果您想防止 HTML/JS 注入,您可以删除编码 HTML 标记。就这么简单。

标签: javascript html javascript-injection html-injections


【解决方案1】:

您可以将 &lt;&gt; 编码为它们的 HTML 等效项。

html = html.replace(/</g, "&lt;").replace(/>/g, "&gt;");

How to display HTML tags as plain text

【讨论】:

  • 王牌!正是我想要的,如果不清楚,请道歉。这是一个正则表达式呢?除了对其他代码稍作修改外,我没有特别使用它们。
  • 这是一个非常简单的正则表达式——表达式基本上是&lt;。在 javascript 中,正则表达式在斜杠 (//) 内定义,g 表示它应该搜索所有出现的事件(g = 全局)。
  • @dlampard 请注意,这正是 .text() 所做的,所以如果你有 jQuery,就不需要自定义正则表达式。
  • 好的,我试图避免使用 jQuery,因为它在我试图理解概念和基础知识时掩盖了实际发生的事情。不过我确实喜欢它。
  • 如果您只想允许一些标签,例如粗体或斜体,您知道该怎么做吗?
【解决方案2】:
myDiv.textContent = arbitraryHtmlString 

正如@Dan 指出的那样,不要使用innerHTML,即使在你不附加到文档的节点中,因为延迟的回调和脚本总是被执行。您可以查看此https://gomakethings.com/preventing-cross-site-scripting-attacks-when-using-innerhtml-in-vanilla-javascript/ 了解更多信息。

【讨论】:

  • 请注意,该问题不包含jquery 标记,因此您应该尝试用纯javascript 回答。
  • 如果 OP 要求,我将详细说明并提供无 jquery 的答案。这里的原理是一样的。
  • 我不完全确定为什么,但如果 html 的值为 0,这似乎返回一个空字符串。
  • 你必须给它一个字符串,所以"0" 可以工作。您可以检查源 github.com/jquery/jquery/blob/2.2-stable/src/core/parseHTML.js 并查看它丢弃非字符串输入
  • 问题:您确定temp.innerHTML = arbitraryHtmlString; 不会开始预加载图像并运行任何在图像标签等中定义的onload 处理程序吗?
【解决方案3】:

单行:

var encodedMsg = $('<div />').text(message).html();

看看效果:

https://jsfiddle.net/TimothyKanski/wnt8o12j/

【讨论】:

    【解决方案4】:

    我使用这个函数 htmlentities($string):

    $msg = "<script>alert("hello")</script> <h1> Hello World </h1>"
    $msg = htmlentities($msg);
    echo $msg;
    

    【讨论】:

      【解决方案5】:

      From here

      var string="<script>...</script>";
      string=encodeURIComponent(string); // %3Cscript%3E...%3C/script%3
      

      【讨论】:

        【解决方案6】:

        我使用打字稿+装饰器+正则表达式的解决方案

        const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
        return value.replace(removeTag, "");
        

        "use strict";
        var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
            var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
            if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
            else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
            return c > 3 && r && Object.defineProperty(target, key, r), r;
        };
        var __metadata = (this && this.__metadata) || function (k, v) {
            if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
        };
        function filter(target) {
            return class extends target {
                constructor(...args) {
                    super(...args);
                }
                setState(opts) {
                    const state = {
                        username: this.filter(opts.username),
                        password: this.filter(opts.password),
                    };
                    super.setState(state);
                }
                filter(value) {
                    const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
                    return value.replace(removeTag, "");
                }
            };
        }
        let Form = class Form {
            constructor() {
                this.state = {
                    username: "",
                    password: "",
                };
            }
            setState(opts) {
                this.state = {
                    ...this.state,
                    ...opts,
                };
            }
            getState() {
                return this.state;
            }
        };
        Form = __decorate([
            filter,
            __metadata("design:paramtypes", [])
        ], Form);
        function getElement(key) {
            return document.getElementById(key);
        }
        const button = getElement("btn");
        const username = getElement("username");
        const password = getElement("password");
        const usernameOutput = getElement("username-output");
        const passwordOutput = getElement("password-output");
        function handleClick() {
            const form = new Form();
            form.setState({ username: username.value, password: password.value });
            usernameOutput.innerHTML = `Username: ${form.getState().username}`;
            passwordOutput.innerHTML = `Password: ${form.getState().password}`;
        }
        button.onclick = handleClick;
        <!DOCTYPE html>
        <html lang="en">
          <head>
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <style>
              :root {
                --bg: #1d1907;
                --foreground: #e3e0cd;
                --primary: #cfb53b;
                --black: #333;
                --white: #fafafa;
              }
        
              @keyframes borderColor {
                from {
                  border-bottom: 1px solid var(--foreground);
                }
        
                to {
                  border-bottom: 1px solid var(--primary);
                }
              }
        
              * {
                outline: none;
                border: none;
              }
        
              body {
                padding: 0.5rem;
                font-family: "Fira Code";
                background-color: var(--bg);
                color: var(--foreground);
              }
        
              input {
                border-bottom: 1px solid var(--foreground);
                background-color: var(--black);
                color: var(--foreground);
                padding: 0.5rem;
              }
        
              input:focus {
                animation-name: borderColor;
                animation-duration: 3s;
                animation-fill-mode: forwards;
              }
        
              button {
                padding: 0.5rem;
                border-radius: 3px;
                border: 1px solid var(--primary);
                background-color: var(--primary);
                color: var(--white);
              }
        
              button:hover,
              button:active {
                background-color: var(--white);
                color: var(--primary);
              }
        
              .form {
                margin-bottom: 2rem;
              }
            </style>
            <title>Decorator</title>
          </head>
          <body>
            <h1>Prevent Injection</h1>
            <div class="form">
              <div class="form-group">
                <label for="username">Username</label>
                <input type="text" id="username" placeholder="Type your username" />
              </div>
              <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" placeholder="Type your password" />
              </div>
              <div class="form-group">
                <button id="btn">Enviar</button>
              </div>
            </div>
            <div class="form-result">
              <p id="username-output">Username:</p>
              <p id="password-output">Password:</p>
            </div>
            <script src="/dist/pratica1.js"></script>
          </body>
        </html>

        打字稿代码如下:

            type State = {
          username: string;
          password: string;
        };
        
        function filter<T extends new (...args: any[]) => any>(target: T): T {
          return class extends target {
            constructor(...args: any[]) {
              super(...args);
            }
        
            setState(opts: State) {
              const state = {
                username: this.filter(opts.username),
                password: this.filter(opts.password),
              };
              super.setState(state);
            }
        
            filter(value: string) {
              const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
              return value.replace(removeTag, "");
            }
          };
        }
        
        @filter
        class Form {
          private state: State;
        
          constructor() {
            this.state = {
              username: "",
              password: "",
            };
          }
        
          setState(opts: State) {
            this.state = {
              ...this.state,
              ...opts,
            };
          }
        
          getState() {
            return this.state;
          }
        }
        
        function getElement(key: string): HTMLElement | null {
          return document.getElementById(key);
        }
        
        const button = getElement("btn") as HTMLButtonElement;
        const username = getElement("username") as HTMLInputElement;
        const password = getElement("password") as HTMLInputElement;
        const usernameOutput = getElement("username-output") as HTMLParagraphElement;
        const passwordOutput = getElement("password-output") as HTMLParagraphElement;
        
        function handleClick() {
          const form = new Form();
          form.setState({ username: username.value, password: password.value });
          usernameOutput.innerHTML = `Username: ${form.getState().username}`;
          passwordOutput.innerHTML = `Password: ${form.getState().password}`;
        }
        
        button.onclick = handleClick;
        

        【讨论】:

          【解决方案7】:

          试试这个方法将“可能包含 html 代码的字符串”转换为“文本格式”:

          $msg = "<div></div>";
          $safe_msg = htmlspecialchars($msg, ENT_QUOTES);
          echo $safe_msg;
          

          希望这会有所帮助!

          【讨论】:

            【解决方案8】:

            用这个,

            function restrict(elem){
              var tf = _(elem);
              var rx = new RegExp;
              if(elem == "email"){
                   rx = /[ '"]/gi;
              }else if(elem == "search" || elem == "comment"){
                rx = /[^a-z 0-9.,?]/gi;
              }else{
                  rx =  /[^a-z0-9]/gi;
              }
              tf.value = tf.value.replace(rx , "" );
            }
            

            在后端,对于 java ,尝试使用 StringUtils 类或自定义脚本。

            public static String HTMLEncode(String aTagFragment) {
                    final StringBuffer result = new StringBuffer();
                    final StringCharacterIterator iterator = new
                            StringCharacterIterator(aTagFragment);
                    char character = iterator.current();
                    while (character != StringCharacterIterator.DONE )
                    {
                        if (character == '<')
                            result.append("&lt;");
                        else if (character == '>')
                            result.append("&gt;");
                        else if (character == '\"')
                            result.append("&quot;");
                        else if (character == '\'')
                            result.append("&#039;");
                        else if (character == '\\')
                            result.append("&#092;");
                        else if (character == '&')
                            result.append("&amp;");
                        else {
                        //the char is not a special one
                        //add it to the result as is
                            result.append(character);
                        }
                        character = iterator.next();
                    }
                    return result.toString();
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-12-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-04-12
              • 2017-09-05
              • 1970-01-01
              • 2020-02-24
              相关资源
              最近更新 更多