1.7 表单处理:旅客递交的申报单


文档摘要

1.7 表单处理:旅客递交的申报单 本节摘要:表单是旅客递交的申报单,也是服务端最经典的完整链路:接收、校验、转义、应答,一步都不能省。本节先分清 GET 与 POST 两条通道,再走通一张"报名表"的完整处理流程,给出 CSRF 令牌的最小实现与文件上传的落盘要点。前面 1.2 到 1.6 攒下的类型转换、函数封装、字符串清洗、文件落盘在这里全部合流。 GET 与 POST:两种进站通道 表单提交走哪条通道,由 的 决定。GET 把数据拼在地址后面,看得见、能收藏、有长度限制,适合查询搜索;POST 把数据装进请求体,不进地址栏,适合提交业务数据。一句话分工:改数据的用 POST,查数据的用 GET。下面是本节贯穿使用的报名表单: 隐藏字段 是防跨站请求伪造的伏笔,本节末尾交代。

1.7 表单处理:旅客递交的申报单

本节摘要:表单是旅客递交的申报单,也是服务端最经典的完整链路:接收、校验、转义、应答,一步都不能省。本节先分清 GET 与 POST 两条通道,再走通一张"报名表"的完整处理流程,给出 CSRF 令牌的最小实现与文件上传的落盘要点。前面 1.2 到 1.6 攒下的类型转换、函数封装、字符串清洗、文件落盘在这里全部合流。

GET 与 POST:两种进站通道

表单提交走哪条通道,由 <form>method 决定。GET 把数据拼在地址后面,看得见、能收藏、有长度限制,适合查询搜索;POST 把数据装进请求体,不进地址栏,适合提交业务数据。一句话分工:改数据的用 POST,查数据的用 GET。下面是本节贯穿使用的报名表单:

<!-- form.html:申报单的纸面部分 --> <form action="signup.php" method="post"> <label>姓名 <input type="text" name="name"></label> <label>邮箱 <input type="email" name="email"></label> <label>舱位 <select name="seat"> <option value="economy">经济舱</option> <option value="business">商务舱</option> </select> </label> <input type="hidden" name="token" value="一站一令牌"> <button type="submit">递交申报单</button> </form>

隐藏字段 token 是防跨站请求伪造的伏笔,本节末尾交代。提交后数据进入 $_POST,键名就是各控件的 name

图:表单提交数据流——从纸面到落库的全链路

图:表单提交数据流——从纸面到落库的全链路

校验与转义:两张不同的安检网

新手最容易把"校验"和"转义"混为一谈。校验判断数据合不合格,不合格就退件;转义保持数据原样,只是让它换一个环境后不惹祸——存进文件要防行注破,输出进 HTML 要防标签注入。两张网各管一段,都要装。完整的 signup.php 如下,可以直接跑:

<?php declare(strict_types=1); if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); exit('请通过表单递交'); } // 第一步:收件与定型 $name = trim((string) ($_POST['name'] ?? '')); $email = trim((string) ($_POST['email'] ?? '')); $seat = (string) ($_POST['seat'] ?? 'economy'); // 第二步:逐项校验,错误统一登记 $errors = []; if ($name === '' || mb_strlen($name) > 20) { $errors[] = '姓名必填且不超过 20 字'; } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = '邮箱格式不正确'; } if (!in_array($seat, ['economy', 'business'], true)) { $errors[] = '舱位选项不合法'; // 下拉框也要验:请求可以伪造 } // 失败:逐条退件并终止 if ($errors) { foreach ($errors as $e) { echo htmlspecialchars($e, ENT_QUOTES, 'UTF-8'), "<br>"; } exit; } // 第三步:持久化(本章先落文件,2.2 换数据库) $record = sprintf("%s|%s|%s\n", $name, $email, $seat); file_put_contents(__DIR__ . '/signup_list.txt', $record, FILE_APPEND | LOCK_EX); // 第四步:成功回执(回显用户数据前先转义) echo '受理成功,欢迎 ', htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); echo ',舱位:', htmlspecialchars($seat, ENT_QUOTES, 'UTF-8');

注意三个细节。其一,下拉框的值同样要校验——别迷信"页面上只有两个选项",任何人都能手工构造请求;in_array 加严格参数 true 是本册的固定动作。其二,错误信息与回显内容全部过了 htmlspecialchars,因为它们最终要进 HTML。其三,exit 提前收班让失败路径不会误触后续写入。

防伪令牌:CSRF 的最小实现

跨站请求伪造的原理:恶意站点诱导已登录旅客的浏览器向你站提交表单,请求带着合法身份就混进来了。解法是"一站一令牌":表单里埋一个随机令牌,会话里存同一份,收单时两边比对,对不上就退件:

<?php session_start(); // 1.8 节展开会话机制 if (empty($_SESSION['csrf'])) { $_SESSION['csrf'] = bin2hex(random_bytes(16)); // 首次发令牌 } // 表单里输出:<input type="hidden" name="token" value="...$_SESSION['csrf']..."> // signup.php 收单时: $token = (string) ($_POST['token'] ?? ''); $expect = (string) ($_SESSION['csrf'] ?? ''); if ($token === '' || !hash_equals($expect, $token)) { http_response_code(419); exit('令牌无效,请回到表单页重新递交'); }

hash_equals 做恒时比较,防的是时序侧信道,安全代码里比对密钥类字符串一律用它。令牌每次会话一份是最简做法,要求更严的系统会在每次表单渲染时轮换新令牌。

文件上传:另一种申报单

<input type="file"> 提交的行李不走 $_POST,走 $_FILES。最小安全落盘流程四步:确认无错误码、验类型与大小、改名、移入库房:

<?php $file = $_FILES['luggage'] ?? null; if (!$file || $file['error'] !== UPLOAD_ERR_OK) { exit('行李未送达或中途破损'); } if ($file['size'] > 2 * 1024 * 1024) { exit('行李超重:上限 2MB'); } // 改名入库:扩展名白名单,原始名不留作路径 $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($ext, ['jpg', 'png', 'pdf'], true)) { exit('此类行李不予托运'); } $target = __DIR__ . '/uploads/' . bin2hex(random_bytes(8)) . '.' . $ext; move_uploaded_file($file['tmp_name'], $target) || exit('入库失败'); echo '托运成功';

原始文件名只配当展示用途,绝不能拼进保存路径——这是 1.6 节仓储纪律的具体化。

本节要点回顾

  • 通道分工:GET 查询、POST 提交,改数据的操作一律 POST。
  • 四步处理:定型、校验、持久化、应答,失败统一登记逐条退件。
  • 两道安检网:入库前清洗、出站前 htmlspecialchars 转义,职责不同都要装。
  • CSRF 令牌:会话存一份、表单埋一份、收单用 hash_equals 比对。
  • 上传四步:验错误码、限大小与扩展、改名、移入库房,原始名不进路径。

下一节解决"旅客下次来还认不认得"的问题:会话与 Cookie,登录态的门道全在里面。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U