参考:什么是使用MySQL扩展的完美代码示例?

Pek*_*ica 60 php mysql security sql-injection

这是为了创建社区学习资源.目标是获得良好代码的示例,这些代码不会重复在复制/粘贴的PHP代码中经常出现的可怕错误.我已经要求它成为社区维基.

这不是一个编码竞赛.这不是要找到最快或最紧凑的查询方式 - 它是为新手提供一个好的,可读的参考.

每天都有大量的问题与使用Stack Overflow上的函数系列的非常糟糕的代码片段大量涌现mysql_*.虽然通常最好将这些人引向PDO,但它有时既不可能(例如继承的遗留软件)也不是现实的期望(用户已经在他们的项目中使用它).

使用该mysql_*库的代码的常见问题包括:

  • SQL注入值
  • LIMIT子句和动态表名中的SQL注入
  • 没有错误报告("为什么此查询不起作用?")
  • 报告错误报告(即,即使代码投入生产,也会发生错误)
  • 跨值脚本(XSS)注入值输出

让我们编写一个PHP代码示例,使用mySQL_*系列函数执行以下操作:

  • 接受两个POST值,id(数字)和name(一个字符串)
  • 对表执行UPDATE查询tablename,name使用ID 更改行中的列id
  • 失败时,请优雅退出,但仅在生产模式下显示详细错误.trigger_error()就足够了; 或者使用您选择的方法
  • 输出消息" $name已更新".

并没有显示上面列出的任何弱点.

它应该尽可能简单.理想情况下,它不包含任何函数或类.目标不是创建一个复制/可粘贴的库,而是显示为使数据库查询安全所需要做的最少的事情.

好评如有好处.

目标是使这个问题成为用户在遇到问题提供者时可以链接到的资源(即使它根本不是问题的焦点),或者遇到错误的查询而不是知道如何解决它.

要抢先讨论PDO讨论:

是的,将这些问题的个人指向PDO往往更为可取.如果是一种选择,我们应该这样做.然而,这并不总是可能的 - 有时,提问者正在处理遗留代码,或者已经在这个库中走了很长一段路,并且现在不太可能改变它.此外,mysql_*如果正确使用,功能系列是完全安全的.所以请不要在这里"使用PDO".

Zna*_*kus 12

我刺伤了它.尽量保持尽可能简单,同时仍然保持一些现实世界的便利.

处理unicode并使用松散比较以提高可读性.对人好点 ;-)

<?php

header('Content-type: text/html; charset=utf-8');
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
// display_errors can be changed to 0 in production mode to
// suppress PHP's error messages

/*
Can be used for testing
$_POST['id'] = 1;
$_POST['name'] = 'Markus';
*/

$config = array(
    'host' => '127.0.0.1', 
    'user' => 'my_user', 
    'pass' => 'my_pass', 
    'db' => 'my_database'
);

# Connect and disable mysql error output
$connection = @mysql_connect($config['host'], 
    $config['user'], $config['pass']);

if (!$connection) {
    trigger_error('Unable to connect to database: ' 
        . mysql_error(), E_USER_ERROR);
}

if (!mysql_select_db($config['db'])) {
    trigger_error('Unable to select db: ' . mysql_error(), 
        E_USER_ERROR);
}

if (!mysql_set_charset('utf8')) {
    trigger_error('Unable to set charset for db connection: ' 
        . mysql_error(), E_USER_ERROR);
}

$result = mysql_query(
    'UPDATE tablename SET name = "' 
    . mysql_real_escape_string($_POST['name']) 
    . '" WHERE id = "' 
    . mysql_real_escape_string($_POST['id']) . '"'
);

if ($result) {
    echo htmlentities($_POST['name'], ENT_COMPAT, 'utf-8') 
        . ' updated.';
} else {
    trigger_error('Unable to update db: ' 
        . mysql_error(), E_USER_ERROR);
}
Run Code Online (Sandbox Code Playgroud)

  • -1表示`#error_reporting(~E_ALL); ...禁用错误输出` - 糟糕的建议.不能downvote更多,否则我会-1为`@`运算符. (5认同)
  • @Pekka,@ Znarkus,`error_reporting(0);`这是非常糟糕的建议,因为并不总是错误将由错误处理程序处理,因为并不总是这个处理程序将被编码.`ini_set('display_errors',0)` - 此变体可用于生产代码,隐藏错误文本,并且仅在处理错误时使用. (3认同)

Aar*_*ron 7

我决定跳枪,然后放一些东西.这是开始的事情.出错时抛出异常.

function executeQuery($query, $args) {
    $cleaned = array_map('mysql_real_escape_string', $args);

    if($result = mysql_query(vsprintf($query, $cleaned))) {
        return $result;
    } else {
        throw new Exception('MySQL Query Error: ' . mysql_error());
    }
}

function updateTablenameName($id, $name) {
    $query = "UPDATE tablename SET name = '%s' WHERE id = %d";

    return executeQuery($query, array($name, $id));
}

try {
    updateTablenameName($_POST['id'], $_POST['name']);
} catch(Exception $e) {
    echo $e->getMessage();
    exit();
}
Run Code Online (Sandbox Code Playgroud)

  • 尽管它有效,但对于新手和复制粘贴人来说,它太复杂了. (3认同)
  • 错误,这不会将$ id值分配给name和$ name到id吗?除了在函数调用updateTablenameName:p时调用updateTableName这一事实. (2认同)