美文网首页
9,react怎么防止xss攻击

9,react怎么防止xss攻击

作者: kafya | 来源:发表于2021-09-08 16:12 被阅读0次

    出处

    React做了哪些事情来实现XSS安全性防御

    XSS 攻击

    Cross-Site Scripting (跨站脚步攻击)简称XSS, 是一种代码注入攻击。XSS攻击通常指利用网页漏洞,攻击者通过技巧注入xss代码到网页,因为浏览器无法分辨哪些是可信的,导致XSS脚步被执行。XSS脚本通常能够窃取用户数据并发送到攻击者的网站,或者冒充用户,调用目标接口并执行攻击者指定操作。

    XSS攻击类型

    反射型

    • XSS脚本来自当前HTTP请求
    • 当服务器在HTTP请求中接收数据并将该数据拼接在HTML中返回时
    // 某网站具有搜索功能,该功能通过URL参数接收用户提供的搜索词
    // https://xxx.com/search?query=123
    // 服务器在此URL的响应中回显提供的搜索词:
    <p>您的搜索是:123 </p>
    // 如果服务器不对数据进行转义等处理,则攻击者可以构造如下链接进行攻击
    // https://xxx.com/search?query=<img src="empty.png" onerror="alert('xss')" />
    // 改url回导致运行alert("xss")的响应
    <p>您的搜索是: <img src="empty.png" onerror="alert('xss')" /></p>
    // 如果有用户请求攻击者的URL,则攻击者提供的脚本将在用户的浏览器中执行
    ### 存储型 XSS
    - XSS 脚本来自服务器数据库中
    - 攻击者将恶意代码提交到目标网站的数据库中,普通用户访问网站时服务器将恶意代码返回,浏览器默认执行
    ~~~html
    // 某个评论页,能查看用户评论
    // 攻击者将恶意代码当做评论提交,服务器没对数据进行转义等处理
    // 评论输入:
    <textarea>
      <img src="empty.png" onerror="alert("xss")" />
    </textarea>
    // 则攻击者提供的脚本将在所有访问该评论页的用户浏览器执行
    

    DOM型XSS

    该漏洞存在以客户端代码,与服务器无关

    • 类似反射型,区别在于DOM型XSS并不会和后台进行交互,前端直接将URL中的数据不做处理并动态插入到HTML中,是纯粹的前端安全问题,要做防御也只能在客户端进行防御。

    React如何防止XSS攻击

    无论使用哪种攻击方式,其本质就是将恶意代码注入到应用中,浏览器去默认执行。react官方提到 React DOM在渲染所有输入内容之前,默认会进行转义。它可以确保在应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换为字符串,因此恶意代码无法成功注入,从而有效的防止XSS攻击。
    具体如下:

    自动转义

    React 在渲染HTML内容和渲染DOM属性时会将" ' & < >这几个字符进行转义,转义部分源码如下

    for(index = match.index; index<str.length; index++){
      switch (str.charCodeAt(index)){
        case 34: // "
          escape = "&quot;";
          break;
        case 38: // &
          escape = "&amp;";
          break;
        case 39: // '
          escape = "&#x27;";
          break;
        case 60: // <
          escape = "&lt;";
          break;
        case 62: // >
          escape = "&gt;";
          break;
        default:
           continue;
      }
    }
    

    这段代码是React在渲染到浏览器前进行转义,可以看到对浏览器有特殊含义的字符都被转义了,恶意代码在渲染到HTML前都被转成字符串,如下

       var p = document.getElementById("p");
      // 一段恶意代码
      const str = `<img src="empty.png" onerror="alert('xss')" />`;
          let _str = "";
          for (let index = 0; index < str.length; index++) {
            let escape = "";
            switch (str.charCodeAt(index)) {
              case 34: // "
                escape = "&quot;";
                break;
              case 38: // &
                escape = "&amp;";
                break;
              case 39: // '
                escape = "&#x27;";
                break;
              case 60: // <
                escape = "&lt;";
                break;
              case 62: // >
                escape = "&gt;";
                break;
              default:
                escape = str[index];
              // continue;
            }
            console.log(escape);
            _str += escape;
          }
          console.log(_str); // &lt;img src=&quot;empty.png&quot; onerror=&quot;alert(&#x27;xss&#x27;)&quot; /&gt;
          p.innerHTML = `您的搜索是:${_str}`;
    

    这样就有效的防止XSS攻击

    JSX语法

    JSX实际上是一种语法糖,Babel会把JSX编译成React.createElement()函数调用,最终返回一个ReactElement,以下为这几个步骤的对应代码:

    const element= (
      <h1 className="greeting">
        hello world!
      </h1>
    )
    // 通过babel编译后的代码
    const element = React.createElement(
      "h1",
      {className: "greeting"},
      "hello world!"
    )
    // React.createElement()  返回ReactElement
    const element = {
      $$typeof: Symbol("react.element"),
      type: "h1",
      key: null,
      props:  {
        children: "Hello world!",
        className: "greeting"
      }
    ...
    }
    
    const symbolFor = Symbol.for;
    REACT_ELEMENT_TYPE = symbolFor("react.element");
    const ReactElement = function(type, key, ref, self, source, owner, props){
      const element ={
        // 这个tag唯一标识了此为ReactElement
        $$typeof: REACT_ELEMENT_TYPE,
        // 元素的内置属性
        type:type, 
        key: key,
        ref: ref,
        props: props,
        // 记录创建此元素的组件
        _owner: owner,
      }
      ...
      return element;
    }
    
    • $$typeof标记对象是一个ReactElement属性,在渲染时会通过此属性校验,校验不通过会抛出错误。React利用这个属性来防止通过构造特殊的Children来进行XSS攻击,原因是$$typeof是个Symbol类型,进行JSON转换后Symbol值会丢失,无法在前后端进行传输。如果用户提交了特殊的Children, 也无法进行渲染,利用此特性,可以防止存储型XSS攻击。

    在React中可引起的漏洞的一些写法

    使用dangerouslySetInnerHTML

    dangerouslySetInnerHTML是React为浏览器DOM提供的innerHTML替换方案,通常来讲,使用代码直接设置HTML存在的风险,因为很容易使用户暴露在XSS攻击下,因为当使用dangerouslySetInnerHTML时, React将不会对输入进行任何处理并直接渲染到HTML中,如果攻击者在dangerouslySetInerHTML传入了恶意代码,那么浏览器将会运行恶意代码

    // dangerouslySetInerHTML 源码
    function getNonChildrenInnerMarkup(props){
      const innerHTML = props.dangerouslySetInnerHTML;
      if(innerHTML != null){
        if(innerHTML.__html != null){
          return innerHTML.__html;
        }
      } else {
        const content = props.children;
        if(typeof content === "string" || typeof content === "number"){
           return escapeTextForBrowser(content);
        }
      }
      return null;
    }
    

    所以平时开发应避免使用dangerouslySetInnerHTML, 如果不得不使用,必须对输入进行相关的验证,例如对特殊输入进行过滤、转义等处理。前端这边可以使用xss白名单过滤,通过白名单控制允许HTML标签及各标签属性。

    通过用户提供的对象来创建React组件

    // 用户输入
    const userProvidePropString = `{"dangerouslySetInnerHTML":{"__html":"<img onerror='alert(\"xss\")' src='empty.png' /> "}}`;
    // 经过JSON转换
    const userProvideProps = JSON.parse(userProvidePropsString);
    render(){
      return <div {...userProvideProps} />
    }
    

    中断代码将用户提供的数据进行json转换后直接当做div的属性,当用户构造了一个类似列子中的特殊字符串时,页面就会被注入恶意代码,所以要注意平时在开发中不要直接使用用户输入作为属性。

    使用用户输入的值来渲染a标签的href属性,或类似的img 标签的src属性等

    const userWebsite = "javascript:alert('xss');";
    <a href={userWebsite}></a>
    

    如果没有对该URL进行过滤以防止通过JavaScript:data:来执行JavaScript,则攻击者可以构造XSS攻击,此处会有潜在的安全问题。用户提供的URL需要前端或服务端在入库之前进行校验并过滤。

    服务端如何防止XSS攻击

    服务端作为最后一道防线,也需要做一些措施以防止XSS攻击的,一般涉及以下几方面:

    • 在收到用户输入时,需要对输入尽可能的严格过滤,过滤或移除特殊的HTML标签、js事件的关键字等
    • 在输出时对数据进行转义,根据输出语境进行对应的转义
    • 对关键Cookie设置http-only属性,js脚本就不能访问到http-only的Cookie
    • 利用CSP来抵御或削弱XSS攻击,一个CSP兼容的浏览器将会仅执行从白名单获取到的脚本文件,忽略所有的其他脚本(包括内联脚本和HTML的事件处理属性)

    CSP

    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';"> 
    

    相关文章

      网友评论

          本文标题:9,react怎么防止xss攻击

          本文链接:https://www.haomeiwen.com/subject/kxdiwltx.html