关于从mysql转向PDO的问题
我最近决定将当前使用 php mysql_query 执行的所有普通 mysql 查询切换为 PDO 样式查询,以提高性能、可移植性和安全性。我只是想向这个数据库交互工具的专家提出一些简单的问题
如果准备好所有语句,它会防止注入吗? (我注意到 php.net 上写着“但是,如果查询的其他部分是使用未转义的输入构建的,SQL 注入仍然是可能的”,我不太确定这意味着什么)。这是否意味着如果所有变量都通过准备函数运行它是安全的,而如果直接插入某些变量则不安全?
目前,我在页面顶部有一个连接,并在页面的其余部分执行查询。我更详细地查看了 PDO,并注意到对于涉及连接和关闭该连接的每个查询都有一个 try 和 catch 过程。有没有一种简单的方法来连接然后重用该连接,而不必将所有内容都放在尝试中或通过连接、查询和关闭不断重复该过程?
任何人都可以用外行的术语简要解释一下 set_exception_handler 的用途吗?
我感谢任何更有经验的人的建议。
I have recently decided to switch all my current plain mysql queries performed with php mysql_query to PDO style queries to improve performance, portability and security. I just have some quick questions for any experts in this database interaction tool
Will it prevent injection if all statements are prepared? (I noticed on php.net it wrote 'however, if other portions of the query are being built up with unescaped input, SQL injection is still possible' I was not exactly sure what this meant). Does this just mean that if all variables are run through a prepare function it is safe, and if some are directly inserted then it is not?
Currently I have a connection at the top of my page and queries performed during the rest of the page. I took a look at PDO in more detail and noticed that there is a try and catch procedure for every query involving a connection and the closing of that connection. Is there a straightforward way to connecting and then reusing that connection without having to put everything in a try or constantly repeat the procedure by connecting, querying and closing?
Can anyone briefly explain in layman's terms what purpose a set_exception_handler serves?
I appreciate any advice from any more experienced individuals.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
预防 SQL 注入没有灵丹妙药。即使您准备好了查询,也可能会出现不安全的查询。
看到了吗?准备只需要一个字符串并将其准备为 SQL 查询。在准备之前,您仍然可以将不安全内容插入到字符串中。准备只看到一个字符串,它不知道您是否按字面意思编写它,或者字符串的某些部分是否来自不可信的来源。
您可以在 SQL 查询中使用参数占位符,然后在对准备好的语句调用
execute()
时,提供动态值。但是您只能使用参数占位符来代替 SQL 表达式中的文字值 - 查询中其他类型的动态内容无法参数化。请参阅我的演示SQL 注入神话和谬误,了解示例和大量内容有关 SQL 注入的其他信息。我不会将每个 PDO 调用都放在 try 块中。我编写了一个类来封装应用程序中某些逻辑内聚部分的数据访问。当我调用该类时,我将调用包装在 try 块中。如果该类中可能存在的许多数据库访问操作之一出现问题,我会捕获它并进行处理。
您可以使用
set_exception_handler()
代替catch
块。如果您的应用程序中发生异常,但您没有捕获它,并且它会一直在堆栈中向上冒泡,直到它中止脚本,则会调用此函数。想象一下,您的整个 PHP 脚本位于一个顶级try
块中,并且您正在声明将进入相应catch
块的代码。我从不使用
set_exception_handler()
。函数运行后,您的脚本无论如何都会停止执行,因此没有机会重试引发异常的操作。而且它在顶级范围内运行,因此您会丢失异常的上下文。此时您唯一能做的就是漂亮地打印异常消息并退出。我更喜欢在更接近其起源的地方处理异常,因此我可以添加一些有关异常上下文的信息,或者在 PHP 脚本停止之前执行一些其他操作。回复您的评论:
您不应该使用
mysql_real_escape_string()
作为表名或列名,因为引用标识符的规则与引用文字字符串值的规则不同。只是不要将来自外部或不受信任来源的输入插入到 SQL 查询中。我在演示文稿中使用关联数组编写了一个代码示例,这样如果用户输入与已知值匹配,它就会使用该值作为关联数组中的键来查找表(或列,在我的示例中)的合法名称。这意味着您不必使用任何转义/引用函数,因为您不会将不受信任的内容插入到 SQL 查询中。您只能插入在关联数组中预定义的值。
关于异常,我的意思是(在高层次上):
create_report() 内部的工作很复杂,可能涉及多个 SQL 查询,其中任何一个查询都可能以多种方式出错。您不一定需要捕获该函数内每个 SQL 操作的异常,您可以捕获从该函数中弹出的所有异常,并在调用 create_report( 的代码中) 在一个位置处理它们)。
另外,您可能不想只向他们逐字抛出异常消息,因为他们不知道该怎么做。
There's no silver bullet of SQL injection prevention. One can have an unsafe query even though you prepare it.
See? Prepare just takes a string and prepares it as an SQL query. You can still interpolate unsafe content into the string before you prepare it. The prepare only sees a string, it doesn't know whether you wrote it literally or if parts of the string came from untrustworthy sources.
You can use a parameter placeholder in the SQL query and then when you call
execute()
on the prepared statement, you supply the dynamic value. But you can use a parameter placeholder only in place of a literal value in an SQL expression -- other types of dynamic content in a query can't be parameterized. See my presentation SQL Injection Myths and Fallacies for examples and lots of other info about SQL injection.I don't put every PDO call in a try block. I write a class to encapsulate data access for some logically cohesive portion of my app. When I call that class, I wrap the call in a try block. If anything goes wrong in one of potentially many database access operations within that class, I catch it and deal with it.
You can use
set_exception_handler()
in lieu of acatch
block. If an exception occurs in your app, but you don't catch it and it bubbles all the way up the stack until it would have aborted the script, this function is called. Imagine your whole PHP script is in one top-leveltry
block, and you're declaring code that would go in the correspondingcatch
block.I never use
set_exception_handler()
. After the function runs, your script halts execution anyway, so there's no opportunity to re-try the operation that spawned the exception. Also it operates at the top-level scope, so you lose the context of exception. The only thing you can do at that point is pretty-print the exception message and bail out. I prefer to handle exceptions closer to their origin, so I can add some information about the context of the exception, or do some other things before the PHP script halts.Re your comments:
You shouldn't use
mysql_real_escape_string()
for table names or column names, because the rules for quoting identifiers are different from the rules for quoting literal string values. Just don't interpolate input from an external or untrusted source into your SQL query.I wrote a code example in my presentation using an associative array so that if user input matches a known value, it uses that as a key in the associative array to look up the legitimate name of a table (or column, in my example). This means you don't have to use any escaping/quoting function, because you don't interpolate untrusted content into your SQL query. You only interpolate values that you have pre-defined in your associative array.
Regarding exceptions, what I mean is this (at a high level):
The work inside
create_report()
is complex and probably involves multiple SQL queries, any of which might go wrong in multiple ways. You don't necessarily need to catch exceptions for every SQL operation inside that function, you could just catch any and all exceptions that pop out of the function, and deal with them in one place, in the code that callscreate_report()
.Also, you probably don't want to just spew the verbatim exception message at them, since they won't know what to make of that.