Skip to content

Instantly share code, notes, and snippets.

@retanoj
Last active July 18, 2019 03:32
Show Gist options
  • Save retanoj/04a120381d9ae5ff3a183b02114adccf to your computer and use it in GitHub Desktop.
Save retanoj/04a120381d9ae5ff3a183b02114adccf to your computer and use it in GitHub Desktop.
<?php
class AntiSSRF
{
private $timeout;
private $limit;
function __construct()
{
$this->timeout = 5; //默认5s超时
$this->limit = 3; //默认跳转3次
}
function setTimeout($var)
{
$this->timeout = $var;
}
function setJmpLimit($var)
{
$this->limit = $var;
}
/**
* @param string url地址
* @return array 'status'状态码 'location'当'status'为30X时为才存在,为跳转的URL, 'host'当请求为200时才有的变量
*/
private function getURLInfo($url)
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, TRUE);
curl_setopt($ch, CURLOPT_NOBODY, TRUE);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$result = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$ret = array();
$match = array();
$ret['status'] = intval($status);
if ($ret['status'] >= 300 && $ret['status'] < 400) {
preg_match("#location: ([^\s]*)#i", $result, $match);
if (substr($match[1], 0, 4) === 'http') {
$ret['location'] = $match[1];
} else {
$ret['location'] = $url . $match[1];
}
}
if ($ret['status'] == 200) {
$ret['host'] = $url;
}
curl_close($ch);
return $ret;
}
/**
* @param string url地址
* @return string 最终请求的IP地址,如失败则返回false
*/
private function getRealIP($url)
{
// 首次判断host的IP性质
$url_host = @parse_url($url)['host'];
if (empty($url_host)) {
return false;
}
$url_ip = gethostbyname($url_host);
if ($this->isInnerIP($url_ip)) {
return $url_ip;
}
$count = 0;
$info = $this->getURLInfo($url);
while ($count < $this->limit - 1 && $info['status'] >= 300 && $info['status'] < 400) {
$count++;
$info = $this->getURLInfo($info['location']);
}
if ($info['status'] >= 300 || $info['status'] < 200) { //大于$limit 次跳转 或 最后一次请求出错
return false;
}
// 判断请求后的host的IP性质
$host = @parse_url($info['host'])['host'];
if ($host == $url_host) {
return $url_ip;
}
$ip = gethostbyname($host);
return $ip;
}
/**
* @param string IP地址
* @return bool 是否为内网地址,是返回true,否返回false
*/
private function isInnerIP($ip_arg)
{
$ip = ip2long($ip_arg);
return ip2long('127.0.0.0') >> 24 === $ip >> 24 or \
ip2long('10.0.0.0') >> 24 === $ip >> 24 or \
ip2long('172.16.0.0') >> 20 === $ip >> 20 or \
ip2long('192.168.0.0') >> 16 === $ip >> 16;
}
/**
* @param string url地址
* @return bool 是否为合法的请求地址
*/
public function checkSafeUrl($url)
{
if (!preg_match('#^https?://[^\\\\]*/.*#', $url)) {
return false;
}
$ip = $this->getRealIP($url);
if (!$ip) {
return false;
}
if ($this->isInnerIP($ip)) {
return false;
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment