Can ads on a page read my password?

Nothing prevents ads from reading your passwords.

Ads (or any other script like analytics or JavaScript libraries) have access to the main JavaScript scope, and are able to read a lot of sensitive stuff: financial information, passwords, CSRF tokens, etc.

Well, unless they're being loaded in a sandboxed iframe.

Loading an ad in a sandboxed iframe will add security restrictions to the JavaScript scope it has access to, so it won't be able to do nasty stuff.

Unfortunately, most of the third-party scripts are not sandboxed. This is because some of them require access to the main scope to work properly, so they're almost never sandboxed.


As a developer, what can I do?

Since any third-party script could compromise the security of all you personal data, all sensitive pages (like login forms or checkout pages) should be loaded on their own origin (a subdomain is fine).

Using another origin allows us to profit from the Same-Origin Policy: scripts running on the main origin can't access anything on the protected origin.

Note: Content Security Policy and Subresource Integrity could also be used if the third-party can be easily reviewed, but most ad networks couldn't work anymore if you used them.


That depends on how the website loads the ads.

In the case of goodreads, their HTML contains javascript from the ad provider. Specifically, lines 81-145 of the HTML document returned by https://www.goodreads.com/ read:

<script>
  //<![CDATA[
    var gptAdSlots = gptAdSlots || [];
    var googletag = googletag || {};
    googletag.cmd = googletag.cmd || [];
    (function() {
      var gads = document.createElement("script");
      gads.async = true;
      gads.type = "text/javascript";
      var useSSL = "https:" == document.location.protocol;
      gads.src = (useSSL ? "https:" : "http:") +
      "//securepubads.g.doubleclick.net/tag/js/gpt.js";
      var node = document.getElementsByTagName("script")[0];
      node.parentNode.insertBefore(gads, node);
    })();
    // page settings
  //]]>
</script>
<script>
  //<![CDATA[
    googletag.cmd.push(function() {
      googletag.pubads().setTargeting("sid", "osid.bd63050e605ccee9f21515a2dedfdaea");
    googletag.pubads().setTargeting("grsession", "osid.bd63050e605ccee9f21515a2dedfdaea");
    googletag.pubads().setTargeting("surface", "desktop");
    googletag.pubads().setTargeting("signedin", "false");
    googletag.pubads().setTargeting("gr_author", "false");
    googletag.pubads().setTargeting("author", []);
      googletag.pubads().enableAsyncRendering();
      googletag.pubads().enableSingleRequest();
      googletag.pubads().collapseEmptyDivs(true);
      googletag.pubads().disableInitialLoad();
      googletag.enableServices();
    });
  //]]>
</script>
<script>
  //<![CDATA[
    ! function(a9, a, p, s, t, A, g) {
      if (a[a9]) return;

      function q(c, r) {
        a[a9]._Q.push([c, r])
      }
      a[a9] = {
      init: function() {
        q("i", arguments)
      },
      fetchBids: function() {
        q("f", arguments)
      },
      setDisplayBids: function() {},
        _Q: []
      };
      A = p.createElement(s);
      A.async = !0;
      A.src = t;
      g = p.getElementsByTagName(s)[0];
      g.parentNode.insertBefore(A, g)
    }("apstag", window, document, "script", "//c.amazon-adsystem.com/aax2/apstag.js");

    apstag.init({
      pubID: '3211', adServer: 'googletag', bidTimeout: 4e3
    });
  //]]>
</script>

As a consequence, the advertizer's javascript code runs in the same execution context as the website itself, and can do everything the website can, including observing all your interactions with the website.

If they had instead loaded the ads by embedding an iframe from a different origin, the advertizer's code would have run in its own execution context, and the browser would have blocked access to the surrounding website as a violation of the same origin policy.

In general, the only way to tell whether the website has isolated the advertizer's code is to inspect the code of the website.