时隔一年!一年啊!我又回来啦!!!
这一年生活节奏也是过得贼快贼快的,面试、转正、职称评定、接二连三的项目等等,都不是我停更的理由,其实呢,主要是。。。。我。。。换手机+忘记密码了[汗]。
经过这教训以后,很好!神马东西都绑定了,下次忘密码就容易找回来点吧~哈哈
是不是又碰到久违的话痨了~行行行!我马上写正文!好久不回来了,总得让我唠嗑两句吧~
由于都是边学边做的,因此这块我都会定期更新续集,还有一个就是这种安全理念不管啥语言都是通用的,我最近做PHP,就用PHP来做栗子了,以后用Java啊,Py啊神马的时候再写,那就是内个语言的源码了,希望大伙儿看了思路以后能够跨一下语言,移植一下,相信都是不难哒~
咱先说个思路吧,也不耽误大神的时间,这篇文讲的是web应用开发这块的两个软件层面的客户端安全校验,一个是请求类型的锁定,另一个是严格访问来源校验。
顾名思义啊,就是检验GET和POST,再加一个Ajax。
为什么要检验呢!?
这个其实是一个软件层面比较基本的安全校验,咱打个比方吧,比如下面写到的一个Ajax:
$.ajax({
url: baseURL + "AjaxGetSomeParams",
type: "POST",
data: param,
async: false,
headers: {
"content-type": "application/x-www-form-urlencoded Access-Control-Allow-Origin"
},
success: function (data, status) {//如果调用php成功
var result = eval("(" + data + ")");
// do something X1 ...
},
error: function (data) {
// do something X2 ...
},
complete: function (XMLHttpRequest, status) {
// do something X3 ...
}
});
我们需要Ajax从服务器获取数据,之后调整页面内的一些布局,然而这点就会被爬虫er看上了,拼命的在URL上进行访问AjaxGetSomeParams页面,哦豁,糟了,神马数据都被爬完了,这时候如果咱的软件能够检测出AjaxGetSomeParams页面是否是通过Ajax来访问的,就可以暂且规避一些诸如上所述的情况了。
下面放这个请求类型锁定的源码,有部分内容需要关联上下文的,我在文末放上整个文章的工具类出来就可以啦~忘了说明一下,是CI框架的,其实都是通用的,无碍~无碍哈~
/**
* 描述:
* 检查是否是对应的访问类型,若不是,则跳转警告页面
* 该是GET的就GET,该POST的就POST,Ajax的就Ajax,检查的是混淆安全
* 参数:
* $True_Type:正确的访问方式:0 -> GET访问,1 -> POST访问,2 -> Ajax访问
* 返回:
* 访问方式正确->true,访问方式错误->false
* 用法:
* // [$True_Type]访问类型安全检查
* $this -> load -> library('safety');
* if(!$this -> safety -> NotSafetyVisitCheck([$True_Type])) return;
*/
public function NotSafetyVisitCheck($True_Type){
if($True_Type == 0) {
// 非GET的异常请求处理
if(!IS_GET) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!干嘛呢";
$data['content'] = $ip."童鞋,你咋想的通过这个方式来看这个页面哦~
这链接只能GET请求的哦!";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
} else if ($True_Type == 1) {
// 非POST的异常请求处理
if(!IS_POST) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!干嘛呢";
$data['content'] = $ip."童鞋,你咋想的通过这个方式来看这个页面哦~
这链接只能POST请求的哦!";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
} else if ($True_Type == 2) {
// 非Ajax或POST的异常请求处理
if(!IS_AJAX || !IS_POST) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
echo $ip."童鞋,你咋想的通过这个方式来看这个页面哦~
这链接只能Ajax请求的哦!";
return false;
}
} else {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!干嘛呢";
$data['content'] = $ip."童鞋,你你你这想法很危险呀~快联系我,咱来相互学习呀~";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
return true;
}
聪明的童鞋估计看了上面这个就有点不下去了,这很明显有个BUG嘛,比如爬虫er写个页面,页面里面加个Ajax,然后通过Ajax的success回调把获取到的内容再显示出来不就照样拿得到嘛,嘿嘿~接下来的这个方法就是解决这个问题的啦~
其实呢,我感觉这个也可以叫做防跨域,只不过跨域好多地方都用得上,已经搞不清楚定义了,我们先理解下这个方法的思路!
这个方法通过校验当前访问来源的URL地址,如果是Ajax的话还要加上Ajax上一级访问来源,即Ajax请求所在页面的URL地址,通过字符串比对,把理论上访问URL和实际访问URL进行比对,进而达到防止这类型的爬虫~
同样是CI的源码,理解思路了都是可以通用的
/**
*描述:
* 检查是否是跨域请求,若是,则跳转警告页面
* 该是我这个域名的接口就是只能我这个域名来访问,外来域名的全都是想来坑我数据的
* 参数:
* $True_Uper:正确的上一页URL地址(若非Ajax则可以为空)
* $True_Thiser:正确当前页URL地址
* $Is_Ajax:是(true)否(false)应是Ajax访问
* 返回:
* 合法请求->true,非法跨域请求->false
* 用法:
* $this -> load -> library('safety');
* if(!$this -> safety -> NoCrossDomainAccess([$True_Uper], [$True_Thiser], [$Is_Ajax])) return;
*/
public function NoCrossDomainAccess($True_Uper, $True_Thiser, $Is_Ajax){
// 访问源是Ajax,则肯定有发起页和Ajax请求URL
if($Is_Ajax) {
// 当前访问的URL地址
$thisUrl = $this::curPageURL();
// stripos(a, b); 查找字符串b在字符串a中第一次出现的位置
// 若当前URL和真实URL不符,或当前页面的上一页URL和真实上一页URL不符,则判断是跨域请求
if(!isset($_SERVER['HTTP_REFERER']) || stripos($_SERVER['HTTP_REFERER'], $True_Uper) === false || !isset($thisUrl) || stripos($thisUrl, $True_Thiser) === false) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
echo $ip."童鞋,干嘛这么认真冲动呢!?
为何要跨Ajax来请求呢?";
return false;
}
} else { // 访问源不是Ajax,则只有发起页的POST或GET
// 当前访问的URL地址
$thisUrl = $this::curPageURL();
// stripos(a, b); 查找字符串b在字符串a中第一次出现的位置
// 若当前URL和真实URL不符,或当前页面的上一页URL和真实上一页URL不符,则判断是跨域请求
if(stristr($thisUrl, $True_Thiser) === false) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!友仔!";
$data['content'] = $ip."童鞋,干嘛这么认真冲动呢!?
为何要跨POST/GET来请求呢?";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
}
return true;
}
哎~其实讲这么多还不如撸源码,工具类源码奉上!注释管饱!
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* 用以区分AJax还是POST的导入
* if(IS_AJAX) { ... }
* if(IS_POST) { ... }
* 或者
* if(IS_AJAX) { ... }
* else { ... }
*/
define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
define('IS_POST', strtolower($_SERVER['REQUEST_METHOD']) == 'post');
define('IS_GET', strtolower($_SERVER['REQUEST_METHOD']) == 'get');
/**
* Created by DNYY
* User: DarkNessYY
* Content: 这是专门用来封装检查客户端安全方法的一个校验工具类
*/
class Safety {
/**
* 描述:
* 检查是否是对应的访问类型,若不是,则跳转警告页面
* 该是GET的就GET,该POST的就POST,Ajax的就Ajax,检查的是混淆安全
* 参数:
* $True_Type:正确的访问方式:0 -> GET访问,1 -> POST访问,2 -> Ajax访问
* 返回:
* 访问方式正确->true,访问方式错误->false
* 用法:
* // [$True_Type]访问类型安全检查
* $this -> load -> library('safety');
* if(!$this -> safety -> NotSafetyVisitCheck([$True_Type])) return;
*/
public function NotSafetyVisitCheck($True_Type){
if($True_Type == 0) {
// 非GET的异常请求处理
if(!IS_GET) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!干嘛呢";
$data['content'] = $ip."童鞋,你咋想的通过这个方式来看这个页面哦~
这链接只能GET请求的哦!";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
} else if ($True_Type == 1) {
// 非POST的异常请求处理
if(!IS_POST) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!干嘛呢";
$data['content'] = $ip."童鞋,你咋想的通过这个方式来看这个页面哦~
这链接只能POST请求的哦!";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
} else if ($True_Type == 2) {
// 非Ajax或POST的异常请求处理
if(!IS_AJAX || !IS_POST) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
echo $ip."童鞋,你咋想的通过这个方式来看这个页面哦~
这链接只能Ajax请求的哦!";
return false;
}
} else {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!干嘛呢";
$data['content'] = $ip."童鞋,你你你这想法很危险呀~,来相互学习呀~";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
return true;
}
/**
* 描述:
* 检查是否是跨域请求,若是,则跳转警告页面
* 该是我这个域名的接口就是只能我这个域名来访问,外来域名的全都是想来坑我数据的
* 参数:
* $True_Uper:正确的上一页URL地址(若非Ajax则可以为空)
* $True_Thiser:正确当前页URL地址
* $Is_Ajax:是(true)否(false)应是Ajax访问
* 返回:
* 合法请求->true,非法跨域请求->false
* 用法:
* $this -> load -> library('safety');
* if(!$this -> safety -> NoCrossDomainAccess([$True_Uper], [$True_Thiser], [$Is_Ajax])) return;
*/
public function NoCrossDomainAccess($True_Uper, $True_Thiser, $Is_Ajax){
// 访问源是Ajax,则肯定有发起页和Ajax请求URL
if($Is_Ajax) {
// 当前访问的URL地址
$thisUrl = $this::curPageURL();
// stripos(a, b); 查找字符串b在字符串a中第一次出现的位置
// 若当前URL和真实URL不符,或当前页面的上一页URL和真实上一页URL不符,则判断是跨域请求
if(!isset($_SERVER['HTTP_REFERER']) || stripos($_SERVER['HTTP_REFERER'], $True_Uper) === false || !isset($thisUrl) || stripos($thisUrl, $True_Thiser) === false) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
echo $ip."童鞋,干嘛这么认真冲动呢!?
为何要跨Ajax来请求呢?";
return false;
}
} else { // 访问源不是Ajax,则只有发起页的POST或GET
// 当前访问的URL地址
$thisUrl = $this::curPageURL();
// stripos(a, b); 查找字符串b在字符串a中第一次出现的位置
// 若当前URL和真实URL不符,或当前页面的上一页URL和真实上一页URL不符,则判断是跨域请求
if(stristr($thisUrl, $True_Thiser) === false) {
$CI = &get_instance();
$CI->load->helper('captcha');
$ip = $CI->input->ip_address();
$data['title'] = "嘿!友仔!";
$data['content'] = $ip."童鞋,干嘛这么认真冲动呢!?
为何要跨POST/GET来请求呢?";
$CI->load->vars($data);
// 绑定显示的视图
$CI->load->view('phone\templates\header');
$CI->load->view('phone\email_check_finish');
$CI->load->view('phone\templates\footer');
return false;
}
}
return true;
// $_SERVER['HTTP_REFERER']当前页面的前一个页面地址,$this::curPageURL()是当前页面的地址
// echo $_SERVER['HTTP_REFERER']."|".$this::curPageURL();
}
/**
* 描述:
* 获取当前页面的完整URL路径
* 参数:
* 无
* 返回:
* 当前页面的完整URL,带HTTP头、参数等
* 用法:
* 直接调用获取返回值即可
*/
function curPageURL() {
$pageURL = 'http';
if (isset( $_SERVER["HTTPS"] ) && strtolower( $_SERVER["HTTPS"] ) == "on") {
$pageURL .= "s";
}
$pageURL .= "://";
if ($_SERVER["SERVER_PORT"] != "80") {
$pageURL .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
} else {
$pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
}
return $pageURL;
}
}
?>
写完收工!久不发文,甚是想念,希望大扎多多支持,三口再三口!