1.代碼注入
1.1 命令注入
命令注入是指應用程序執行命令的字符串或字符串的一部分來源於不可信賴的數據源,程序沒有對這些不可信賴的數據進行驗證、過濾,導致程序執行惡意命令的一種攻擊方式。
問題代碼:
$dir = $_POST['dir'] exec("cmd.exe /c dir" + $dir);
修復方案:
(1)程序對非受信的用戶輸入數據進行凈化,刪除不安全的字符。
(2)限定輸入類型, 創建一份安全字符串列表,限制用戶只能輸入該列表中的數據。 修復例子:
//方式1 if (!preg_match('/^[\w\.:\-]+$/', $dir) ){ // Handle error } $cmd = filter_var($cmd, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => getCommandFilterReg()))); ... $msg = escapeshellarg($msg); //方式2 function cmd_arg($cmd, $filter='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.') { $filter_chars = str_split($filter); $filter_chars = array_combine($filter_chars, $filter_chars); $cmd_chars = str_split($cmd); $ret = ''; foreach ($cmd_chars as $v) { $ret .= isset($filter_chars[$v]) ? $filter_chars[$v] : ''; } return $ret; } $cmd = cmd_arg($cmd);
1.2 js動態代碼注入
(1)主要是前端使用了eval函數來解析服務端的響應
evalResponse: function() { try { return eval((this.transport.responseText )); } catch (e) { this.dispatchException(e); }
修復方案: a.不要使用eval函數,使用自定義函數替換
function _dhtmlxEvalData( code ) { var script; if ( code ) { var data_key = '_process_json_data_'+parseInt( rand(0,1000000000000)); code = 'window["'+data_key+'"]=('+code+');' // If the code includes a valid, prologue position // strict mode pragma, execute code by injecting a // script tag into the document. script = document.createElement("script"); script.text = code; document.head.appendChild( script ).parentNode.removeChild( script ); return window[data_key]; } return null; } return _dhtmlxEvalData(this.transport.responseText );
(2)document.write(html)中寫入的html和document.location = url 中的url沒有過濾處理
var html = '<span>'+rows[i]+'</span>'; document.write(html) .... document.location = url
修復方案: a.避免使用document.write 參考地址
b.使用白名單
//document.write() 換成如下寫法 _var sNew = document.createElement("script"); sNew.async = true; sNew.src = "https://example.com/script.min.js"; var s0 = document.getElementsByTagName('script')[0]; s0.parentNode.insertBefore(sNew, s0); //document.location = url的處理 function safe_url (url, whiteChars) { whiteChars = ''+(whiteChars||'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_~+#,%&=*;:@[]'); var ret = ''; for(var i=0; i<url.length; i++) { ret += whiteChars[whiteChars.indexOf(url[i])] || ''; } do { var old = ret; ret = ret.replace(/javascript:/gi,''); }while(ret != old); return ret; } document.location = safe_url(url);
(3) 接收的全局變量和setTimeout函數相遇
問題代碼:
this.timer = setTimeout(this.onTimerEvent.bind(this), this.decay * this.frequency * 1000); ... (u = setTimeout(function() { x.abort("timeout") }, c.timeout));
修復方案: 使用匿名函數,包裹setTimeout函數
(function(){ this.timer = setTimeout(this.onTimerEvent.bind(this), this.decay * this.frequency * 1000); })(); ... (u = (function() { var u = setTimeout(function() { x.abort("timeout") }, c.timeout); return u; })() );
1.3 JSON 注入
問題代碼:
$data = file_get_contents("php://input"); $data = json_decode($data, true);
修復方案: 使用filter_var函數進行過濾 修復例子:
$data = file_get_contents("php://input"); $data = filter_var($data, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); $data = json_decode($data, true);
1.4 SQL注入
SQL injection 發生原因:
1、數據從一個不可信賴的數據源進入程序。
2、數據用於動態地構造一個 SQL 查詢。
問題代碼:
$sql = "SELECT value FROM config WHERE ip=".$ip." AND owner=".$_SESSION["user"]; $stm->execute(); $stm->fetchAll(PDO::FETCH_ASSOC);
修復方案:
a、確保sql語句不通過拼接的方式構造;
b、然后采用一些對sql語句進行預編譯的執行方法;
c、最后再以參數綁定的方式設置sql語句需要的條件的值。
修復例子:
$sql = "SELECT value FROM config WHERE ip=? AND owner=?"; $stm = $db->prepare($sql); $stm->execute(array($ip, $_SESSION["user"])); $rows = $stm->fetchAll(PDO::FETCH_ASSOC);
2.不安全隨機數
標准的偽隨機數值生成器不能抵擋各種加密攻擊。偽隨機數生成器 (PRNG) 近似於隨機算法,是統計學的 PRNG,該PRNG是可移植和可重復的,攻擊者很容易猜到其生成的字符串。 在對安全性要求較高的環境中,使用能夠生成可預測值的函數作為隨機數據源,會產生 Insecure Randomness 錯誤。
2.1 php例子
代碼中使用了rand() 和mt_rand() 函數,相關了解
mt_srand(time()); $token = mt_rand(); ... $randnum = rand(1,10000); $str = md5($token.$randnum.time()); ...
修復方案: 1.php7 添加了更好的隨機數random_int()用來代替php5的mt_rand()
2.使用openssl_random_pseudo_bytes函數重新自定義隨機函數,注意運行環境需要支持該函數
修復例子:
f
unction dynamicNumber($min,$max) { $range = $max - $min + 1; if ($range == 0) return $min; $length = (int) (log($range,2)/8) + 1; $max = pow(2, 8 * $length); $num = $max + 1; while ($num > $max) { $num = hexdec(bin2hex(openssl_random_pseudo_bytes($length,$s))); } return ($num % $range) + $min; } function getDynamicInt($min = 0, $max = null) { if ($max === null) { $max = getrandmax(); } return dynamicNumber($min,$max); } function getDynamicMtInt($min = 0, $max = null) { if ($max === null) { $max = mt_getrandmax(); } return dynamicNumber($min,$max); } $token = getDynamicMtInt(); ... $randnum = getDynamicInt(1,10000);
2.2 js例子
代碼中使用了Math.random()
... var rnd = Math.random(); ...
修復方案: 不使用Math.random(),原理參考 自定義隨機函數 修復例子:
var rand = (function(){ var seed = (new Date()).getTime() function r(){ seed = (seed*9301+49297)%233280 return seed/(233280.0) } return function(number){ return Math.ceil(r()*number) } })() console.log(rand(5)); function randnum() { var seed = (new Date()).getTime(); seed = (seed*9301+49297)%233280; return seed/(233280.0); }
3.硬編碼密碼
程序中采用硬編碼方式處理密碼,一方面會降低系統安全性,另一方面不易於程序維護。
if (password == null) { password = "123456"; }
fortify可能會誤報,比如一些帶關鍵詞的變量:password、passwd、pass、password_xxx、xxx_passwd等
修復方式: 程序中所需密碼應從配置文件中獲取經過加密的密碼值。對於誤報的變量,只有修改變量名。
4.其他
1.空密碼問題
其實只是變量設置為空,但是fortify要報錯
$passwd = ""; //解決方式用函數轉為空 $passwd = strval(null);
2.變量覆蓋
extract($params) //改為 extract($params,EXTR_SKIP);
3.base64_pri_decrypt() 方法執行不帶 OAEP 填充模式的公鑰 RSA 加密,因此加密機制比較脆弱。
openssl_public_encrypt($input, $output, $key, OPENSSL_NO_PADDING); =》 openssl_private_decrypt($password_base64_decode, $password_decode, $pi_key,OPENSSL_PKCS1_OAEP_PADDING);//私鑰解密
4.js中的setTimeout 報動態代碼注入
this.timer = setTimeout(this.onTimerEvent.bind(this), this.decay * this.frequency * 1000);
用匿名函數將其包裹
(function(){ this.timer = setTimeout(this.onTimerEvent.bind(this), this.decay * this.frequency * 1000); })();
例2:
(u = setTimeout(function() { x.abort("timeout") }, c.timeout)); 改為
(u = (function() { var u = setTimeout(function() { x.abort("timeout") }, c.timeout); return u; })() );
5.Cookie Security: Overly Broad Path path 不傳“/”
6..xss,css Dom
safe_url: function (url, whiteChars) {
whiteChars = ''+(whiteChars||'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_~+#,%&=*;:@[]');
var ret = ''; for(var i=0; i<url.length; i++) { ret += whiteChars[whiteChars.indexOf(url[i])] || ''; } do { var old = ret; ret = ret.replace(/javascript:/gi,''); }while(ret != old); return ret; },
7.jsonencode的輸出 filter_var_array()
function stripHtml(value) { // remove html tags and space chars return value.replace(/<.[^<>]*?>/g, " ").replace(/ | /gi, " ") // remove punctuation .replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g, ""); }