Safety design of php interface
The security of the interface is mainly designed around the three mechanisms of Token, Timestamp and ign, which ensure that the data of the interface will not be tampered with and called repeatedly. The following is specific:
(1)Token authorization mechanism: (Token is the credential of the client to access the server)--After the user logs in with the username password, the server returns a Token (usually a UUID) to the client and stores the Token-UserId in the cache server as a key-value pair.The server receives the request and performs Token authentication. If Token does not exist, the request is invalid.
(2) Timestamp timeout mechanism: (The signature mechanism ensures that the data will not be tampered with). Each request of the user is timestamped with the current time stamp. The server receives the timestamp and compares it with the current time. If the time difference is greater than a certain time (for example, 5 minutes), the request is considered invalid.The timestamp timeout mechanism is an effective means of defending against DOS attacks.
(3) Signature mechanism: Token and timestamp are added with other request parameters and encrypted with MD5 or SHA-1 algorithm (salt can be added as appropriate). The encrypted data is the signature sign of this request. The server receives the request and gets the signature with the same algorithm and compares it with the current signature. If different, the parameter is changed and the error identification is returned directly.
/** * @desc Accept parameter handling */ private function dealParam(){ //Accept header parameter--system parameter $systemParam=getAllHeadersParam(); //Accept body data -- business parameters (in json format) $data=file_get_contents('php://input'); //Read private key information in configuration file $api_apiKey=C('api_apiKey'); $privatekey=$api_apiKey[$systemParam['token']]; $arr['token'] =$systemParam['token']; //Server-side assigned identities (different clients need different identities) $arr['timestamp']=$systemParam['timestamp']; //Timestamp, UTC time, based on Beijing Time East Eight (+8) $arr['version'] =$systemParam['version']; //version number $arr['sign'] =$systemParam['sign']; //autograph $arr['source'] =$systemParam['source']; //Source (0-Android/1-IOS/2-H5/3-PC/4-php/5-java) $arr['data'] =json_decode($data,true); //Business parameter json format $arr['method'] =$data['method']; //Access interface, format: model name. method name return $arr; }
/* * @desc Get all header parameters starting with HTTP * @return array */ private function getAllHeadersParam(){ $headers = array(); foreach($_SERVER as $key=>$value){ if(substr($key, 0, 5)==='HTTP_'){ $key = substr($key, 5); $key = str_replace('_', ' ', $key); $key = str_replace(' ', '-', $key); $key = strtolower($key); $headers[$key] = $value; } } return $headers; }
/* * @desc Signature Verification * @param $token string Server-side assigned identities (different clients need different identities) * @param $timestamp string Timestamp, UTC time, based on Beijing Time East Eight (+8) * @param $version string version number * @param $sign string autograph * @param $source int Source (0-Android/1-IOS/2-H5/3-PC/4-php/5-java) * @param $privatekey string private key * @param $data Business parameter json format * @return bool */ private function checkAuth($token,$timestamp,$version,$sign,$source,$privatekey,$data){ //Parameter Judgment if(empty($token)){ E('token Can't be empty!'); } if(empty($timestamp)){ E('Timestamp cannot be empty!'); } if(empty($version)){ E('Version number cannot be empty!'); } if(empty($data)){ E('Business parameters cannot be empty!'); } if(empty($source) && $source<>'0'){ E('Source cannot be empty!'); } if(empty($sign)){ E('Signature cannot be empty!'); } if(empty($privatekey)){ E('The private key cannot be empty!'); } //Time Check $expire_second=C('expire_second',null,10); $timestamp_t=$timestamp+$expire_second; if($timestamp_t<time()){ E('Request has expired!'); } $public= D('public'); $datas=$this->original; //system parameter $paramArr=array( 'token'=>$token, 'timestamp'=>$timestamp, 'version'=>$version, 'source'=>$source, 'data'=>$data, ); //Stitch into strings by rules $str = $this->createSign($paramArr,$this->privatekey); if($str != $this->sign){ E('Verification error!'); } return true; }
sign generation rules and steps:
First step: Sort all request parameters (except empty parameter values, files, byte streams, sign s) from smallest to largest (dictionary order) by parameter name ASCII code.
Be careful:
l. Parameter name ASCII code sorting from smallest to largest (dictionary order);
l. Do not participate in signing if the value of the parameter is null;
l. File and byte streams do not participate in signing;
l sign does not participate in signing;
l. Parameter names and values are case sensitive;
(2) Step 2: Format the sorted parameters as URL key-value pairs (that is, key1=value1&key2=value2...)Stitch into string strA;
(3) Step 3: Stitch apiKey after strA to get striSignTemp string, convert strSignTemp string to lowercase string and MD5 operation. After MD5 operation, the value is passed to the server as sign value;
Example (all parameters, parameter values are examples, developer reference format is sufficient):
token: cd171009328172Ad3sc
apiKey: cd13H2ddd22212ds1da
First step (get the request parameters and sort them from smallest to largest by parameter name ASCII code):
token=cd173309328172Ad322
data={"userName":"18817201899",goods:["addrId":323,{"skuNo":"p12232-023","count":3},{"skuNo":"p12232-013","count":1}]}
timestamp=1507537036
version=v3.6.0
(2) Step 2 (stitching into string strA according to rules):
token=cd171009328172Ad3sc&data={"userName":"18817201899",goods:["addrId":323,{"skuNo":"p12232-023","count":3},{"skuNo":"p12232-013","count":1}]}timestamp=1507537036&version=v3.6.0
Third step (generating sign):
1) The string strSignTemp to be signed:
token=cd171009328172Ad3sc&data={"userName":"18817201899",goods:["addrId":323,{"skuNo":"p12232-023","count":3},{"skuNo":"p12232-013","count":1}]}timestamp=1507537036&version=v3.6.0cd13H2ddd22212ds1da
2) Convert to lowercase string
strtolower()
3)MD5 encrypted ciphertext
6D556D52822658FD47F7FE362544CEE1
/* * @desc Signature function * @param $paramArr system parameter * @param $apiKey private key * @return string Return Signature */ private function createSign ($paramArr,$apiKey) { ksort($paramArr); $sign=''; foreach ($paramArr as $key => $val) { if ($key != '' && $val != '') { $sign .= $key."=".$val."&"; } } $sign=rtrim($sign,"&"); $sign.=$apiKey; $sign=strtolower($sign); $sign = md5($sign); return $sign; }
(4) Reject duplicate calls: when the client first accesses, the signature is stored in the cache server and the timeout time is set to be the same as the timestamp timeout time, which guarantees that the timestamp or external URL can only be accessed once.If someone accesses again using the same URL and finds that the signature already exists on the cache server, the service is denied.If a signature in the cache fails and someone accesses it again using the same URL, it will be blocked by the timestamp timeout mechanism.This is why the timestamp timeout is required to be set to match the timestamp timeout.Reject duplicate invocation mechanisms to ensure that URLs are intercepted and unusable (such as grabbing data).
/** * @desc Limit number of request interfaces * @return bool */ private function ask_count(){ $client_ip = $this->sys_get_client_ip(); $ask_url = $this->sys_GetCurUrl(); //Limit number of times $limit_num = C('api_ask_limit',null,5); //Effective time in seconds $limit_time = C('api_ask_time'); $now_time = time(); $valid_time = $now_time - $limit_time; $ipwhere['creatime'] = array('EGT',date('Y-m-d H:i:s',$valid_time)); $ipwhere['ip_name'] = $client_ip; $ipwhere['ask_url'] = $ask_url; $check_result = M('log_ip_ask')->where($ipwhere)->count(); if($check_result !=='0'){ if($check_result >= $limit_num){ E('The limit has been exceeded!'); } } //Perform Insertion $add_data = array( 'ip_name'=>$client_ip, 'ask_url'=>$ask_url, 'creatime'=>date('Y-m-d H:i:s',time()) ); $result = M('log_ip_ask')->data($add_data)->add(); if($result===false){ E('Write record failed!'); } return true; }
/** * Get Client IP Address * @param integer $type Return type 0 Return IP address 1 Return IPV4 address number * @param boolean $adv Is advanced mode acquisition possible (possibly disguised) * @return mixed */ private function sys_get_client_ip($type = 0,$adv=false) { $type = $type ? 1 : 0; static $ip = NULL; if ($ip !== NULL) return $ip[$type]; if($adv){ if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $pos = array_search('unknown',$arr); if(false !== $pos) unset($arr[$pos]); $ip = trim($arr[0]); }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; }elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } }elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } // IP Address Legal Authentication $long = sprintf("%u",ip2long($ip)); $ip = $long ? array($ip, $long) : array('0.0.0.0', 0); return $ip[$type]; } /** * @desc php Get the full url address of the current access * @return string */ private function sys_GetCurUrl() { $url = 'http://'; if (isset ( $_SERVER ['HTTPS'] ) && $_SERVER ['HTTPS'] == 'on') { $url = 'https://'; } if ($_SERVER ['SERVER_PORT'] != '80') { $url .= $_SERVER ['HTTP_HOST'] . ':' . $_SERVER ['SERVER_PORT'] . $_SERVER ['REQUEST_URI']; } else { $url .= $_SERVER ['HTTP_HOST'] . $_SERVER ['REQUEST_URI']; } return $url; }
Illegal ip restrictions on access, which are typically used in interface calls between servers
// IP List Allowed Access private $ip_allow = array( '111.11.111.111', // LAN ip '111.11.111.112', // Task Server '111.11.111.113', // Proxy IP ); /** * @desc Illegal IP Restrict Access * @param array $config * @return bool */ private function illegalip(){ if(!$this->ip_limit){ return true; } $remote_ip = get_client_ip(); if(in_array($remote_ip, $ip_allow)){ return true; } return false; }