必须先提取并预检Base64数据部分:用preg_match匹配合法图像类型前缀,strpos+substr提取逗号后纯Base64内容,str_replace清理空白符,再校验长度是否为4的倍数,最后用base64_decode($data, true)严格解码。

直接用 base64_decode() 解码再写文件是危险的,必须在解码前完成三重校验:格式合法性、原始尺寸上限、图像真实性。跳过任意一环都可能触发内存溢出、文件覆盖或 WebShell 写入。
怎么提取并预检 Base64 数据部分
前端传来的字符串通常是 data:image/png;base64,iVBORw... 这种 Data URI 格式,不能直接丢给 base64_decode()。
- 先用
preg_match('/^data:image\/(png|jpg|jpeg|gif|webp);base64,/', $base64, $matches)锁定合法类型,避免接受image/php或其他伪造 mime - 用
strpos($base64, ',')找到分隔符位置,再用substr($base64, $pos + 1)提取纯 Base64 内容 - 立即清理空格和换行:
str_replace([' ', "\t", "\n", "\r"], '', $base64Data),否则base64_decode()可能静默失败 - 检查长度是否为 4 的倍数:
strlen($base64Data) % 4 === 0,不满足说明编码损坏或被篡改
为什么必须用 base64_decode($data, true)
默认模式下 base64_decode($data) 遇到非法字符(如 `
-
base64_decode($base64Data, true)在 PHP 5.2+ 中可用,遇到任何非法字符都会明确返回false - 必须配合
=== false严格比较,不能用!= false或empty(),因为解码出空图片(如 1×1 透明 GIF)也是合法的 - 如果返回
false,立刻中止流程并返回 400 错误,不要尝试“修复”或忽略
如何准确限制原始图像大小(不是 Base64 字符串长度)
strlen($base64) 是编码后长度,比原始二进制大 33% 左右。限制它等于放行超大图——1.3MB 的 Base64 字符串对应原始图可能已达 1MB,而你本意只允许 500KB。
前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。
立即学习“PHP免费学习笔记(深入)”;
- 先解码:
$decoded = base64_decode($base64Data, true) - 再校验原始体积:
if (strlen($decoded) > 524288) { // 500KB } - 别信前端传的
Content-Length或自报尺寸,攻击者可伪造;真实字节数只能从$decoded得到 - PHP 内存限制(
memory_limit)需预留余量,比如允许 2MB 原图,memory_limit至少设为 8M
怎么确认它真是图片,不是伪装的 PHP 文件
仅靠 data:image/jpeg 前缀毫无意义,攻击者可手动拼接伪造字符串。必须用二进制方式验证图像头和结构。
- 用
getimagesizefromstring($decoded)检查是否返回有效数组,它会解析文件头、宽高、真实 mime,失败即非图像 - 对比
$matches[1](从前缀提取的 type)和$info['mime'],例如前缀说png但实际是image/jpeg就该拒绝 - 额外加一层:用
finfo_buffer()检查魔数,防止文件头被刻意构造绕过getimagesizefromstring() - 别用
exif_imagetype(),它只读前几个字节,对裁剪过的或带元数据的图容易误判
最容易被忽略的是:所有校验必须在 file_put_contents() 之前完成,且顺序不能颠倒——先格式、再大小、最后图像真实性。中间任一环节失败都要立即退出,不能“尽力而为”。


















