SQL审计实战经验分享与关键问题解决思路

a'ゞ悦辰 安全 阅读 1,341
赞 31 收藏
二维码
手机扫码查看
反馈

SQL审计踩坑记:这锅我不背

前两天被后端同事叫过去,说是我写的前端代码导致SQL注入问题。我当时就懵了,心想前端怎么会导致SQL注入呢?折腾了半天才发现,原来是接口调用的时候参数处理不当惹的祸。

SQL审计实战经验分享与关键问题解决思路

这里我踩了个坑,一开始以为是后端的问题,还跟后端同事争论了半天。后来仔细看了下接口调用的地方,发现确实是我这边在拼接参数的时候太随意了。具体来说,就是在调用搜索接口的时候,直接把用户输入的内容拼接到请求参数里,没有做任何转义处理。

排查过程:从甩锅到认错

最开始我先检查了前端的代码,发现是这么写的:

function search(keyword) {
    const url = https://jztheme.com/api/search?keyword=${keyword};
    fetch(url)
        .then(res => res.json())
        .then(data => console.log(data));
}

看起来好像没啥问题,对吧?但其实这里有个大坑:如果用户输入的是 1' OR '1'='1 这种恶意字符串,就会导致后端SQL语句出问题。

我试着在控制台输出了实际发出去的请求,果然看到了完整的恶意payload。后端同事给我解释说,他们那边虽然有基本的防护,但如果前端传过来的参数已经是个完整SQL片段,还是会有风险。

解决方案:双重保险才靠谱

最后的解决办法分两步走:前端做好基础的参数校验和转义,后端依然保持严格的SQL审计和参数化查询。

先说前端的改动,我把原来的代码改成了这样:

function escapeSql(str) {
    return str.replace(/'/g, "\'")
              .replace(/"/g, '\"')
              .replace(/;/g, '')
              .replace(///g, '\/')
              .replace(/\/g, '\\');
}

function search(keyword) {
    const safeKeyword = encodeURIComponent(escapeSql(keyword));
    const url = https://jztheme.com/api/search?keyword=${safeKeyword};
    fetch(url)
        .then(res => res.json())
        .then(data => console.log(data));
}

这里有几个关键点要注意:

  • 用了正则表达式把特殊字符都转义了一遍
  • 配合 encodeURIComponent 确保传输安全
  • 即使这样处理完,还是要提醒后端同事做好防护

后端那边的改进主要是用了预编译语句,类似这样:

$stmt = $pdo->prepare("SELECT * FROM articles WHERE title LIKE :keyword");
$stmt->execute(['keyword' => "%{$keyword}%"]);
$results = $stmt->fetchAll();

技术细节补充:为啥要这么干

有人可能会问,既然后端已经做了SQL参数化处理,前端还需要这么麻烦吗?我的理解是:安全这件事,从来都是越谨慎越好。

首先,前端做一层过滤可以提前拦截大部分明显恶意的请求,减轻后端压力。其次,即使后端有防护,万一哪天升级或者修改代码时不小心漏掉了,前端这一层还能兜底。

这里还要特别提醒一点:千万不要以为用了HTTPS就万事大吉。HTTPS只能保证传输过程中的加密,对于SQL注入这种应用层的安全问题完全无能为力。

遗留的小问题:完美很难

改完之后其实还有个小瑕疵:如果用户正常搜索包含特殊字符的内容,比如程序员常用的 SELECT * FROM users; 这种合法的技术讨论,会被过度转义。不过考虑到实际情况,这种情况出现的概率很小,暂时就这么着吧。

以上是我踩坑后的总结,如果你有更好的方案欢迎评论区交流。毕竟安全这个东西,多学点总没错。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论