What’s the Right Way to Prevent SQL Injection in PHP Scripts?

防止SQL注入的最佳实践
本文详细阐述了如何在PHP脚本中预防SQL注入攻击,并对比了传统的输入验证方法与使用参数化查询的方法,强调后者是更为安全的选择。

https://blogs.msdn.microsoft.com/brian_swan/2010/03/04/whats-the-right-way-to-prevent-sql-injection-in-php-scripts/

How to prevent SQL injection in PHP scripts is probably a topic that doesn’t need anything more written about it. It is pretty easy to find blog posts, documentation, videos, etc.  that explain the importance of preventing SQL injection and suggestions for preventing it. In fact, I’ve already written a post on this topic as a guest writer on the SQL Server Driver for PHP team blog. However, it is important to have fresh information for new Web developers and I don’t necessarily agree with some of the most common suggestions for preventing SQL injection. (Besides, I hear that this is the Month of PHP Security.) So, this will be yet another post about preventing SQL injection, but I will offer my 2 cents about what I think is the right way to prevent it.


 


What is SQL Injection?


SQL injection (or a SQL injection attack) occurs when a user provides SQL code as user input for a Web page, and the SQL code is then executed in the database. For example, consider the following login script:



<form method=”post” action=”injection.php” enctype=”multipart/form-data” >
    Username:<input type=”text” name=”Username” id=”Username”/></br>
    Password:<input type=”text” name=”Password” id=”Password”/></br>
    <input type=”submit” name=”submit” value=”Submit” />
</form>
<?php
$username = $_POST[‘Username’];
$password = $_POST[‘Password’];


$server = “MyServer\sqlexpress”;
$options = array(“Database”=>”ExampleDB”, “UID”=>”MyUID”, “PWD”=>”MyPWD”);
$conn = sqlsrv_connect($server, $options);
$sql = “SELECT * FROM UserTbl WHERE Username = ‘$username’ and Password = ‘$password'”;
$stmt = sqlsrv_query($conn, $sql);
if(sqlsrv_has_rows($stmt))
{
    echo “Welcome.”;
}
else
{
    echo “Invalid password.”;
}
?>


Now consider the form with the following inputs:



    image


Now the statement that is executed in the database is the following:



SELECT * FROM UserTbl WHERE Username= ‘Brian’ and Password= ”or 1 = 1–‘


Because 1=1 is always true, this query will return all users. (Note that the last quotation is commented out.) So, in the script above, sqlsrv_has_rows is true, and access is granted.


SQL injection is possible here because user input is concatenated with the executed SQL code. Scripts should not be written in this way…ever. The example above only scratches the surface of what can be done with SQL injection – much more malicious attacks are possible.


 


A Common Attempt at Prevention


The most common suggestion I’ve seen for preventing SQL injection involves trying to remove or escape any possible SQL code from user input before concatenating it with the SQL code to be executed. There are several PHP functions (and functions in PHP extensions) that can be used to do this, but all of them are potentially vulnerable. If you concatenate user input with SQL code that will be executed in the database, you run the risk of a SQL injection attack no matter how much parsing and escaping of the input you do. How can you be 100% sure that you’ve thought of all possibilities that a creative hacker might think of? How can you be sure that you’ve taken the appropriate measures to mitigate an attack? How can you be sure that the functions you are using to remove or escape dangerous user input aren’t buggy?


Now, having posed those questions, can I actually come up with an attack that gets by the best of the “remove and escape” strategies? No. When done carefully, this strategy is pretty good at preventing SQL injection. However, it still allows for the possibility of some clever hacker finding a way to inject SQL, even if it is a remote possibility. Why take that chance when an easier, safer alternative exists?  


 


The Right Way to Prevent SQL Injection


The right way to prevent SQL injection is by using parameterized queries. This means defining the SQL code that is to be executed with placeholders for parameter values, programmatically adding the parameter values, then executing the query. Doing this allows the server to create an execution plan for the query, which prevents any “injected” SQL from being executed. An example will help in explaining this. Let’s use the same script, but I’ll define the SQL query with parameter placeholders:



$sql = “SELECT * FROM UserTbl WHERE Username = ? and Password = ?”;


Now, I’ll define an array that holds the parameter values:



$params = array($_POST[‘Username’], $_POST[‘Password’]);


When I execute the query, I pass the $params array as an argument:



$stmt = sqlsrv_query($conn, $sql, $params);


When sqlsrv_query is called, an execution plan is created on the server before the query is executed. The plan only allows our original query to be executed. Parameter values (even if they are injected SQL) won’t be executed because they are not part of the plan. So, if I submit a password like I did in the example above (‘or 1=1–), it will be treated as user input, not SQL code. In other words, the query will look for a user with this password instead of executing unexpected SQL code.


The script above, modified to prevent SQL injection, looks like this:



<form method=”post” action=”injection.php” enctype=”multipart/form-data” >
    Username:<input type=”text” name=”Username” id=”Username”/></br>
    Password:<input type=”text” name=”Password” id=”Password”/></br>
    <input type=”submit” name=”submit” value=”Submit” />
</form>
<?php
$params = array($_POST[‘Username’], $_POST[‘Password’]);


$server = “MyServer\sqlexpress”;
$options = array(“Database”=>”ExampleDB”, “UID”=>”MyUID”, “PWD”=>”MyPWD”);
$conn = sqlsrv_connect($server, $options);
$sql = “SELECT * FROM UserTbl WHERE Username = ? and Password = ?”;
$stmt = sqlsrv_query($conn, $sql, $params);
if(sqlsrv_has_rows($stmt))
{
    echo “Welcome.”;
}
else
{
    echo “Invalid password.”;
}
?>



Note: If you expect to execute a query multiple times with different parameter values, use the sqlsrv_prepare and sqlsrv_execute functions. The sqlsrv_prepare function creates an execution plan on the server once and the sqlsrv_execute function executes the query with different parameter values each time it is called.


I’m using SQL Server Express and the sqlsrv API to demonstrate parameterized queries here, but this technique can (and should) be applied regardless of the database and extension being used.


OK…that’s my 2 cents about preventing SQL injection. I’d certainly be interested in other opinions about the best way to prevent SQL injection…let me know what you think.

<think>根据用户的问题,用户在使用IDEA时遇到了一个提示:“Would you like to install shellcheck to verify your shell scripts?”,并希望找到解决办法。 分析:ShellCheck是一个用于静态分析Shell脚本的工具,可以检查脚本中的语法错误和常见问题。IDEA(特别是IntelliJ IDEA)集成了对ShellCheck的支持,当用户打开Shell脚本文件时,可能会提示安装ShellCheck以进行脚本验证。 解决办法: 1. 如果用户希望使用ShellCheck,可以直接在IDEA的提示中点击“Install”进行安装。安装过程通常由IDEA自动完成。 2. 如果用户没有看到提示或想手动安装,可以按照以下步骤操作: a. 确保系统上已经安装了ShellCheck。如果没有,需要先安装ShellCheck。 对于不同的操作系统,安装方法不同: - 在Debian/Ubuntu上:`sudo apt install shellcheck` - 在Fedora上:`sudo dnf install shellcheck` - 在macOS上(使用Homebrew):`brew install shellcheck` - 在Windows上:可以通过scoop安装(`scoop install shellcheck`)或者从官方下载二进制文件。 b. 在IDEA中配置ShellCheck: 打开设置(Settings) -> 选择“Languages & Frameworks” -> 选择“Shell Script” -> 在“Inspections”中确保“ShellCheck”已启用,并指定ShellCheck的安装路径(如果IDEA没有自动检测到)。 3. 如果用户不想使用ShellCheck,可以选择忽略或禁用该提示: - 在提示出现时,选择“Don't ask again”来避免再次提示。 - 在设置中禁用ShellCheck检查:打开设置 -> 选择“Editor” -> “Inspections” -> 在搜索框中输入“ShellCheck” -> 取消勾选。 注意:用户提供的引用内容中,引用[1]提到了bash别名的写法,引用[2]是一个Shell脚本的示例,引用[3]是一段关于控制使用的文字,引用[4]是关于Spring Boot项目结构的说明。这些引用与当前问题关联性不大,但引用[2]中的Shell脚本示例提醒我们,编写Shell脚本时可能会需要静态检查工具,因此安装ShellCheck是有益的。 为了帮助用户更好地使用Shell脚本开发,建议安装ShellCheck,因为它可以帮助发现脚本中的潜在问题。 生成相关问题: 考虑到用户可能对ShellCheck的使用和配置有进一步的问题,可以生成以下相关问题: §§相关问题§§ 1. 如何配置IDEA以使用已安装的ShellCheck? 2. ShellCheck能检查哪些常见的Shell脚本错误? 3. 除了ShellCheck,IDEA还有哪些支持Shell脚本的功能? 4. 如何在其他编辑器(如VSCode)中使用ShellCheck? </think>### 解决IDEA提示“Would you like to install shellcheck to verify your shell scripts?”的方法 该提示表示IDEA检测到您正在编辑Shell脚本(如`.sh`文件),并建议安装**ShellCheck**——一个开源的Shell脚本静态分析工具。以下是完整解决方案: #### 解决方法 1. **直接安装ShellCheck(推荐)** 在弹出提示时点击 **`Install`**,IDEA会自动下载并配置ShellCheck[^2]。安装后: - 脚本中的语法错误会实时显示波浪线提示 - 鼠标悬停可查看错误详情(如未引用的变量、语法错误等) - 支持自动修复部分问题 2. **手动安装(若自动安装失败)** ```bash # Linux (Debian/Ubuntu) sudo apt install shellcheck # macOS (Homebrew) brew install shellcheck # Windows (Scoop) scoop install shellcheck ``` 安装后在IDEA中配置路径: `Settings → Languages & Frameworks → Shell Script → ShellCheck path` → 指定安装路径 3. **禁用提示(不推荐)** 若无需此功能: - 点击提示中的 **`Don't ask again`** - 或在设置中关闭: `Settings → Editor → Inspections → Shell Script → 取消勾选ShellCheck` #### 注意事项 1. **ShellCheck的作用** 可检测脚本中的: - 语法错误(如缺少空格 `[ "${yn}"=="Y" ]` → 应为 `[ "${yn}" == "Y" ]`) - 未引用的变量(如 `rm $DIR/*` 应改为 `rm "$DIR"/*`) - 安全风险(如 `curl http://x.com | bash`) 2. **与IDEA集成的优势** 相比命令行使用,IDEA集成提供: - 实时错误高亮 - 快速修复建议(Alt+Enter) - 支持 `.sh`、`.bash`、`.zsh` 等文件 #### 验证是否生效 创建测试脚本: ```bash #!/bin/bash # test.sh file_path="/tmp" rm $file_path/* # 触发SC2086警告(未引用变量) ``` 生效后IDEA会在 `$file_path` 下方显示黄色波浪线,提示:**`Double quote to prevent globbing and word splitting`**。 > **提示**:ShellCheck特别适合检查条件语句(如`[ ]`)、变量引用等常见错误[^2],能显著提升Shell脚本的健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值