您没有来错地!为了更好的发展,黑基网已于9月19日正式更名为【安基网】,域名更换为www.safebase.cn,请卸载旧的APP并安装新的APP,给您带来不便,敬请理解!谢谢

黑基Web安全攻防班
安基网 首页 IT技术 编程开发 查看内容

从Chrome源码看浏览器如何加载资源

2017-10-30 14:08| 投稿: lofor |来自: 互联网

摘要: 对浏览器加载资源有很多不确定性,例如:css/font的资源的优化级会比img高,资源的优化级是怎么确定的呢?资源优先级又是如何影响加载的先后顺序的?有几种情况可能会导致资源被阻止加载?通过源码可以找到答案。此 ...

对浏览器加载资源有很多不确定性,例如:

  1. css/font的资源的优化级会比img高,资源的优化级是怎么确定的呢?
  2. 资源优先级又是如何影响加载的先后顺序的?
  3. 有几种情况可能会导致资源被阻止加载?

通过源码可以找到答案。此次源码解读基于Chromium 64(10月28日更新的源码)。

下面通过加载资源的步骤,依次说明。

1. 开始加载

通过以下命令打开Chromium,同时打开一个网页:

chromium --renderer-startup-dialog https://www.baidu.com

Chrome会在DocumentLoader.cpp里面通过以下代码去加载:

 main_resource_ =
      RawResource::FetchMainResource(fetch_params, Fetcher(), substitute_data_);

页面资源属于MainRescouce,Chrome把Rescource归为以下几种:

 enum Type : uint8_t {
    kMainResource,
    kImage,
    kCSSStyleSheet,
    kScript,
    kFont,
    kRaw,
    kSVGDocument,
    kXSLStyleSheet,
    kLinkPrefetch,
    kTextTrack,
    kImportResource,
    kMedia,  // Audio or video file requested by a HTML5 media element
    kManifest,
    kMock  // Only for testing
  };

除了常见的image/css/js/font之外,我们发现还有像textTrack的资源,这个是什么东西呢?这个是video的字幕,使用webvtt格式:

<video controls poster="/images/sample.gif">
   <source src="sample.mp4" type="video/mp4">
   <track kind="captions" src="sampleCaptions.vtt" srclang="en">
</video>

还有动态请求ajax属于Raw类型。因为ajax可以请求多种资源。

MainResource包括location即导航输入地址得到的页面、使用frame/iframe嵌套的、通过超链接点击的页面以及表单提交这几种。

接着交给稍底层的ResourceFecher去加载,所有资源都是通过它加载:

fetcher->RequestResource(
      params, RawResourceFactory(Resource::kMainResource), substitute_data)

在这个里面会先对请求做预处理。

2. 预处理请求

每发个请求会生成一个ResourceRequest对象,这个对象包含了http请求的所有信息:



包括url、http header、http body等,还有请求的优先级信息等:



然后会根据页面的加载策略对这个请求做一些预处理,如下代码:

 PrepareRequestResult result = PrepareRequest(params, factory, substitute_data,
                                               identifier, blocked_reason);
  if (result == kAbort)
    return nullptr;
  if (result == kBlock)
    return ResourceForBlockedRequest(params, factory, blocked_reason);

prepareRequest会做两件事情,一件是检查请求是否合法,第二件是把请求做些修改。如果检查合法性返回kAbort或者kBlock,说明资源被废弃了或者被阻止了,就不去加载了。

被block的原因可能有以下几种:

enum class ResourceRequestBlockedReason {
  kCSP,              // CSP内容安全策略检查
  kMixedContent,     // mixed content
  kOrigin,           // secure origin
  kInspector,        // devtools的检查器
  kSubresourceFilter,
  kOther,
  kNone
};

源码里面会在这个函数做合法性检查:

 blocked_reason = Context().CanRequest(/*参数省略*/);
  if (blocked_reason != ResourceRequestBlockedReason::kNone) {
    return kBlock;
  }

CanRequest函数会相应地检查以下内容:

(1)CSP(Content Security Policy)内容安全策略检查

CSP是减少XSS攻击一个策略。如果我们只允许加载自己域的图片的话,可以加上下面这个meta标签:

<meta http-equiv="Content-Security-Policy" content="img-src 'self';">

或者是后端设置这个http响应头。

self表示本域,如果加载其它域的图片浏览器将会报错:



所以这个可以防止一些XSS注入的跨域请求。

源码里面会检查该请求是否符合CSP的设定要求:

 const ContentSecurityPolicy* csp = GetContentSecurityPolicy();
  if (csp && !csp->AllowRequest(
                 request_context, url, options.content_security_policy_nonce,
                 options.integrity_metadata, options.parser_disposition,
                 redirect_status, reporting_policy, check_header_type)) {
    return ResourceRequestBlockedReason::kCSP;
  }

如果有CSP并且AllowRequest没有通过的话就会返回堵塞的原因。具体的检查过程是根据不同的资源类型去获取该类资源资源的CSP设定进行比较。

接着会根据CSP的要求改变请求:

ModifyRequestForCSP(request);

主要是升级http为https。

(2)upgrade-insecure-requests

如果设定了以下CSP规则:

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

那么会将网页的http请求强制升级为https,这是通过改变request对象实现的:

     url.SetProtocol("https");
      if (url.Port() == 80)
        url.SetPort(443);
      resource_request.SetURL(url);

包括改变url的协议和端口号。

(3)Mixed Content混合内容block

在https的网站请求http的内容就是Mixed Content,例如加载一个http的JS脚本,这种请求通常会被浏览器堵塞掉,因为http是没有加密的,容易受到中间人的攻击,如修改JS的内容,从而控制整个https的页面,而图片之类的资源即使内容被修改可能只是展示出问题,所以默认没有block掉。源码里面会检查Mixed Content的内容:

 if (ShouldBlockFetchByMixedContentCheck(request_context, frame_type,
                                          resource_request.GetRedirectStatus(),
                                          url, reporting_policy))
    return ResourceRequestBlockedReason::kMixedContent;

在源码里面,以下4种资源是optionally-blockable(被动混合内容):

   // "Optionally-blockable" mixed content
    case WebURLRequest::kRequestContextAudio:
    case WebURLRequest::kRequestContextFavicon:
    case WebURLRequest::kRequestContextImage:
    case WebURLRequest::kRequestContextVideo:
      return WebMixedContentContextType::kOptionallyBlockable;

什么叫被动混合内容呢?W3C文档是这么说的:那些不会打破页面重要部分,风险比较低的,但是使用频率又比较高的Mixed Content内容。

而剩下的其它所有资源几乎都是blockable的,包括JS/CSS/Iframe/XMLHttpRequest等:



我们注意到img srcset里的资源也是默认会被阻止的,即下面的img会被block:

<img srcset="http://fedren.com/test-1x.png 1x, http://fedren.com/test-2x.png 2x" alt>

但是使用src的不会被block:

<img src="http://fedren.com/images/sell/icon-home.png" alt>

如下图所示:



这就是optionally-blockable和blocakable资源的区分。

对于被动混合内容,如果设置strick mode:

<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">

那么即使是optionally的也会被block掉:

   case WebMixedContentContextType::kOptionallyBlockable:
      allowed = !strict_mode;
      if (allowed) {
        content_settings_client->PassiveInsecureContentFound(url);
        client->DidDisplayInsecureContent();
      }
      break;

上面代码,如果strick_mode是true,allowed就是false,被动混合内容就会被阻止。

而对于主动混合内容,如果用户设置允许加载:



那么也是可以加载的:

   case WebMixedContentContextType::kBlockable: {
      // Strictly block subresources that are mixed with respect to their
      // subframes, unless all insecure content is allowed. This is to avoid the
      // following situation: https://a.com embeds https://b.com, which loads a
      // script over insecure HTTP. The user opts to allow the insecure content,
      // thinking that they are allowing an insecure script to run on
      // https://a.com and not realizing that they are in fact allowing an
      // insecure script on https://b.com.

      bool should_ask_embedder =
          !strict_mode && settings &&
          (!settings->GetStrictlyBlockBlockableMixedContent() ||
           settings->GetAllowRunningOfInsecureContent());
      allowed = should_ask_embedder &&
                content_settings_client->AllowRunningInsecureContent(
                    settings && settings->GetAllowRunningOfInsecureContent(),
                    security_origin, url);
      break;

代码倒数第4行会去判断当前的client即当前页面的设置是否允许加载blockable的资源。另外源码注释还提到了一种特殊的情况,就是a.com的页面包含了b.com的页面,b.com允许加载blockable的资源,a.com在非strick mode的时候页面是允许加载的,但是如果a.com是strick mode,那么将不允许加载。

并且如果页面设置了strick mode,用户设置的允许blockable资源加载的设置将会失效:

 // If we're in strict mode, we'll automagically fail everything, and
  // intentionally skip the client checks in order to prevent degrading the
  // site's security UI.
  bool strict_mode =
      mixed_frame->GetSecurityContext()->GetInsecureRequestPolicy() &
          kBlockAllMixedContent ||
      settings->GetStrictMixedContentChecking();

(4)Origin Block

这个主要是svg使用use的获取svg资源的时候必须不能跨域,如下以下资源将会被阻塞:

<svg>
    <use href="http://cdn.test.com/images/logo.svg#abc"></use>
</svg>

如下图所示:



并且控制台会打印:



源码里面会对这种use link加载的svg做一个检验:

小编推荐:欲学习电脑技术、系统维护、网络管理、编程开发和安全攻防等高端IT技术,请 点击这里 注册黑基账号,公开课频道价值万元IT培训教程免费学,让您少走弯路、事半功倍,好工作升职加薪!



免责声明:本文由投稿者转载自互联网,版权归原作者所有,文中所述不代表本站观点,若有侵权或转载等不当之处请联系我们处理,让我们一起为维护良好的互联网秩序而努力!联系方式见网站首页右下角。


鲜花

握手

雷人

路过

鸡蛋

相关阅读

发表评论

最新评论

最新

返回顶部