此 PHP 代码中是不是存在任何安全漏洞? [关闭]
Posted
技术标签:
【中文标题】此 PHP 代码中是不是存在任何安全漏洞? [关闭]【英文标题】:Are there any security vulnerabilities in this PHP code? [closed]此 PHP 代码中是否存在任何安全漏洞? [关闭] 【发布时间】:2011-02-02 05:31:21 【问题描述】:我刚要管理一个网站,但对前人编写的代码不太确定。我正在粘贴下面的登录过程,你能看看并告诉我是否有任何安全漏洞吗?乍一看,似乎可以通过 SQL 注入或操作 cookie 和 ?m= 参数进入。
define ( 'CURRENT_TIME', time ()); / / Current time.
define ( 'ONLINE_TIME_MIN', (CURRENT_TIME - BOTNET_TIMEOUT)); / / Minimum time for the status of "Online".
define ( 'DEFAULT_LANGUAGE', 'en'); / / Default language.
define ( 'THEME_PATH', 'theme'); / / folder for the theme.
/ / HTTP requests.
define ( 'QUERY_SCRIPT', basename ($ _SERVER [ 'php_SELF']));
define ( 'QUERY_SCRIPT_html', QUERY_SCRIPT);
define ( 'QUERY_VAR_MODULE', 'm'); / / variable contains the current module.
define ( 'QUERY_STRING_BLANK', QUERY_SCRIPT. '? m ='); / / An empty query string.
define ( 'QUERY_STRING_BLANK_HTML', QUERY_SCRIPT_HTML. '? m ='); / / Empty query string in HTML.
define ( 'CP_HTTP_ROOT', str_replace ( '\ \', '/', (! empty ($ _SERVER [ 'SCRIPT_NAME'])? dirname ($ _SERVER [ 'SCRIPT_NAME']):'/'))); / / root of CP.
/ / The session cookie.
define ( 'COOKIE_USER', 'p'); / / Username in the cookies.
define ( 'COOKIE_PASS', 'u'); / / user password in the cookies.
define ( 'COOKIE_LIVETIME', CURRENT_TIME + 2592000) / / Lifetime cookies.
define ( 'COOKIE_SESSION', 'ref'); / / variable to store the session.
define ( 'SESSION_LIVETIME', CURRENT_TIME + 1300) / / Lifetime of the session.
////////////////////////////////////////////////// /////////////////////////////
/ / Initialize.
////////////////////////////////////////////////// /////////////////////////////
/ / Connect to the database.
if (! ConnectToDB ()) die (mysql_error_ex ());
/ / Connecting topic.
require_once (THEME_PATH. '/ index.php');
/ / Manage login.
if (! empty ($ _GET [QUERY_VAR_MODULE]))
(
/ / Login form.
if (strcmp ($ _GET [QUERY_VAR_MODULE], 'login') === 0)
(
UnlockSessionAndDestroyAllCokies ();
if (isset ($ _POST [ 'user']) & & isset ($ _POST [ 'pass']))
(
$ user = $ _POST [ 'user'];
$ pass = md5 ($ _POST [ 'pass']);
/ / Check login.
if (@ mysql_query ( "SELECT id FROM cp_users WHERE name = '". addslashes ($ user). "' AND pass = '". addslashes ($ pass). "' AND flag_enabled = '1 'LIMIT 1") & & @ mysql_affected_rows () == 1)
(
if (isset ($ _POST [ 'remember']) & & $ _POST [ 'remember'] == 1)
(
setcookie (COOKIE_USER, md5 ($ user), COOKIE_LIVETIME, CP_HTTP_ROOT);
setcookie (COOKIE_PASS, $ pass, COOKIE_LIVETIME, CP_HTTP_ROOT);
)
LockSession ();
$ _SESSION [ 'Name'] = $ user;
$ _SESSION [ 'Pass'] = $ pass;
/ / UnlockSession ();
header ( 'Location:'. QUERY_STRING_BLANK. 'home');
)
else ShowLoginForm (true);
die ();
)
ShowLoginForm (false);
die ();
)
/ / Output
if (strcmp ($ _GET [ 'm'], 'logout') === 0)
(
UnlockSessionAndDestroyAllCokies ();
header ( 'Location:'. QUERY_STRING_BLANK. 'login');
die ();
)
)
////////////////////////////////////////////////// /////////////////////////////
/ / Check the login data.
////////////////////////////////////////////////// /////////////////////////////
$ logined = 0, / / flag means, we zalogininy.
/ / Log in session.
LockSession ();
if (! empty ($ _SESSION [ 'name']) & &! empty ($ _SESSION [ 'pass']))
(
if (($ r = @ mysql_query ( "SELECT * FROM cp_users WHERE name = '". addslashes ($ _SESSION [' name'])."' AND pass = ' ". addslashes ($ _SESSION [' pass']). " 'AND flag_enabled = '1' LIMIT 1 ")))$ logined = @ mysql_affected_rows ();
)
/ / Login through cookies.
if ($ logined! == 1 & &! empty ($ _COOKIE [COOKIE_USER]) & &! empty ($ _COOKIE [COOKIE_PASS]))
(
if (($ r = @ mysql_query ( "SELECT * FROM cp_users WHERE MD5 (name )='". addslashes ($ _COOKIE [COOKIE_USER ])."' AND pass = '". addslashes ($ _COOKIE [COOKIE_PASS]). " 'AND flag_enabled = '1' LIMIT 1 ")))$ logined = @ mysql_affected_rows ();
)
/ / Unable to login.
if ($ logined! == 1)
(
UnlockSessionAndDestroyAllCokies ();
header ( 'Location:'. QUERY_STRING_BLANK. 'login');
die ();
)
/ / Get the user data.
$ _USER_DATA = @ Mysql_fetch_assoc ($ r);
if ($ _USER_DATA === false) die (mysql_error_ex ());
$ _SESSION [ 'Name'] = $ _USER_DATA [ 'name'];
$ _SESSION [ 'Pass'] = $ _USER_DATA [ 'pass'];
/ / Connecting language.
if (@ strlen ($ _USER_DATA [ 'language'])! = 2 | |! SafePath ($ _USER_DATA [ 'language']) | |! file_exists ( 'system / lng .'.$_ USER_DATA [' language '].' . php'))$_ USER_DATA [ 'language'] = DEFAULT_LANGUAGE;
require_once ( 'system / lng .'.$_ USER_DATA [' language'].'. php ');
UnlockSession ();
【问题讨论】:
附上本站地址,我会告诉你的。 :) 使用 addlashes(md5($pass)) 是多余的。 SQL 注入不能让它认为是 md5(),它可以有时让它通过addslashes()。 md5() 也永远不会生成单引号、双引号、反斜杠或空字节,因此addslashes() 永远不会对 md5() 哈希做任何事情。最好使用 adodb 和参数化查询。 【参考方案1】:接受的答案在一些事情上遗漏了很多和错误。以下是我在代码中看到的漏洞:
define('QUERY_SCRIPT', basename($_SERVER['PHP_SELF']));
正如其他地方所说,这可以包含的不仅仅是脚本路径。请改用$_SERVER['SCRIPT_NAME']
或__FILE__
。
define('CP_HTTP_ROOT', ...
此常量用于将cookie路径设置为脚本路径。这不是不安全的,但用户需要单独登录每个脚本。而是使用会话(下面讨论)并为您的应用设置一个基本路径。
UnlockSessionAndDestroyAllCokies()
我不知道这是在做什么,但听起来不太好。 只需在脚本的早期为每个请求启动会话。检查会话中的现有用户信息以了解他们是否已经登录。
$pass = md5($_POST['pass']);
每个散列的密码应该有一个唯一的盐,最好使用更好的散列算法。 这些天(PHP 5.5+)您应该使用password_hash
和password_verify
为您处理密码哈希详细信息。
mysql_query("SELECT id FROM cp_users WHERE name = '". addslashes($user)...
这是一个老问题,但今天不再支持mysql_
PHP 函数。使用mysqli
或 PDO。 使用不受支持的库会使您面临未修补的漏洞。 addslashes
不是针对 SQL 注入的完美保护。 使用准备好的语句,或至少使用库的字符串转义函数。
if (isset($_POST['remember']) && $_POST['remember'] == 1)
(
setcookie(COOKIE_USER, md5($user), COOKIE_LIVETIME, CP_HTTP_ROOT);
setcookie(COOKIE_PASS, $pass, COOKIE_LIVETIME, CP_HTTP_ROOT);
)
这是最大的问题。 Cookie 正在存储用户凭据。您正在将散列用户名和散列密码作为 cookie 值与响应一起发回。第 3 方可以轻松阅读和使用这些内容。由于该脚本使用简单的散列,彩虹表将让某人查找许多用户密码。但由于响应包含“安全”凭据,攻击者无需执行任何操作,只需将它们传递给任何其他登录请求。这不是“记住”用户的正确方法,也没有必要。
用户与请求的连接应仅在会话中处理。这就是他们的目的。请求和响应包含一个带有随机标识符的 cookie。 用户详细信息仅保留在服务器上并链接到此标识符。 (旁注:这个块是用括号而不是大括号包裹的错误。)
$_SESSION['Pass'] = $pass;
现在用户的密码存储在两个地方:数据库和会话存储。如果会话存储在数据库之外(并且 PHP 默认将它们存储在磁盘上),那么您现在有两种方法可以尝试窃取用户凭据。
if ($logined !== 1 && !empty($_COOKIE[COOKIE_USER]) ...
if (($r = @mysql_query("SELECT * FROM cp_users WHERE MD5(name)='". addslashes($_COOKIE [COOKIE_USER ])."' AND pass = '". addslashes($_COOKIE [COOKIE_PASS])..
攻击者现在可以通过简单地在请求中传递 cookie 标头以及用户名和密码的 md5 散列来尝试登录,该用户名和密码已在之前的响应中传递给他们。登录表单应该是获取用户凭据并将其登录的唯一地方。此表单(以及所有其他表单)应使用 CSRF 令牌。
UnlockSession();
同样,我不知道这是在做什么,但在脚本结束时,会话应该被写入存储。
【讨论】:
【参考方案2】:是的,此代码中存在一些漏洞。
这可能是个问题:
define ( 'QUERY_SCRIPT', basename ($ _SERVER [ 'PHP_SELF']));
PHP_SELF
不好,因为攻击者可以控制这个变量。例如,当您使用以下 URL 访问脚本时尝试打印 PHP_SELF
:http://localhost/index.php/test/junk/hacked
。尽可能避免使用此变量,如果您确实使用它,请确保对其进行消毒。使用此变量时,很常见会出现 XSS。
第一个漏洞:
setcookie (COOKIE_USER, md5 ($ user), COOKIE_LIVETIME, CP_HTTP_ROOT);
setcookie (COOKIE_PASS, $ pass, COOKIE_LIVETIME, CP_HTTP_ROOT);
这是一个相当严重的漏洞。如果攻击者在您的应用程序中进行了 SQL 注入,那么他们可以获取 md5 哈希和用户名并立即登录,而无需破坏 md5()
哈希。就好像您以明文形式存储密码一样。
这个会话漏洞是双重的,它也是一个“不朽的会话”,会话 id 必须始终是过期的大随机生成值。如果它们没有过期,那么它们更容易暴力破解。
您应该绝不重新发明***,在您的应用程序开始时调用session_start()
,这将自动生成一个过期的安全会话ID。然后使用$_SESSION['user']
之类的会话变量来跟踪浏览器是否实际登录。
第二个漏洞:
$ pass = md5 ($ _POST [ 'pass']);
md5()
被证明是不安全的,因为冲突是故意产生的。 md5() 应该从不用于密码。您应该使用 sha2 系列的成员,sha-256 或 sha-512 是不错的选择。
第三个漏洞:
CSRF
我没有看到任何针对您的身份验证逻辑的 CSRF 保护。我怀疑您的应用程序中的所有请求都容易受到 CSRF 的攻击。
【讨论】:
我不同意 md5 部分。冲突漏洞对于密码哈希并不重要,所以这不是 md5 在这里不好的原因。相关的攻击是原像。普通 SHA-2 在这里几乎不比 MD5 好。重要的是使用盐和慢散列函数,最好是 bcrypt 或 PBKDF2。 @CodeInChaos bcrypt 会是一个不错的选择,而且 sha1 比 md5 好很多。这是一个 5 字节的变化,使系统更难被攻击。 md5也有很多问题,不仅仅是前缀攻击,它的使用凸显了安全无知。 如果有人参考这篇文章,PHP 5.5.0 及更高版本具有使用 bcrypt 生成哈希和盐的函数 password_hash 和 password_verify,它简单而高效。查看更多:php.net/manual/en/function.password-hash.phpphp.net/manual/en/function.password-verify.php以上是关于此 PHP 代码中是不是存在任何安全漏洞? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
在提交到 SVN 之前,是不是有任何预提交验证来验证代码的注释/java-docs 是不是存在? [关闭]
代码安全 | 第十八期:调用System.exit()存在安全漏洞