01 业务安全的第一道防线输入验证
安全621 字
在我们日常的编码过程中,输入校验可以说是我们业务安全的第一道防线,我们需要对每一次的用户输入做一定的校验,也就是我们常说的,不要信任任何用户的输入。
对于用户的输入,我们需要做到如下的校验:
- 白名单
- 黑名单
- 规范化
- 合法性校验
- 防范 SQL 注入文件校验
- 访问控制
白名单
场景: 当可能输入的集合比较小的,可以选择使用列表选择来确保不能绕过验证。
说明: 不可信数据可设定白名单校验的,应接受所有和白名单匹配的数据,并阻止其他数据。
function isValidateCommandRoutine($command) {
$WhiteList = array('cmd','ls','sh','env');
if (false === empty($command)) {
foreach ($WhiteList as $key=>$value) {
if ($value === $command) {
return true;
}
}
return false;
} else {
return false;
}
}
function validateCommandArray($cmds) {
if (isValidateCommandRoutine($cmds)) {
//Process valid input and return here.
system(EscapeShellCmd($cmds));
}
return;
}
黑名单
场景:部分可控输入数据集合时,拒绝已知恶意的输入数据。
说明:不可信数据中包含不良输入字符时,如空字节(%00)、换行符(%0d,%0a,\r,\n)、路径字符(../,..\)等,建议直接阻止该数据,若需要接受该数据,则应做不同方式的净化处理。
function isValidateString($s) {
$keywords = "[<>]";
if (preg_match("$keywords", $s)) {
return false;
} else {
return true;
}
}
规范化
场景:部分可控输入数据集合时,拒绝已知恶意的输入数据。
说明:不可信数据的净化和校验前需进行规范化,如将目录遍历(../或..\)等相对路径转化成绝对路径,URL解码等。
function fileCheck() {
$basepath = './site/img/';
$realBase = realpath($basepath);
$realUserPath = realpath($basepath . $_GET['path']);
if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
return false;
} else {
return true;
}
}
合法性校验
场景:当合法值的范围太广泛时,不能明确确定时,根据业务场景进行类似正则匹配。
说明:不可信数据的合法性校验包括:数据类型如字符、数字、日期等特征;数据范围;数据长度等。
function isValidateString() {
$patternname = "^[A-Za-z0-9_-]{4,}$";
if ($_POST['name'] && preg_match("$patternname", $_POST['name'])) {
return true;
} else {
return false;
}
}
防范SQL注入
场景:当用户数据进入后端SQL操作时,使用参数化查询来处理避免出现SQL注入。
说明:不可信数据的合法性校验包括:数据类型如字符、数字、日期等特征;数据范围;数据长度等。
<?php
$dbns = 'mysql:dbname=testdb;host=127.0.0.1';
$username = 'dbuser';
$password = 'dbpasswd'
try {
$pdo = new PDO($dbns, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('set names utf8');
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit;
}
$str = $pdo->prepare('select * from content where id=? and name=?');
$str->execute(array('id'));
$str->execute(array('name'));
print_r($str->fetchAll());
?>
文件校验
场景:用户提交压缩文件,服务器需要进行解压缩存储时。
说明:不可信数据为解压缩的文件时,如果文件位于服务目录外或文件大小超过限制,应拒绝处理。
class Unzip{
public function __construct() {
header("content-type:text/html;charset=utf8");
}
public function unzip($src_file, $dest_dir) {
$zip = zip_open($src_file)
if ($zip) {
$this->create_dirs($dest_dir);
while ($zip_entry = zip_read($zip)) {
if (zip_entry_filesize($zip_entry) > 0x640000) {
return false;
}
$pos_last_slash = strrpos(zip_entry_name($zip_entry), "/");
if ($pos_last_slash !== false) {
$this->create_dirs($dest_dir.substr(zip_entry_name($zip_entry), 0, $pos_last_slash+1));
}
if (zip_entry_open($zip, $zip_entry, "r")) {
$file_name = $dest_dir.zip_entry_name($zip_entry);
if (!is_file($file_name)) {
$fstream = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
@file_put_contents($file_name, $fstream);
chmod($file_name, 0666);
}
zip_entry_close($zip_entry);
}
}
zip_close($zip);
return true;
} else {
return false;
}
}
public function create_dirs($path) {
if (!is_dir($path)) {
$directory_path = "";
$directories = explode("/", $path);
array_pop($directories);
foreach($directories as $directory) {
$directory_path .= $directory . "/";
if (!is_dir($directory_path)) {
mkdir($directory_path);
chmod($directory_path, 0755);
}
}
}
}
}
访问控制
场景:用户提交的数据,在处理前需要进行身份确认。
说明:不可信数据通过上述校验后,还应确认所提交的内容是否与用户的身份匹配,避免越权访问。
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
public function __construct() {
$this->middleware('auth');
}
public function show($id) {
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
?>