OPY-bbt / OPY-bbt.github.io

my webpage
https://opy-bbt.github.io/
0 stars 0 forks source link

浏览器同源策略 #2

Open OPY-bbt opened 5 years ago

OPY-bbt commented 5 years ago

作用

限制从一个源中加载的文档和脚本如何访问其他源的资源

定义

具有相同的协议、域名、端口号(默认80)的URL Same-origin policy for file: URIs 有些不同只有当起源的文件所在的父目录是目标文件的祖先目录时,才算同源。例如: 起源文件 /home/user/foo.html 和目标文件 /home/user/subdir/bar.html 被视为同源。而起源文件 /home/user/subdir/foo.html 和 /home/user/bar.html则不是同源。

源的继承

about:blank 页面会继承打开该URL的文档的源,例如利用window.open()打开一个新页面,新页面的document.domain 和之前的页面一样。

IE 的特殊性

OPY-bbt commented 5 years ago

改变源

一个页面可以改变源,但是有些限制。只能将document.domain更改为当前域或者当前域的超级域。如果设置为超级域,则其会被用于同源检查。 例如 http://store.company.com/dir/other.html 通过设置 document.domain = "company.com";则会被认为和 http://company.com/dir/page.html 同源。但是 company.com 不能设置为othercompany.com。 这里需要的是,只有两个页面同时设置了document.domain才会认定为同源,比如上面的栗子,http://company.com/dir/page.html也需要执行document.domain = "company.com",表示自己接受同源。 是为了防止站点成为他的子域的xss攻击对象。因为子域可以将domain设置为超级域。 任何对document.domain的调用都会将port重置为null(包括 document.domain = document.domain),这也和上面一点相辅相成。

OPY-bbt commented 5 years ago

XSS攻击

Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。 为了和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS。 XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。

分类

预防

OPY-bbt commented 5 years ago

跨域的网络访问

跨域的分类

跨域资源访问的栗子

如何允许跨域

CORS(Cross-Origin Resource Sharing)

如何阻止跨域访问

OPY-bbt commented 5 years ago

CORS preflight

simple request

不会触发preflight的请求被称为简单请求(不属于fetch的术语,其单独定义了CORS)。 需满足一下几个条件

preflight request

此情况下会先发送options请求来判断服务器是否允许该实际请求。可以避免跨域请求对服务器的用户数据产生未预期的影响。 客户端会先发送options请求,其头部有Access-Control-Request-Method,Access-Control-Request-Headers 来表是真实的请求的方法和其带的自定义首部字段。服务器据此判断是否允许请求。服务器返回的头部带有 Access-Control-Allow-Methods表示支持的方法。Access-Control-Allow-Headers 允许的头部字段 Access-Control-Max-age 表示预检请求的有效时间,在有效时间内,无需再发起预检请求。

大部分浏览器不支持预检请求的重定向

OPY-bbt commented 5 years ago

fetch

为了统一web平台的数据获取,fetch规范取代了许多算法和规范。

规范中指出

规范里fetch是支持progress的

function consume(reader) {
  var total = 0
  return pump()
  function pump() {
    return reader.read().then(({done, value}) => {
      if (done) {
        return
      }
      total += value.byteLength
      log(`received ${value.byteLength} bytes (${total} bytes in total)`)
      return pump()
    })
  }
}

fetch("/music/pk/altes-kamuffel.flac")
  .then(res => consume(res.body.getReader()))
  .then(() => log("consumed the entire body without keeping the whole thing in memory!"))
  .catch(e => log("something went wrong: " + e))
OPY-bbt commented 5 years ago

Abusing HTTP Status Codes to Expose Private Information. 文章里提出可以通过在gmail里上传一张图片,设置为对所有人可见后,就可以在网页上通过添加这张图片来判断用户是否登录了gmail.登录了图片可以加载,否则不能。twitter和facebook则是通过返回http status不同来区别。

OPY-bbt commented 5 years ago

CORS 解决跨域问题demo

client

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => res.send(`
  <!DOCTYPE html>
    <html>
      <body>
        <button id="btn1">blocked by CORS policy</button>
        <button id="btn2">CORS</button>
        <button id="btn3">CORS preflight</button>
      </body>
      <script>
        document.body.querySelector('#btn1').onclick = () => {
          fetch('http://localhost:3001/api/blocked').then(res => res.json()).then(val => {
            console.log(val);
          })
        }
        document.body.querySelector('#btn2').onclick = () => {
          fetch('http://localhost:3001/api/cors').then(res => res.json()).then(val => {
            console.log(val);
          })
        }
        document.body.querySelector('#btn3').onclick = () => {
          fetch('http://localhost:3001/api/cors', { headers: { 'content-type': 'application/json' } }).then(res => res.json()).then(val => {
            console.log(val);
          })
        }
      </script>
    </html>
  </DOCTYPE>
`))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

server

const express = require('express')
const app = express()
const port = 3001

var myLogger = function (req, res, next) {
  console.log(req.method, req.headers);
  next()
}

app.use(myLogger)

app.get('/api/blocked', (req, res) => {
  res.send(JSON.stringify({ name: 'zy' }))
});

app.options('/api/cors', (req, res) => {
  res.append('access-control-allow-origin', 'http://localhost:3000');
  res.append('access-control-allow-methods', 'GET');
  res.append('access-control-allow-headers', 'content-type');
  res.send();
});

app.get('/api/cors', (req, res) => {
  res.append('access-control-allow-origin', 'http://localhost:3000');
  res.append('content-type', 'application/json');
  res.send(JSON.stringify({ name: 'zy' }))
});

app.listen(port, () => console.log(`Example app listening on port ${port}!`))
OPY-bbt commented 5 years ago

通过改变hash来传递数据,利用hashchange解决跨域问题。a->c->b->a

OPY-bbt commented 5 years ago

浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。具体来说,当iframe切换src时,window.name会被保留下来。举个例子,a,b同域,先在a html里iframe引入c,c中设置window.name,然后将a中iframe换到b,此时window.name会被保留下来,因为a, b同域,最终间接获取到了c中设置的window.name属性。

OPY-bbt commented 5 years ago

HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)

p2c document.body.querySelector('iframe').contentWindow.postMessage("msg from p", ""); c2p parent.postMessage('msg from c', '');

OPY-bbt commented 5 years ago

JSONP 解决跨域问题

client

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => res.send(`
  <!DOCTYPE html>
    <html>
      <body>
        <button id="btn">send jsonp</button>
      </body>
      <script>
        function addScriptTag(src) {
          var script = document.createElement('script');
          script.setAttribute("type","text/javascript");
          script.src = src;
          document.body.appendChild(script);
        }

        window.onload = function() {
          addScriptTag('http://localhost:3001/api/jsonp?callback=log');
        }

        const log = ({ name }) => {
          console.log(name);
        }
      </script>
    </html>
  </DOCTYPE>
`))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

server

const express = require('express')
const app = express()
const port = 3001

app.get('/api/jsonp', (req, res) => {
  const { callback } = req.query;

  res.send(`${callback}({ name: 'zy' })`);
})

app.listen(port, () => console.log(`Example app listening on port ${port}!`))
OPY-bbt commented 5 years ago

Cross Site Request Forgery 跨站请求伪造

CSRF 攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点

预防