PHP开发分页SERP时urlencode参数错误及XSS防护函数用法咨询
报错原因
核心错误是变量覆盖:你最初从$_GET中获取的$col是字符串类型的查询列参数,后续执行数据库查询时,用$col = mysqli_fetch_array($result)将该变量覆盖为了查询结果数组,在构造分页参数时调用urlencode($col)传入了数组,因此触发类型错误警告。
安全函数使用错误修正
三个函数的使用场景和你的错误点如下:
urlencode():仅用于编码URL查询参数的值部分,你原本对$search/$tbl/$col参数值编码的逻辑是正确的,不要修改查询参数值之外的部分。rawurlencode():用于编码URL的路径部分,你对当前文件名编码的逻辑符合使用场景,无需调整。htmlentities():用于对输出到HTML页面的内容做转义,需要在内容最终输出到HTML标签/属性的时机调用,你提前对拼接中的查询字符串调用属于时机错误。另外所有用户可控的输出内容(搜索结果的标题、描述、URL等)都需要调用该函数转义,防范XSS注入。
其他存在的问题
- 未对
$_GET参数做存在性校验和类型转换,用户未传对应参数时会抛出Notice错误,$page/$max需要强制转为整数,避免运算和SQL拼接出错。 - 搜索结果输出环节未做XSS转义,恶意内容被渲染时会触发注入风险。
- LIMIT子句的参数直接拼接SQL,虽然是数值类型仍建议提前做强制类型转换。
修改后完整代码
<?php //ERROR REPORTING FOR DEVMODE ONLY. ini_set('display_errors','1'); ini_set('display_startup_errors','1'); error_reporting(E_ALL); //MYSQLI CONNECTION. mysqli_report(MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT); $server = 'localhost'; $user = 'root'; $password = ''; $database = 'brute'; if(!$conn = mysqli_connect($server,$user,$password,$database)) { echo 'Mysqli Connection Error' .mysqli_connect_error($conn); echo 'Mysqli Connection Error Number' .mysqli_connect_errno($conn); } if(!mysqli_character_set_name($conn) == 'utf8mb4') { echo 'Initial Character Set: ' .mysqli_character_set_name($conn); mysqli_set_charset($conn,'utf8mb4'); echo 'Current Character Set: ' .mysqli_character_set_name($conn); } //PAGINATION SECTION. 增加默认值和类型转换 $search = isset($_GET['search']) ? trim($_GET['search']) : ''; //Keyword(s) to search. $col = isset($_GET['col']) ? trim($_GET['col']) : ''; //MySql Tbl Col to search. $tbl = isset($_GET['tbl']) ? trim($_GET['tbl']) : ''; //MySql Tbl to search. $max = isset($_GET['max']) ? intval($_GET['max']) : 10; //Max Result per page. $page = isset($_GET['page']) ? intval($_GET['page']) : 1; //Serp Number. // 防止参数为负 $max = $max < 1 ? 10 : $max; $page = $page < 1 ? 1 : $page; //QUERY DATABASE FOR KEYWORD COUNT. $query = "SELECT COUNT(id) From links WHERE keyword = ?"; $stmt = mysqli_stmt_init($conn); if(mysqli_stmt_prepare($stmt,$query)) { mysqli_stmt_bind_param($stmt,'s',$search); mysqli_stmt_execute($stmt); mysqli_stmt_bind_result($stmt,$row_count); if(mysqli_stmt_fetch($stmt)) { echo 'Row Count: ' .$row_count; echo '<br>'; } else { echo 'Record fetching failed!'; echo 'Error: ' .mysqli_stmt_error($conn); echo 'Error: ' .mysqli_stmt_errno($conn); } mysqli_stmt_close($stmt); } else { echo 'Search Preparation Failed!'; } echo '<b>'; echo __LINE__; echo '</b>'; echo '<br>'; //START KEYWORD SEARCH & OUTPUT RESULT $offset = ($page*$max)-$max; echo $offset; echo '<br>'; echo '<b>'; echo __LINE__; echo '</b>'; echo '<br>'; // LIMIT参数提前转int保证安全 $query = "SELECT id,date_and_time,domain,domain_email,ip,url,anchor,title,description,keyword,keyphrase From links WHERE keyword = ? LIMIT " . intval($offset) . "," . intval($max); $stmt = mysqli_stmt_init($conn); if(mysqli_stmt_prepare($stmt,$query)) { mysqli_stmt_bind_param($stmt,'s',$search); mysqli_stmt_execute($stmt); if($result = mysqli_stmt_get_result($stmt)) { // 修改变量名为$row,避免覆盖原来的$col参数 while($row = mysqli_fetch_array($result)) { $id = $row['0']; //MYSQLI_NUM $date_and_time = $row['date_and_time']; //MYSQLI_ASSOC $domain = $row['2']; //MYSQLI_NUM $domain_email = $row['domain_email']; //MYSQLI_ASSOC $ip = $row['4']; //MYSQLI_NUM $url = $row['url']; //MYSQLI_ASSOC $anchor = $row['6']; //MYSQLI_NUM $title = $row['title']; //MYSQLI_ASSOC $description = $row['8']; //MYSQLI_NUM $keyword = $row['keyword']; //MYSQLI_ASSOC $keyphrase = $row['10']; //MYSQLI_NUM // 输出内容全部加htmlentities转义防范XSS echo 'Id: ' . htmlentities($id) ; echo '<br>'; echo 'Date And Time: ' . htmlentities($date_and_time); echo '<br>'; echo 'Domain: ' . htmlentities($domain); echo '<br>'; echo 'Domain Email: ' . htmlentities($domain_email); echo '<br>'; echo 'Ip: ' . htmlentities($ip); echo '<br>'; echo 'Url: ' . htmlentities($url); echo '<br>'; echo 'Anchor: ' . htmlentities($anchor); echo '<br>'; echo 'Title: ' . htmlentities($title); echo '<br>'; echo 'Description: ' . htmlentities($description); echo '<br>'; echo 'Keyword: ' . htmlentities($keyword); echo '<br>'; echo 'Keyphrase: ' . htmlentities($keyphrase); echo '<br>'; } } else { echo 'Record fetching failed!'; echo 'Error: ' .mysqli_stmt_error($stmt); echo 'Error: ' .mysqli_stmt_errno($stmt); } mysqli_stmt_close($stmt); } mysqli_close($conn); echo '<b>'; echo __LINE__; echo '</b>'; echo '<br>'; //PAGINATION SECTION TO NUMBER THE PAGES AND LINK THEM. $total_pages = ceil($row_count/$max); $i = 1; $selfpage = basename(__FILE__,''); $path = rawurlencode($selfpage); // 此时$col已经是原来的字符串参数,不会报错 $query_string_1 = '?search=' .urlencode($search) .'&tbl=' .urlencode($tbl) .'&col=' .urlencode($col) .'&max=' .intval($max); while($i<=$total_pages) { $query_string_2 = '&page=' .intval($i); // 先拼接完整URL,再在输出到href属性时做htmlentities转义 $url = $path . $query_string_1 . $query_string_2; if($page == $i) { echo '<a href="' . htmlentities($url) . '"><b>' .intval($i) .'</b></a>'; } else { echo '<a href="' . htmlentities($url) . '">' .intval($i) .'</a>'; } $i++; } ?>
内容的提问来源于stack exchange,提问作者I Ali
相关产品推荐
相关产品推荐

