SQL注入的風(fēng)險(xiǎn)與解決方案實(shí)戰(zhàn)解析
前言
在日常開發(fā)中,數(shù)據(jù)庫(kù)操作幾乎是繞不開的環(huán)節(jié)。很多同學(xué)寫查詢語(yǔ)句的時(shí)候,習(xí)慣直接用字符串拼接,比如:
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
乍一看挺正常,但一旦碰上心懷不軌的攻擊者,后果就可能非常嚴(yán)重。這就是我們常說的 SQL 注入 問題。
什么是 SQL 注入
SQL 注入的本質(zhì)就是:
用戶的輸入被直接拼接到 SQL 語(yǔ)句中,沒有做任何防護(hù),導(dǎo)致數(shù)據(jù)庫(kù)把攻擊者的輸入當(dāng)成真正的 SQL 指令去執(zhí)行。
舉個(gè)最經(jīng)典的例子:
如果登錄接口這樣寫:
String username = "admin"; String password = "' OR '1'='1"; // 攻擊者輸入的內(nèi)容 String sql = "SELECT * FROM users WHERE username='" + username + "' AND password='" + password + "'"; System.out.println(sql);
拼接后的 SQL 就變成了:
SELECT * FROM users WHERE username='admin' AND password='' OR '1'='1'
這一句 OR '1'='1' 永遠(yuǎn)成立,所以攻擊者輕輕松松繞過了密碼驗(yàn)證,直接登錄成功。
真實(shí)場(chǎng)景下的危害
別以為這只是理論,現(xiàn)實(shí)中因?yàn)?SQL 注入出問題的案例太多了。常見危害包括:
- 繞過身份認(rèn)證:像上面的例子,用戶不用知道密碼也能登錄。
- 數(shù)據(jù)泄露:攻擊者可以拼接語(yǔ)句,把整個(gè)表的數(shù)據(jù)都查詢出來(lái)。
- 數(shù)據(jù)篡改或刪除:嚴(yán)重的情況下,甚至可以執(zhí)行
DROP TABLE users;直接把表刪掉。 - 權(quán)限提升:通過復(fù)雜的注入方式,攻擊者可能拿到數(shù)據(jù)庫(kù)更高權(quán)限,導(dǎo)致系統(tǒng)全面失控。
所以在企業(yè)開發(fā)里,SQL 注入算是基礎(chǔ)中的基礎(chǔ),必須提前預(yù)防。
常見解決方案
那我們?cè)撛趺捶婪赌兀坑袔追N常見的方式:
1. 使用 PreparedStatement
PreparedStatement 是 JDBC 提供的預(yù)編譯語(yǔ)句對(duì)象。它會(huì)先把 SQL 模板交給數(shù)據(jù)庫(kù)編譯好,然后再傳入?yún)?shù)。這樣參數(shù)和 SQL 邏輯嚴(yán)格分離,用戶輸入就不會(huì)被當(dāng)成 SQL 指令執(zhí)行。
Demo 代碼:
import java.sql.*;
public class SafeLoginDemo {
public static void main(String[] args) throws Exception {
String url = "jdbc:mysql://localhost:3306/testdb";
String user = "root";
String pass = "123456";
String inputUser = "admin";
String inputPass = "' OR '1'='1";
Connection conn = DriverManager.getConnection(url, user, pass);
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, inputUser);
stmt.setString(2, inputPass);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
System.out.println("登錄成功: " + rs.getString("username"));
} else {
System.out.println("用戶名或密碼錯(cuò)誤");
}
rs.close();
stmt.close();
conn.close();
}
}
這里無(wú)論用戶輸入什么奇怪的密碼,數(shù)據(jù)庫(kù)都會(huì)把它當(dāng)成一個(gè)普通的字符串參數(shù)處理,而不是 SQL 邏輯。
2. MyBatis 使用#{}占位符
在 MyBatis 中,有兩個(gè)寫法經(jīng)常被混淆:${} 和 #{}。
${}:直接拼接字符串,可能導(dǎo)致 SQL 注入。#{}:安全的參數(shù)綁定,底層會(huì)幫你用 PreparedStatement。
錯(cuò)誤寫法(容易被注入):
<select id="getUserByName" resultType="User">
SELECT * FROM users WHERE username = '${username}'
</select>
正確寫法(推薦使用):
<select id="getUserByName" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>
這樣就算有人傳了 ' OR '1'='1 這樣的輸入,也只會(huì)作為字符串參數(shù)傳入,不會(huì)破壞 SQL 邏輯。
3. 使用 ORM 框架封裝查詢
像 Hibernate、JPA 這類 ORM 框架,通常都已經(jīng)封裝了參數(shù)綁定邏輯,默認(rèn)情況下就能避免注入風(fēng)險(xiǎn)。
比如用 Spring Data JPA:
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsernameAndPassword(String username, String password);
}
Spring Data JPA 底層會(huì)自動(dòng)生成類似 PreparedStatement 的語(yǔ)句,所以基本不需要擔(dān)心 SQL 注入問題。
總結(jié)
回過頭來(lái)看,SQL 注入的核心原因是 “把用戶輸入當(dāng)成 SQL 語(yǔ)句的一部分”。
解決的思路就是 “參數(shù)化查詢”,讓 SQL 和用戶輸入嚴(yán)格分離。
落地建議:
- 在 JDBC 層,統(tǒng)一用
PreparedStatement。 - 在 MyBatis 中,統(tǒng)一用
#{}而不是${}。 - 在使用 ORM 框架時(shí),不要為了圖省事拼接 JPQL/HQL。
- 養(yǎng)成安全開發(fā)習(xí)慣,做代碼審查時(shí)重點(diǎn)關(guān)注數(shù)據(jù)庫(kù)查詢部分。
到此這篇關(guān)于SQL注入的風(fēng)險(xiǎn)與解決方案實(shí)戰(zhàn)解析的文章就介紹到這了,更多相關(guān)SQL注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL 8.0新特性 — 檢查性約束的使用簡(jiǎn)介
這篇文章主要介紹了MySQL 8.0新特性 — 檢查性約束的簡(jiǎn)單介紹,幫助大家更好的理解和學(xué)習(xí)使用MySQL數(shù)據(jù)庫(kù),感興趣的朋友可以了解下2021-03-03
mysql?binlog查看指定數(shù)據(jù)庫(kù)的操作方法
MySQL 的 binlog(二進(jìn)制日志)主要記錄了數(shù)據(jù)庫(kù)上執(zhí)行的所有更改數(shù)據(jù)的 SQL 語(yǔ)句,包括數(shù)據(jù)的插入、更新和刪除等操作這篇文章主要介紹了mysql?binlog查看指定數(shù)據(jù)庫(kù)的方法,需要的朋友可以參考下2024-06-06
MySQL中實(shí)現(xiàn)動(dòng)態(tài)表單中JSON元素精準(zhǔn)匹配的方法示例
本文主要介紹了MySQL中實(shí)現(xiàn)動(dòng)態(tài)表單中JSON元素精準(zhǔn)匹配的方法示例,重點(diǎn)講解如何在將設(shè)計(jì)好的動(dòng)態(tài)表單信息進(jìn)行提取,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
如何解決mysql出現(xiàn)Incorrect string value for co
這篇文章主要介紹了如何解決mysql出現(xiàn)Incorrect string value for column ‘表項(xiàng)‘ at row 1錯(cuò)誤問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
MySQL查詢優(yōu)化的三種處理階段(Index Key、Index Filter和Table 
在 MySQL 中,索引主要用于優(yōu)化查詢,在 MySQL 查詢優(yōu)化中涉及到三種處理階段,Index Key、Index Filter 和 Table Filter,下面小編就為大家詳細(xì)介紹一下這三個(gè)階段吧2025-09-09
MySQL數(shù)據(jù)庫(kù)InnoDB引擎主從復(fù)制同步經(jīng)驗(yàn)總結(jié)
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)InnoDB引擎主從復(fù)制同步經(jīng)驗(yàn)總結(jié),本文總結(jié)了設(shè)置主從復(fù)制時(shí)遇到的一些錯(cuò)誤和解決方法,需要的朋友可以參考下2015-01-01
MySQL Workbench導(dǎo)入excel數(shù)據(jù)的實(shí)現(xiàn)示例
本文主要介紹了MySQL Workbench導(dǎo)入excel數(shù)據(jù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
SQL Server 完整備份遇到的一個(gè)不常見的錯(cuò)誤及解決方法
這篇文章給大家介紹了SQL Server 完整備份遇到的一個(gè)不常見的錯(cuò)誤及解決方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2019-05-05

