前端安全,前端防火墙

前端防火墙的试行

通过近一段时间通过对 zjcqoo 的《XSS
前端防火墙》六板斧的再三探讨通晓,基本上防范措施能够归为两大类:一种是从公约上遮盖,一种是在此之前端代码层面实行阻拦移除。通过
zjcqoo
建议的二种注入防范措施,实行多少个月的进行观望,对广告注入方式大致能够归为二种:完全静态注入、先静态注入后动态修改(成立)。

  1. 一心静态注入
    统统内联 js、css、和 dom,不管是 body
    内外,甚是恶心,何况一旦是在督察脚本后边注入的,还足以超超过实际行,形成防守不起成效。注入的
    DOM 也无计可施清除。
  2. 先静态注入后动态修改
    这种能够分成三种:一种是异步诉求接口数据再生成 DOM 注入,一种是修改
    iframe 源地址举办引进,别的一种是修改 script 源地址,央浼实行 js
    再异步获取数据或生成 DOM。

锁死 apply 和 call

接下去要介绍的这么些是锁住原生的
Function.prototype.apply 和 Function.prototype.call
方法,锁住的意味就是使之不能够被重写。

这边要用到 Object.defineProperty ,用于锁死
apply 和 call。

双剑合璧

不畏是只是的 DOM
注入,鲜明无法满意更加高端作用的行使,也会使运行商的广告分发平台效应大降价扣。假设单独当中一种方法进行应用,也只是表明了一招一式的半成功力,假诺是双臂互搏,那也足以发挥成倍的素养。

而后边一个防火墙再增添 CSP
安全攻略,双剑合璧,则足以大大减弱广告注入带来的阴暗面效果,重则产生广告代码严重瘫痪不能运维:在监察和控制脚本后注入广告脚本,基本上能够被前端防火墙封闭扼杀殆尽,就算有漏网之鱼,也会被
CSP 进行追杀,不死也残。

纵然在监察和控制脚本运营前注入,通过 CSP content-src
计策,能够阻碍白名单域名列表外的接口诉求,使得广告代码的异步央浼工夫被封闭扼杀,script-src
战略,也得以封闭扼杀脚本外链的一些外界央浼,进一步封闭扼杀异步脚本援引,frame-src
战略无论先后创办的 iframe,一律照杀。

侥幸者躲过了初中一年级,却躲但是十五,前端防火墙拍马赶到,照样封闭扼杀准确,独一的渠道只有注入
DOM 这一措施,别忘了,只要张开 img-src
攻略配置,广告代码只剩下文字链。尽管是叁个文字链广告,但点击率又能高到哪去呢?

即便您是 node
派系,二哥附上《太虚神甲谱》 helmet 一本,假诺你的业务有涉及到
UCBrowser,更有《无量尺谱之 UC
版》helmet-csp-uc 。

所谓道高一尺魔高一丈,既然大家有急忙的防卫措施,相信她们赶紧也会追究出反防守措施,如此,我们也急需和那帮人斗智斗勇,一向等到
HTTP/2 标准的正统落地。

1 赞 3 收藏
评论

图片 1

 

创制双剑合璧的 XSS 前端防火墙

2015/09/30 · HTML5 ·
XSS

原版的书文出处: 林子杰(@Zack__lin)   

自然,堤防那个威胁最佳的办法只怕从后端入手,前端能做的实在太少。何况由于源码的展露,攻击者很轻易绕过大家的守卫手腕。不过那不代表大家去探听那块的有关文化是没意义的,本文的成百上千方法,用在其他方面也是大有成效。

前言

深远接触 xss 注入是从排查职业的广告注入最早,在此以前对 xss
注入片面认为是页面输入的拉萨校验漏洞导致一层层的主题素材,通过对 zjcqoo
的《XSS 前端防火墙》类别小说,认知到温馨实在对 XSS
注入的认知还真是半桶水。

接下去步向正文。

前端防火墙拦截

前端防火墙显明切合当作第一道防线进行规划,能够优先对一些注入的内联 js
代码、script/iframe 源引用实行移除,同期对 script/iframe
源地址修改做监察和控制移除。
主导安排逻辑大致如下:

图片 2

详尽的贯彻逻辑,参谋zjcqoo 的《XSS 前端防火墙》连串文章。

缺点:

  1. 借使是在监督检查脚本实施前,注入的脚本早就实行,鲜明后知后觉无法起防卫作用了。
  2. 一对 DOM 的注入明显无法。

优点:

  1. 能够针对 iframe 做一些自定义的过滤准则,制止对地面通讯误伤。
  2. 可以搜聚到有的流入行为数据进行解析。

静态脚本拦截

XSS
跨站脚本的卓越不在于“跨站”,在于“脚本”。

平日性来说,攻击者可能运维商会向页面中流入一个<script>本子,具体操作都在剧本中落实,这种威吓格局只供给注入贰遍,有改造的话无需每趟都再度注入。

小编们只要今后页面上被注入了多个 <script src="http://attack.com/xss.js"> 脚本,大家的指标就是阻止那几个剧本的实践。

听上去很辛勤啊,什么意思啊。便是在本子试行前发掘那一个猜忌脚本,况且销毁它使之不可能举行内部代码。

故此大家供给采取一些高等API ,能够在页面加载时对转移的节点开展检查实验。

 

守护措施介绍

浏览器事件模型

此处说能够堵住,涉及到了事件模型有关的原理。

咱俩都知道,标准浏览器事件模型存在五个阶段:

  • 破获阶段
  • 对象阶段
  • 冒泡阶段

对于一个这样 <a href="javascript:alert(222)" ></a> 的
a 标签来说,真正触发成分 alert(222) 是处于点击事件的指标阶段。

点击上边的 click me ,先弹出
111 ,后弹出 222。

那正是说,大家只需求在点击事件模型的捕获阶段对标签内 javascript:... 的内容组建着重字黑名单,举行过滤核查,就可以实现大家想要的遏止效果。

对于 on*
类内联事件也是同理,只是对于那类事件太多,大家无法手动枚举,能够利用代码自动枚举,完毕对内联事件及内联脚本的阻碍。

以堵住 a
标签内的 href="javascript:... 为例,大家能够这么写:

// 建立关键词黑名单
var keywordBlackList = [
  'xss',
  'BAIDU_SSP__wrapper',
  'BAIDU_DSPUI_FLOWBAR'
];

document.addEventListener('click', function(e) {
  var code = "";

  // 扫描 <a href="javascript:"> 的脚本
  if (elem.tagName == 'A' && elem.protocol == 'javascript:') {
    var code = elem.href.substr(11);

    if (blackListMatch(keywordBlackList, code)) {
      // 注销代码
      elem.href = 'javascript:void(0)';
      console.log('拦截可疑事件:' + code);
    }
  }
}, true);

/**
 * [黑名单匹配]
 * @param  {[Array]} blackList [黑名单]
 * @param  {[String]} value    [需要验证的字符串]
 * @return {[Boolean]}         [false -- 验证不通过,true -- 验证通过]
 */
function blackListMatch(blackList, value) {
  var length = blackList.length,
    i = 0;

  for (; i < length; i++) {
    // 建立黑名单正则
    var reg = new RegExp(whiteList[i], 'i');

    // 存在黑名单中,拦截
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}

能够戳作者翻看DEMO。(展开页面后张开调控台查看
console.log) 

点击图中那多少个开关,能够看来如下:

图片 3

那边大家用到了黑名单相配,下文还或然会细说。

 

全站 HTTPS + HSTS

拉开 HTTPS,可以进步数据保密性、完整性、和地点校验,而 HSTS (全称 HTTP
Strict Transport Security)能够保险浏览器在非常长日子里都会只用 HTTPS
访问站点,这是该防备措施的长处。不过,劣点和缺欠也不得忽略。

网络全站HTTPS的一代已经到来 一文已有详细的深入分析,加密解密的习性损耗在服务端的消耗和互连网互动的损耗,可是运动端浏览器和
webview 的宽容性帮忙却是个难点,举个例子 Android webview
必要固件4.4之上才支撑,iOS safari 8 以上也才支撑,而 UC
浏览器最近还不扶助。

而当前推向集体有着事务支撑 HTTPS 难度也是一对一高,部分 302
重定向也可以有比极大恐怕存在 SSLStrip,更并且 UC
浏览器还不帮助那些公约,很轻松通过 SSLStrip
实行威逼利用,即便运转商大多数景观下不会那样干,然而自个儿或然坚定疑心他们的节操。由于国内宽带互连网的基国内情,短期可望速度进步基本上不容许的,就算总理一句话,但哪个运维商不想致富?所以,业务属性的低沉和事务安全,需求打开权衡利弊。

Object.defineProperty

Object.defineProperty()
方法直接在二个目的上定义八个新属性,大概涂改三个早已存在的习性,
并重临那个目的。

Object.defineProperty(obj, prop, descriptor)

其中: 

  • obj –
    须求定义属性的靶子
  • prop –
    需被定义或改换的属性名
  • descriptor –
    需被定义或修改的性质的叙说符

咱俩得以选取如下的代码,让
call 和 apply 不或许被重写。

// 锁住 call
Object.defineProperty(Function.prototype, 'call', {
  value: Function.prototype.call,
  // 当且仅当仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变
  writable: false,
  // 当且仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除 
  configurable: false,
  enumerable: true
});
// 锁住 apply
Object.defineProperty(Function.prototype, 'apply', {
  value: Function.prototype.apply,
  writable: false,
  configurable: false,
  enumerable: true
}); 

为何要这么写吧?其实依旧与上文的 重写 setAttribute 有关。

就算大家将原始
Element.prototype.setAttribute
保存在了叁个闭包个中,不过还会有奇技淫巧能够把它从闭包中给“偷出来”。

试一下:

(function() {})(
    // 保存原有接口
    var old_setAttribute = Element.prototype.setAttribute;
    // 重写 setAttribute 接口
    Element.prototype.setAttribute = function(name, value) {
        // 具体细节
        if (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {}
        // 调用原始接口
        old_setAttribute.apply(this, arguments);
    };
)();
// 重写 apply
Function.prototype.apply = function(){
    console.log(this);
}
// 调用 setAttribute
document.getElementsByTagName('body')[0].setAttribute('data-test','123'); 

猜猜上边一段会输出什么?看看:
图片 4

乃至再次回到了原生
setAttribute 方法!

那是因为我们在重写 Element.prototype.setAttribute 时最后有 old_setAttribute.apply(this, arguments);这一句,使用到了
apply 方法,所以大家再重写 apply ,输出 this ,当调用被重写后的
setAttribute
就足以从中反向获得原生的被保存起来的 old_setAttribute 了。

如此大家地方所做的嵌套
iframe 重写 setAttribute 就毫无意义了。

采用方面包车型地铁 Object.defineProperty 能够锁死
apply 和 类似用法的 call
。使之无法被重写,那么也就无法从闭包上将我们的原生接口偷出来。这一年才算真的含义上的打响重写了大家想重写的习性。

监察数据阅览解析

对 zjcqoo
建议的三种防守措施的实践,前些时间首假诺花在优化检验脚本和增添白名单过滤脏数据方面,因为那块专门的学业只可以选取业余时间来搞,所以拖的时光稍微久。白名单那块的确是相比较麻烦,很多个人觉着深入分析下已知的域名就
ok 了,其实不然,云龙在这篇 iframe
黑魔法就关系移动端 Native 与 web
的通讯机制,所以在种种 应用程式 上,会有各个 iframe
的流入,并且是种种精彩纷呈的会谈地址,也包括 chrome。

督察得到的数目比非常多,不过,由于对一切广告注入黑产行业的面生,所以,有必要借助
google
举办搜寻商量,开掘,运转商大满世界狡滑,他们自身只会注入自个儿职业的广告,如
4G
免费换卡/送流量/送话费,可是商业广告那块翻糖蛋糕他们会拱手让人?答案是不恐怕,他们会勾结其余广告代理公司,利用他们的广告分发平台(运转商被美名称叫广告系统平台提供商)进行广告投放然后分成…

对于顾客起诉,他们平日都是认错,然后对这几个客商加白名单,可是他们对别的客户依然一而再作恶。对于商家地方的控诉,要是影响到她们的域名,假如你从未确切的凭据,他们就能够用各样借口摆脱自身的权利,如顾客手提式有线电话机中毒等等,若是你有可相信的凭据,还得是他俩运行商本身的域名照旧IP,不然他们也不或许管理。他们照旧一直以来的假说,客户手提式有线电话机中毒等等。

除非您把运转商的域名或 IP
监控数据列给她看,他才转变态度认错,不过那可是也是前面我们提到的流量话费广告,对于第三方广告经销商的广告,还是迫于解决,这一个第三方广告承包商有广告家、花生米、XX
传媒等等中型小型型广告商,当然也不消除,有的是“个体工商户广告商”。

从另一方面来看,由于使用的是古老的 http 公约,这种公然传输的说道,html
内容能够被运转商不言自明地记录下来,页面关键字、访谈时间、地域等客商标签都能够进行募集,聊到那,你恐怕已经知晓了二个事(隐衷入侵已经数见不鲜了)——大额解析+本性化推荐,在
google 一查,运行商还真有布置类似于 iPush
网络广告定向直投这样的系统,何况广告点击率也特别的高,不拔除会定向推送一些偏藏杏黄的图样或娱乐。

除此以外,数据分析中窥见有个别百度总括的接口央求,也在部分 js
样本中开采百度计算地址,猜度很有一点都不小概率是这种广告平台应用百度总计系统做多少解析,如定向投放用户PV 计算,广告效应计算等等。
监督数据剖判也扯这么多了,大家照旧回到看如何是好防止措施呢!

window.self

回到三个针对当前
window 对象的援用。

Content Security Policy(简称 CSP)

CSP
内容安全计谋,属于一种浏览器安全战略,以可靠白名单作机制,来界定网站中是不是足以分包某来源内容。包容性辅助同样是个难点,比如Android webview 需求固件4.4以上才支撑,iOS safari 6 以上帮衬,幸运的是
UC 浏览器近来支撑 1.0
战术版本,具体能够到 CANIUSE 驾驭。近些日子对
CSP 的利用唯有不到两周的阅历而已,上边轻松说说其优弱点。

缺点:

  1. CSP
    标准也正如繁琐,每体系型需求重新配置一份,暗许配置不可能接二连三,只好替换,那样会招致整个
    header 内容会大大扩展。
  2. 假设职业中有爬虫是抓取了表面图片的话,那么 img
    配置或者必要枚举各个域名,要么就相信全数域名。
    1. 挪动端 web app 页面,即使有存在 Native 与 web 的通讯,那么 iframe
      配置只可以信赖全部域名和协调了。
    1. 部分业务场景导致无能为力消除内联 script 的情状,所以只可以张开unsafe-inline
    1. 一部分库仍在行使 eval,所以免止误伤,也不得不展开 unsafe-eval
    1. 鉴于 iframe 信赖全体域名和情商,而 unsafe-inline
      开启,使得全体防备效用大大收缩

优点:

  1. 因此 connect/script 配置,大家得以决定什么
    外界域名异步须求能够发生,那无疑是大大的福音,就算内联 script
    被注入,异步须求依旧发不出,那样一来,除非攻击者把持有的 js
    都内联进来,不然注入的职能也运转不了,也爱莫能助总结功能如何。
  2. 经过 reportUri 能够总计到攻击类型和
    PV,只可是这几个接口的宏图无法自定义,上报的原委大多数都是鸡肋。
  3. object/media
    配置能够屏蔽部分表面多媒体的加载,不过那对于录像播放类的事情,也会贻误到。
  4. 当下 UC 浏览器 Android 版本的客户端和 web 端通讯机制都以运用职业的
    addJavascriptInterface 注入方式,而 酷派 版本已将 iframe
    通讯情势改成 ajax 情势(与页面同域,10.5
    全体制改善形成功),尽管是只重视 UC
    浏览器的职业,能够大胆放心使用,假诺是须要依赖于第三方平台,提出先打开reportOnly,将部分本土公约出席白名单,再完全开启防卫。

总的看吧,单靠 CSP
单打独斗明显是十一分,就算完全张开全数计谋,也无法成功搞定注入攻击,可是作为纵深防卫系统中的一道封锁防线,价值也是十一分实用的。

HTTP劫持、DNS劫持与XSS

先轻松讲讲怎么是
HTTP 威逼与 DNS 要挟。

肇事的运维商

是因为 xss 注入的限制太广,本文仅对网关威迫这一方面包车型地铁 XSS 注入实行座谈。
这里读者有个相当小的疑云,为何作者要选网关威逼实行研讨?因为网关威迫能够分布范围开展实用调节。

曾经,有那样一道风靡前端的面试题(当然小编也当场笔试过):当你在浏览器地址栏输入多少个U宝马7系L后回车,将会生出的事情?其实本文不珍贵央求发到服务端的求实进度,但是自个儿关切的时,服务端响应输出的文书档案,也许会在怎么着环节被注入广告?手机、路由器网关、互联网代理,还应该有拔尖运行商网关等等。所以,无论怎么着,任何网页都得经过运行商网关,並且最调(zui)皮(da)捣(e)蛋(ji)的,正是经过运转商网关。

除此以外,
也唤起大家,借使手提式有线电电话机安装了有个别上网加快软件、互连网代理软件或设置网络代理
IP,会有安全风险,也富含公开场馆/商家的无需付费 WIFI。

 

内联事件及内联脚本拦截

在 XSS
中,其实能够注入脚本的法子不少,特别是 HTML5
出来之后,一不细心,非常多的新标签都能够用于注入可实行脚本。

列出一部分相比分布的流入格局:

  1. <a href="javascript:alert(1)" ></a>
  2. <iframe src="javascript:alert(1)" />
  3. <img src='x' onerror="alert(1)" />
  4. <video src='x' onerror="alert(1)" ></video>
  5. <div onclick="alert(1)" onmouseover="alert(2)" ><div>

除去一些未列出来的相当的少见生僻的流入格局,超越二分一皆以 javascript:... 及内联事件 on*

咱俩只要注入已经产生,那么有未有法子拦截这个内联事件与内联脚本的推行呢?

对于地方列出的
(1) (5)
,这种必要顾客点击只怕实践某种事件随后才施行的脚本,我们是有法子开展防范的。

动态脚本拦截

地方使用
MutationObserver
拦截静态脚本,除了静态脚本,与之对应的正是动态变化的台本。

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.example.com/xss/b.js';

document.getElementsByTagName('body')[0].appendChild(script); 

要阻拦那类动态变化的台本,且拦截时机要在它插入
DOM
树中,实践从前,本来是能够监听 Mutation Events 中的 DOMNodeInserted 事件的。

 

MutationObserver

MutationObserver
是 HTML5 新添的 API,作用很强劲,给开垦者们提供了一种能在某些范围内的
DOM 树发生变化时作出确切反应的力量。

说的很美妙,大致的情致正是能够监测到页面
DOM 树的调换,并作出反应。

MutationObserver() 该构造函数用来实例化贰个新的Mutation观察者对象。

MutationObserver(
  function callback
);

目瞪狗呆,这一大段又是啥?意思正是MutationObserver
在观看时绝不开掘叁个新因素就立时回调,而是将贰个小时有个别里涌出的兼具因素,一同传过来。所以在回调中大家须求开展批量甩卖。何况,个中的 callback 会在钦赐的
DOM
节点(目的节点)发生变化时被调用。在调用时,观看者对象会传给该函数多少个参数,第多个参数是个包括了若干个
MutationRecord
对象的数组,第二个参数则是其一观望者对象自己。

从而,使用
MutationObserver
,大家能够对页面加载的每种静态脚本文件,进行监督检查:

// MutationObserver 的不同兼容性写法
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || 
window.MozMutationObserver;
// 该构造函数用来实例化一个新的 Mutation 观察者对象
// Mutation 观察者对象能监听在某个范围内的 DOM 树变化
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    // 返回被添加的节点,或者为null.
    var nodes = mutation.addedNodes;

    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      if (/xss/i.test(node.src))) {
        try {
          node.parentNode.removeChild(node);
          console.log('拦截可疑静态脚本:', node.src);
        } catch (e) {}
      }
    }
  });
});

// 传入目标节点和观察选项
// 如果 target 为 document 或者 document.documentElement
// 则当前文档中所有的节点添加与删除操作都会被观察到
observer.observe(document, {
  subtree: true,
  childList: true
});

能够见到如下:能够戳作者查看DEMO。(张开页面后展开调整台查看
console.log)

图片 5

<script type="text/javascript" src="./xss/a.js"></script> 是页面加载一上马就存在的静态脚本(查看页面结构),我们运用
MutationObserver
能够在剧本加载之后,推行在此之前这么些时刻段对其剧情做正则相配,开掘恶意代码则 removeChild() 掉,使之不能试行。

 

 

HTTP劫持

何以是HTTP劫持呢,大许多情形是营业商HTTP劫持,当大家选拔HTTP央浼央求叁个网址页面包车型大巴时候,互联网运行商会在正规的多少流中插入精心设计的网络数据报文,让顾客端(经常是浏览器)体现“错误”的数据,通常是某些弹窗,宣传性广告依然直接展现某网址的从头到尾的经过,大家应该都有相逢过。

HTTPS 与 CSP

末尾再简单谈谈
HTTPS 与
CSP。其实预防威胁最棒的不二等秘书诀依然从后端入手,前端能做的实在太少。并且由于源码的揭露,攻击者很轻巧绕过我们的守护花招。

window.top

回去窗口连串中的最顶层窗口的援用。

对于非同源的域名,iframe
子页面无法通过 parent.location 只怕 top.location
得到现实的页面地址,可是可以写入 top.location
,也正是能够操纵父页面包车型客车跳转。

三个属性分别能够又简写为 self 与 top,所以当开采大家的页面被嵌套在
iframe 时,能够重定向父级页面:

if (self != top) {
  // 我们的正常页面
  var url = location.href;
  // 父级页面重定向
  top.location = url;
}

  

 

重写 document.write

遵照上述的艺术,我们能够持续开采一下,还会有怎么着办法能够重写,以便对页面举行更加好的护卫。

document.write 是三个很科学选取,注入攻击者,平日会利用这几个格局,往页面上注入一些弹窗广告。

我们能够重写 document.write ,使用主要词黑名单对剧情开展相称。

什么样相比较切合当黑名单的重要字呢?大家能够看看一些广告比比较多的页面:

图片 6

此处在页面最尾部放置了一个iframe ,里面装了广告代码,这里的最外层的 id
id="BAIDU_SSP__wrapper_u2444091_0" 就很相符成为大家看清是还是不是是恶意代码的一个证明,纵然我们早就依据拦截上报采摘到了一批黑名单列表:

// 建立正则拦截关键词
var keywordBlackList = [
'xss',
'BAIDU_SSP__wrapper',
'BAIDU_DSPUI_FLOWBAR'
];

接下去大家只必要使用那一个首要字,对 document.write 传入的内容举办正则判别,就会鲜明是还是不是要阻止document.write 这段代码。 

```javascript
// 建立关键词黑名单
var keywordBlackList = [
  'xss',
  'BAIDU_SSP__wrapper',
  'BAIDU_DSPUI_FLOWBAR'
];

/**
 * 重写单个 window 窗口的 document.write 属性
 * @param  {[BOM]} window [浏览器window对象]
 * @return {[type]}       [description]
 */
function resetDocumentWrite(window) {
  var old_write = window.document.write;

  window.document.write = function(string) {
    if (blackListMatch(keywordBlackList, string)) {
      console.log('拦截可疑模块:', string);
      return;
    }

    // 调用原始接口
    old_write.apply(document, arguments);
  }
}

/**
 * [黑名单匹配]
 * @param  {[Array]} blackList [黑名单]
 * @param  {[String]} value    [需要验证的字符串]
 * @return {[Boolean]}         [false -- 验证不通过,true -- 验证通过]
 */
function blackListMatch(blackList, value) {
  var length = blackList.length,
    i = 0;

  for (; i < length; i++) {
    // 建立黑名单正则
    var reg = new RegExp(whiteList[i], 'i');

    // 存在黑名单中,拦截
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
} 

我们能够把 resetDocumentWrite 放入上文的 installHook 方法中,就能够对近年来window 及全数变化的 iframe 情状内的 document.write 进行重写了。

重写 setAttribute 与 document.write

发表评论

电子邮件地址不会被公开。 必填项已用*标注