一篇文章教会你通过专业的角度去代码审计DVWA及修复方法

前言

在这个数字化时代,安全已经成为最重要的关键词之一。对于开发人员和安全专业人员来说,代码审计是确保应用程序和服务安全性的一项重要任务。通过审计,可以发现并修复各种类型的漏洞,包括 SQL 注入、XSS、CSRF、文件上传漏洞等。愿你们在代码审计的道路上勇往直前,不断学习和探索,找到应用程序中的所有漏洞,并将其修复,为我们的数字世界带来更加安全可靠的服务。

Brute Force爆破

Low

<?php
    //检测内容是否为空
if( isset( $_GET[ 'Login' ] ) ) {
    // Get username    通过get传参接收user
    $user = $_GET[ 'username' ]; 

    // Get password   通过get传参接收password
    $pass = $_GET[ 'password' ]; 
    $pass = md5( $pass );

    // Check the database   将过滤后的username和password拼接到SQL语句里
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    //将拼接后的SQL语句带入数据库执行
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    //判断返回的结果是否为真
    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details   将返回结果以数组的形式返回
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful      提示登录成功
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed   如果if条件不满足则提示账号或密码错误
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }
      //关闭数据库连接

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

分析思路:

这是一个 PHP 代码段,其功能是检查用户输入的用户名和密码是否正确。但是,它存在多个潜在漏洞,具体情况如下:

  1. 注入攻击:该代码使用GET方法接收两个参数,用户名和密码。但是,它直接将两个参数用于SQL语句的拼接,而没有对它们进行过滤或验证。这使得攻击者可以通过使用注入攻击轻松地绕过身份验证和访问受保护的内容。

  2. 明文密码:该代码通过md5函数加密不安全的明文密码。不要使用 md5 加密用户密码,因为 MD5 已经被证明是容易被破解的可逆算法,应选择更安全的加密算法,如 SHA-256 和 bcrypt 等。

  3. mysqli 查询:在 mysqli_query 函数中,没有正确使用 Prepared Statements(预编译语句)的方式来防止 SQL 注入攻击。对于这种情况,使用参数化查询可以有效地防止 SQL 注入攻击。

除了上述漏洞之外,该代码还存在一些其他问题,如未能正确关闭数据库连接,缺少错误处理机制等。需要对其进行优化才能提高安全性。

Medium

<?php
    //检测内容是否为空
if( isset( $_GET[ 'Login' ] ) ) {
    // Sanitise username input    使用mysqli_real_escape_string()函数过滤传入的数据user
    $user = $_GET[ 'username' ];
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitise password input  使用mysqli_real_escape_string()函数过滤传入的数据password
    $pass = $_GET[ 'password' ];
    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    //将password利用md5加密
    $pass = md5( $pass );

    // Check the database  将过滤后的username和password拼接到SQL语句里
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";

    //将拼接后的SQL语句带入数据库执行
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
    //判断返回的结果是否为真
    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details  将返回结果以数组的形式返回
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful     提示登录成功
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed   如果if条件不满足则休眠2秒,然后提示账号或密码错误
        sleep( 2 );
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }
    //关闭数据库连接
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?> 

分析思路:

该代码段修复了第一种漏洞——SQL 注入攻击,因为它使用了 mysqli_real_escape_string 函数来过滤用户输入的用户名和密码。但是,它仍然存在一些其他潜在的安全风险,具体如下:

  1. 明文密码:该代码使用 md5 函数来加密密码。应选择更安全的密码算法,如 bcrypt 或 SHA-256 等,因为 md5 已经被证明是容易被破解的可逆算法。

  2. 判断操作是否成功:使用 $result 和 mysqli_num_rows() 函数来判断操作是否成功。但是,这并不是一个可靠的方法,因为它可能会产生一些已知的安全隐患。因此,应该使用更可靠的方法,如 Prepared Statements 或 PDO 等方式来防止 SQL 注入攻击。

    3.回显错误信息:该代码可以通过休眠来防止暴力破解密码,但是仍然通过返回错误信息告知攻击者是密码错误。攻击者可以利用这个信息来了解密码是否正确,并继续进行攻击。因此,可以考虑隐藏错误信息,而是记录失败的登录信息,以后分析入侵者的攻击方式和试图登录的用户信息。

总的来说,虽然该代码修复了 SQL 注入漏洞,但仍需要进一步优化来提高其安全性。例如使用更强大的加密算法,使用参数化查询和处理错误信息,以加强防御暴力攻击和其他攻击。

High

<?php
//检测内容是否为空
if( isset( $_GET[ 'Login' ] ) ) {
    // Check Anti-CSRF token  检查token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Sanitise username input  通过get传参接收user
    $user = $_GET[ 'username' ];

    //使用stripslashes()函数用于删除反斜杠
    $user = stripslashes( $user );
    
    //使用mysqli_real_escape_string()函数过滤传入的数据user
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitise password input     通过get传参接收password
    $pass = $_GET[ 'password' ];

    //使用stripslashes()函数用于删除反斜杠
    $pass = stripslashes( $pass );

    //使用mysqli_real_escape_string()函数过滤传入的数据user
    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    //将password使用md5函数加密
    $pass = md5( $pass );   

    // Check database  将过滤后的username和password拼接到SQL语句里
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";

   //将拼接后的SQL语句带入数据库执行
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
 
    //判断返回的结果是否为真
    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details   将返回结果以数组的形式返回
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful   提示登录成功
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        sleep( rand( 0, 3 ) ); 如果if条件不满足则随机休眠0-3秒后提示账号或密码错误
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

   //关闭数据库连接
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

// Generate Anti-CSRF token   重新生成新的token
generateSessionToken();

?> 

分析思路:

该代码段修复了前两个PHP代码段的漏洞,即使用了 mysqli_real_escape_string() 函数来过滤用户输入,并使用 stripslashes() 函数删除反斜杠。还使用了一个防止 CSRF 攻击的 token 机制来保护登录表单。

但是,它仍存在以下潜在漏洞:

  1. 过于简单的加密算法:该代码使用 md5 函数来加密密码。MD5 算法已被证明是不安全的,应使用更强大和更安全的算法,如 bcrypt 和 SHA-256。

  2. CSRF攻击的特点是攻击者发起攻击并利用被攻击者的登录状态。但是,如果攻击者能够控制 victim 登录传递给自己的 cookie,CSRF 的 token 机制也被攻击者攻击。

  3. SQL 注入攻击:该代码虽然使用了 mysqli_real_escape_string() 来过滤输入,但这并不是一种足够强大的防御 SQL 注入攻击的方法。更好的做法是使用 Prepared Statements 或 PDO 等方式来防止 SQL 注入攻击。

  4. 反爬虫等攻击:由于 sleep() 函数在代码执行期间暂停了几秒钟,攻击者可以通过频繁尝试登录的方式来执行一种 能够占领服务器资源的反爬虫攻击.

综上,该代码段通过使用 token 和一些基本的输入过滤来加强了安全性,但仍存在其他漏洞,可以采取其他措施如使用更强大的加密算法,采取更为强大的 anti-csrf 技术,并寻找其他方法防御 SQL 注入等攻击。

Command Injection命令注入

Low

<?php
//检测内容是否为空
if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input   通过post方式提交用户输入,然后使用request函数进行接收变量ip
    $target = trim($_REQUEST[ 'ip' ]);

    // Determine OS and execute the ping command.
    //使用stristr()函数对服务器系统进行判断
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows   使用shell_exec函数执行系统命令
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix      使用shell_exec函数执行命令
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user  输出执行后的数据
    echo "<pre>{$cmd}</pre>";
}

?> 

分析思路:

该代码段存在一个严重的安全漏洞,允许用户执行任意的命令,称为命令注入攻击,具体原因如下:

  1. 用户可控变量:该代码允许用户通过提交一个IP地址来执行 ping 命令,攻击者可以在这个变量中放入任意命令,从而控制服务器上的命令执行。

  2. 使用 shell_exec() 函数:该函数是一种可执行操作系统命令的函数,使用时应特别小心,因为它可以在服务器上执行任意的命令,并且不提供保护机制。如果认为用户输入的是不可信任的数据,则不应直接执行 shell_exec() 函数。

  3. 缺乏输入验证和过滤:该代码没有对用户输入进行验证和过滤,攻击者可以在输入中注入任何命令。因为操作系统命令是可执行的,所以在执行命令之前,应该对输入进行严格的过滤和验证,以防止命令注入攻击。

因此,为了修复这个安全问题,应该对用户输入进行验证和限制,并使用可靠的方法,如 escapeshellarg() 函数来转义和过滤 shell 命令的参数。最好的解决方法是避免使用 shell_exec() 和 exec() 等函数,而是使用更加安全的用于执行命令的替代方案,如使用 PHP 模块来执行命令的特定 API,而不是直接在服务器上执行一些系统命令。

Medium

<?php
//检测内容是否为空
if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input   通过post方式提交用户输入,然后使用request函数进行接收变量ip
    $target = trim($_REQUEST[ 'ip' ]);

    // Set blacklist    设置黑名单
    $substitutions = array(
        '&&'  => '',
        ';'  => '',
       
    );

    // Remove any of the characters in the array (blacklist).
    //使用str_replace()函数将用户传入的值与黑名单进行匹配并替换
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    //使用stristr()函数对服务器系统进行判断
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows   使用shell_exec函数执行系统命令
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix      使用shell_exec函数执行命令
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user  输出执行后的数据
    echo "<pre>{$cmd}</pre>";
}

?> 

分析思路:

该代码使用了一些输入过滤和黑名单机制来防止命令注入攻击。然而,仍存在一些潜在问题:

1. 只使用了黑名单:该代码段只定义了一些黑名单中的字符,并没有对这些字符进行全面的过滤和替换。因此,如果攻击者尝试使用其他不在黑名单中的特殊字符或命令注入技巧,仍然可以成功地执行任意的命令。

2. 仍然使用了 shell_exec():该代码段仍然使用 shell_exec() 函数来执行 ping 命令。shell_exec() 函数本身就具有很高的风险,并容易受到命令注入攻击,在这种情况下应该避免使用该函数,或者使用更可靠的方式来执行命令。

3. 缺乏错误处理机制:该代码段没有提供任何错误处理机制,当遇到错误时,用户只会看到一个空白页面或者是错误信息,这会导致用户体验和安全性都受到影响。

因此,要修复这些问题,应该使用更完整的输入过滤机制来避免漏洞。最好的方法是使用白名单策略,只接受合法的输入数据,并在执行命令时使用更安全的方式,如使用 PHP 模块来执行命令的特定 API,而不是直接在服务器上执行一些系统命令。同时还应该在代码中添加错误处理机制,使得错误的发生不会影响到用户的体验。

High

<?php
//检测内容是否为空
if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input   通过post方式提交用户输入,然后使用request函数进行接收变量ip
    $target = trim($_REQUEST[ 'ip' ]);

    // Set blacklist    设置黑名单
    $substitutions = array(
        '&'  => '',
        ';'  => '',
        '| ' => '',
        '-'  => '',
        '$'  => '',
        '('  => '',
        ')'  => '',
        '`'  => '',
        '||' => '',
    );

    // Remove any of the characters in the array (blacklist).
    //使用str_replace()函数将用户传入的值与黑名单进行匹配并替换
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    //使用stristr()函数对服务器系统进行判断
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows   使用shell_exec函数执行系统命令
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix      使用shell_exec函数执行命令
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user  输出执行后的数据
    echo "<pre>{$cmd}</pre>";
}

?> 

分析思路:

该代码段可以防止一些命令注入攻击,因为它使用了黑名单机制来过滤一些特殊字符,但是仍存在以下问题:

  1. 只使用了黑名单机制:这个代码段只提供了一个黑名单列表,并没有考虑其他可能的攻击场景。例如,如果攻击者使用编码或其他逃避技巧,就可能绕过该黑名单,并成功执行恶意命令。因此,只依赖于一个黑名单机制是不够的,应该采用更全面的方法来过滤和验证输入数据。

  2. 仍然使用了 shell_exec():即使该代码段使用了黑名单机制来过滤用户输入,仍然使用 shell_exec() 函数来执行 ping 命令,这仍然是一个非常危险的做法,容易受到命令注入攻击。

因此,为了增强代码的安全性,可以采用以下建议:

  1. 使用白名单机制:白名单比黑名单更可靠,应该采用白名单机制来验证和过滤输入数据,只接受已定义的合法数据,拒绝其他不可信数据。

  2. 使用更安全的方式来执行命令:避免在服务器上执行系统命令,可以使用 PHP 模块的某些 API,如 escapeshellcmd() 和 exec() 等函数等来执行命令,这些函数具有更高的安全性,能够更好地保护服务器和应用程序。

综上所述,完善的安全机制应该采取防范措施和安全措施相结合的策略,在程序设计和开发过程中考虑各种安全场景,并使用更安全,更完整的方法来保护应用程序和服务器。

Cross Site Request Forgery (CSRF)跨站点请求伪造

Low

<?php
//检测内容是否为空
if( isset( $_GET[ 'Change' ] ) ) {
    // Get input  通过get传参接收password数据
    $pass_new  = $_GET[ 'password_new' ];      
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?   比对两次密码输入是否一致
    if( $pass_new == $pass_conf ) {
        // They do!    使用mysqli_real_escape_string()函数将新的密码进行过滤
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

        //使用md5()函数进行加密
        $pass_new = md5( $pass_new );

        // Update the database   将新密码插入到数据库并且更新数据
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the user     提示密码修改成功
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching   提示两次密码属于不一致
        echo "<pre>Passwords did not match.</pre>";
    }

     //关闭数据库连接
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

分析思路:

该代码段有以下几个主要问题:

  1. 只使用了 mysqli_real_escape_string() 进行过滤:该代码数据接收了用户传入的密码参数,并仅使用 mysqli_real_escape_string() 函数作为数据过滤的手段,没有综合考虑其他可能的攻击场景。这种做法可能会存在 SQL 注入或其他类型的攻击风险。

  2. 使用了 MD5 加密:该代码段仅仅使用了 MD5 来加密用户输入的密码,这种做法已经过时,现今常见的密码加密方式是使用 PBKDF2、bcrypt 或 scrypt 这样的加密算法,它们的安全性更高。

  3. 错误处理缺失:在插入查询执行失败时,该代码段只是简单地输出了一个错误消息,而没有提供充分的错误处理机制,响应信息可能会泄漏敏感信息,并提供攻击者足够的信息来发起攻击。

因此,要增强代码的安全性,应该采用以下建议:

  1. 使用更全面的过滤和验证机制:应该使用多种方法对数据进行过滤和验证,包括使用白名单和正则表达式,过滤掉无效或不安全的数据或格式。例如,在接收密码数据时,可以使用 password_hash() 函数和 password_verify() 函数配合使用,将密码进行更彻底的加密和验证。

  2. 采用错误处理机制:合适的错误处理机制应当协同管理,不仅在调用 mysqli_query() 时,但也在其他操作发生错误时产生提示。同时,在应答消息中不应该因错误而泄漏敏感信息,以掩盖内部信息。

  3. 定期更新加密算法:应该使用最新的密码加密算法,例如 bcrypt 或 scrypt,而且还应该定期更新加密算法与版本,以确保密文在未来依然是安全的。

因此,在安全程序的开发过程中,采用这些措施可以减少不同类型的安全漏洞,包括 SQL 注入,XSS 攻击,不恰当的密码存储和身份验证,数据泄漏等等。

medium

<?php
//检测内容是否为空
if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from   检查用户来源
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
       
        // Get input   通过get传参接收password数据
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        // Do the passwords match?   比对两次密码输入是否一致
        if( $pass_new == $pass_conf ) {
        // They do!    使用mysqli_real_escape_string()函数将新的密码进行过滤
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

        //使用md5()函数进行加密
        $pass_new = md5( $pass_new );

        // Update the database   将新密码插入到数据库并且更新数据
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the user     提示密码修改成功
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching   提示两次密码属于不一致
        echo "<pre>Passwords did not match.</pre>";
    }

     //关闭数据库连接
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

分析思路:

该代码段有以下几个主要问题:

  1. 使用 HTTP_REFERER 进行来源验证:该代码段使用 $_SERVER['HTTP_REFERER'] 作为依据来确定用户的请求来源,但这是不安全的。攻击者可以通过伪造 HTTP_REFERER 的方式来绕过这个验证,或者某些浏览器不会发送 HTTP_REFERER,这样的话整个验证程序就毫无用处。

  2. 仍然使用 MD5 加密:该代码仅仅使用了 MD5 来加密用户输入的密码,这种做法已经过时,现今常见的密码加密方式是使用 PBKDF2、bcrypt 或 scrypt 这样的加密算法。MD5 已经不再是安全的,并且可以通过大量计算机的 GPU 加速破解,应该选择更加安全的加密算法。

  3. 错误处理方法不当:该代码段只是简单地输出一条信息,而没有提供充分的错误处理机制,这可能会泄漏敏感信息,并为攻击者提供足够的信息来发起攻击。

因此,要增强代码的安全性,应该采用以下建议:

  1. 使用其他方法进行来源验证:可以使用其他的方法来验证来自请求的可靠性。例如,可以使用 token 值或请求签名等方式来验证请求的来源,而不是单纯依赖于 $_SERVER['HTTP_REFERER']。

  2. 使用更安全的密码加密方法:应该使用更加安全的密码加密算法,例如 bcrypt、scrypt、或 Argon2,并且应该确保实现使用强大的技术来保护用户的密码。

  3. 增加恰当的错误处理机制:应该使用合适的错误处理机制,例如,将敏感信息隐藏起来以掩盖内部信息,并提供错误处理机制,以便处理系统运行时发生的任何异常情况。

综上所述,在安全程序的设计和开发过程中,采用这些措施有助于减少不同类型的安全漏洞,包括 SQL 注入、XSS 攻击、不恰当的密码存储和身份验证、数据泄漏等等。

High

<?php

$change = false;
$request_type = "html";
$return_message = "Request Failed";

if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {
    $data = json_decode(file_get_contents('php://input'), true);
    $request_type = "json";
    if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&
        array_key_exists("password_new", $data) &&
        array_key_exists("password_conf", $data) &&
        array_key_exists("Change", $data)) {
        $token = $_SERVER['HTTP_USER_TOKEN'];
        $pass_new = $data["password_new"];
        $pass_conf = $data["password_conf"];
        $change = true;
    }
} else {
    if (array_key_exists("user_token", $_REQUEST) &&
        array_key_exists("password_new", $_REQUEST) &&
        array_key_exists("password_conf", $_REQUEST) &&
        array_key_exists("Change", $_REQUEST)) {
        $token = $_REQUEST["user_token"];
        $pass_new = $_REQUEST["password_new"];
        $pass_conf = $_REQUEST["password_conf"];
        $change = true;
    }
}

if ($change) {
    // Check Anti-CSRF token
    checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert );

        // Feedback for the user
        $return_message = "Password Changed.";
    }
    else {
        // Issue with passwords matching
        $return_message = "Passwords did not match.";
    }

    mysqli_close($GLOBALS["___mysqli_ston"]);

    if ($request_type == "json") {
        generateSessionToken();
        header ("Content-Type: application/json");
        print json_encode (array("Message" =>$return_message));
        exit;
    } else {
        echo "<pre>" . $return_message . "</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>

分析思路:

该代码段的漏洞比较难以言明,因为它已经引入了较多的安全考虑,并且使用了合适的安全技术。但是,以下是该代码段仍然存在的漏洞:

  1. 该代码本身没有引入 XSS 和 CSRF:尚未被引入 XSS 和 CSRF 攻击的实现,对于不同攻击有特定防范措施的代码段已经有特别用意,并且常常需要多轮审计才能发现和消除它们。

  2. 对密码强度和复杂性的验证可能不够 rigorous:需要确认密码是否满足密码复杂度要求,例如至少8位以上包含字母、数字和标点符号,如果密码是可重置的,则应该采用替代方案,例如遵循企业密码策略或使用密码保护方案。

  3. 数据库安全性方面需要更多措施 :该代码段没有考虑数据库安全性,例如:考虑数据库主人权限的管理,规范化数据结构,留意空间问题和扫描数据库以异常行为为主的恶意行为等。

因此,即使该代码段并没有明显的漏洞,开发人员应该对代码进行更多的安全审计,并尝试考虑各种攻击风险和场景,以确保程序的安全性和完整性。

File Inclusion文件包含

low

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

?>

分析思路:

该代码段有一个非常明显和严重的漏洞,存在任意文件包含漏洞(LFI)。

该代码仅仅使用 $_GET['page'] 作为文件名,并且没有对文件名进行任何验证,攻击者可以构造特殊的文件名,直接访问恶意的本地文件,由于攻击者可以选择任何需要包含的文件名,他们可以轻松地检索到任何受影响的文件并访问其内容。通常,攻击者利用此漏洞来窃取敏感信息,包括配置文件,SQL 数据库凭据,私钥等等。

为了防止此漏洞,应该对文件名进行正确的验证和过滤,例如使用白名单机制,验证是否存在该文件等机制,而不是简单地直接包含用户提供的文件名。同时建议采取安全的文件系统架构和适当的操作系统级和网络安全措施来增强文件系统的安全性,减少任意文件包含此外的其他安全威胁。

medium

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\\" ), "", $file );

?> 

分析思路:

该代码段尝试对用户提供的文件名进行操作,是一个常用的安全措施,但这个代码可能仍然存在一个相对较少出现的漏洞(空字节攻击)。

使用 str_replace 去掉 "http://"和 "https://" 不足以有效地防止任意文件包含攻击,以下两种方式可以利用这个漏洞:

  1. 利用空字节:某些攻击者可以使用空字节,例如 '%00' 或 '\0' 空字节来避免字符串过滤和绕过验证。

  2. 利用弱保护路径:如果文件名只是简单地删除 "../" 和 "..\",攻击者仍然可以通过利用这个漏洞来访问到一些私密的或未经验证的文件,例如:'../../config.php';

因此,为了防止任意文件引用,需要更加严格的过滤策略,不能依赖简单的字符串替换,例如使用白名单机制、检查文件路径有效行和限制文件访问总体路径深度等来限制目标文件被访问的文件系统中的位置,同时如果应用程序需要读取和写入文件,还需要进行更加全面和严格的权限配置和限制。

high

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
    // This isn't the page we want!
    echo "ERROR: File not found!";
    exit;
}

?>

分析思路:

该代码进行了一些简单的输入验证,但这种方法也可能存在问题,攻击者可以利用一些方法绕过检查,例如:

  1. 文件名不以 'file' 开头,然后使用参数 '?page=somefile' 访问恶意的文件;

  2. 文件名为 'include.php',但攻击者可以在该文件中注入恶意代码并让其执行。

  3. 静态字符串匹配不会防止攻击者使用其他的绕过方法,例如大小写混合、URL 编码、Unicode 编码等等。

因此,要有效地防御任意文件包含攻击,需要使用可靠的函数和规则,例如 realpath() 和完全限定路径名,有效的文件名白名单等,从而限制攻击者可以访问的目标文件。

File Upload

low

<?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Where are we going to be writing to?
    $target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

    // Can we move the file to the upload folder?
    if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
        // No
        echo '<pre>Your image was not uploaded.</pre>';
    }
    else {
        // Yes!
        echo "<pre>{$target_path} succesfully uploaded!</pre>";
    }
}

?> 

分析思路:

该代码通过 PHP 的文件上传机制将文件上传至服务端,然而这种代码实现方法也可能存在安全问题。

代码中的 target_path 变量构成方式使用targetp​ath变量构成方式使用_FILES['uploaded']['name'],这可以导致任意文件上传攻击。攻击者可以构造一个文件名上传到服务器,因此如果上传目录没有适当的权限配置,恶意文件可能被执行。

为避免任意文件上传攻击,应对用户提交的文件进行严格的验证和限制。一种保护措施是使用白名单验证来接受在服务器上安全上传的文件。此外,还应该对上传的文件进行其他类型验证,例如文件类型、文件大小和文件名。为了限制错误使用,服务器端应在上传文件夹上设置适当的权限和访问控制。

medium

<?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Where are we going to be writing to?
    $target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];

    // Is it an image?
    if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
        ( $uploaded_size < 100000 ) ) {

        // Can we move the file to the upload folder?
        if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
            // No
            echo '<pre>Your image was not uploaded.</pre>';
        }
        else {
            // Yes!
            echo "<pre>{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        // Invalid file
        echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

?> 

分析思路:

该代码对文件上传进行了类型、大小验证来限制非法上传,但这种实现方式可能存在安全问题,例如:

  1. 缺少文件扩展名验证:该代码只验证了上传的文件是否为 JPEG 或 PNG 图像,但攻击者可以轻松地通过将文件内容与 .jpg/.png 扩展名相匹配来绕过此检查,因此还需要对文件的扩展名进行验证。

  2. 验证文件类型的方式可能不可靠:HTTP 请求类型和内容可能被篡改和伪造,因此只依赖浏览器提供的文件类型信息进行上传文件的验证可能不是最可靠的做法。因此,建议使用额外的文件类型验证机制,例如 Linux 的 file 命令来进行验证。

  3. 限制文件大小验证机制应该根据具体的应用场景来确定大小范围,以避免阻塞上传操作。同时,还要注意如果攻击者提交较小的文件数量,可以在使用总量不超过约束值的情况下上传大量恶意文件的情况。

综上所述,建议结合多种验证机制来确保上传文件的安全,包括扩展名、类型、大小、内容校验等等。同时,还要按需限制上传目录的访问权限和用户权限等等,并实时监控上传的文件,以保证服务器的安全和稳定。

high

<?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Where are we going to be writing to?
    $target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
    $uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];

    // Is it an image?
    if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
        ( $uploaded_size < 100000 ) &&
        getimagesize( $uploaded_tmp ) ) {

        // Can we move the file to the upload folder?
        if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
            // No
            echo '<pre>Your image was not uploaded.</pre>';
        }
        else {
            // Yes!
            echo "<pre>{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        // Invalid file
        echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

?>

分析思路:

该代码对文件上传进行了类型、大小以及扩展名验证,并且使用了 getimagesize() 函数来验证是否为 JPEG 或 PNG 图像,较为严谨。但是,仍然存在以下漏洞:

  1. 验证 getimagesize() 并不十分准确:该函数有时会根据文件的扩展名而不是文件的内容检测文件类型并认为它是一种有效的 JPEG 或 PNG 图像。因此,建议使用 fileinfo 或其他更可靠的库函数来进行类型验证。

  2. 偽造文件可绕过验证机制:攻击者可以在文件头部伪造文件内容,从而绕过诸如 getimagesize() 的函数验证机制,所以应该对上传的文件内容进行多重验证和限制,如文件类型、文件大小和扩展名,以防止攻击者上传恶意文件。

综上所述,建议使用多种验证机制来确保上传文件的安全,并且对上传目录的访问权限和用户权限等等进行限制和规范。

SQL Injection SQL注入

low

<?php

if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

            // Get results
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Get values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }

            mysqli_close($GLOBALS["___mysqli_ston"]);
            break;
        case SQLITE:
            global $sqlite_db_connection;

            #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']);
            #$sqlite_db_connection->enableExceptions(true);

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    } 
}

?> 

分析思路:

该代码中存在 SQL 注入漏洞,即攻击者可以通过修改参数 ID 的值,从而在网页上执行恶意的 SQL 语句,比如利用 1=1,将 WHERE 后的语句注释掉,从而显示整个用户列表。

解决方式:

  1. 参数化查询:将查询语句中的参数化,这可以使用绑定变量来完成。 也可以使用预处理语句和占位符来预编译 SQL 查询。 如果使用的是 PDO,则可以使用 PDOStatement::bindParam() 或 PDOStatement::bindValue() 函数,如果是 mysqli 则可以使用 mysqli_stmt_bind_param() 函数,通过绑定传递给 SQL 查询语句的参数,而不是直接拼接到 SQL 查询语句中。
  2. 过滤输入参数:可以使用 addslashes()、mysqli_real_escape_string()、SQLite3::escapeString() 等函数来过滤输入参数来消除潜在的 SQL 注入问题。
  3. 限制查询返回的条数:由于在引发 SQL 注入的同时,客户端可以请求获取无数的数据,导致资源浪费或拒绝服务攻击,因此应设置查询返回的数据量的上限。

综上所述,对于SQL注入问题,应该使用符号转义和参数化查询来避免 SQL 注入攻击处于暴露状态。同时,还需限制用户输入数据的类型和大小,以确保安全和稳定性。

medium

<?php

if( isset( $_POST[ 'Submit' ] ) ) {
    // Get input
    $id = $_POST[ 'id' ];

    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
            $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );

            // Get results
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Display values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }
            break;
        case SQLITE:
            global $sqlite_db_connection;

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    }
}

// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query  = "SELECT COUNT(*) FROM users;";
$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
$number_of_rows = mysqli_fetch_row( $result )[0];

mysqli_close($GLOBALS["___mysqli_ston"]);
?> 

分析思路:

该代码仍然存在 SQL 注入漏洞,因为在构建 SQL 语句时仅仅使用了 mysqli_real_escape_string 函数对输入参数进行了转义,而没有使用参数化查询或预处理语句等安全措施。攻击者可以在参数 id 中插入恶意的 SQL 语句,从而执行操纵数据库操作。

为了解决这个漏洞,可以使用以下措施:

  1. 参数化查询:将查询语句中的参数化,这可以使用绑定变量来完成。 也可以使用预处理语句和占位符来预编译 SQL 查询。 如果使用的是 PDO,则可以使用 PDOStatement::bindParam() 或 PDOStatement::bindValue() 函数,如果是 mysqli 则可以使用 mysqli_stmt_bind_param() 函数,通过绑定传递给 SQL 查询语句的参数,而不是直接拼接到 SQL 查询语句中。

  2. 过滤输入参数:可以使用 mysqli_real_escape_string()、SQLite3::escapeString() 等函数来过滤输入参数来消除潜在的 SQL 注入。

综上所述,让参数化查询和参数化预备语句称为管理 SQL 注入攻击的最佳方式,同时需要对数据库进行严格的访问控制和数据保护来确保数据库安全。

high

<?php

if( isset( $_SESSION [ 'id' ] ) ) {
    // Get input
    $id = $_SESSION[ 'id' ];

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
            $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );

            // Get results
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Get values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }

            ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);        
            break;
        case SQLITE:
            global $sqlite_db_connection;

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    }
}

?>

分析思路:

该代码中存在潜在的会话固定攻击漏洞,即攻击者可以使用会话 ID(SID)即 $_SESSION['id'] 的值来冒充已经通过身份验证的用户并访问敏感数据。

解决方法:

  1. 随机化会话 ID:为避免会话固定攻击,应该在每个请求中随机生成新的会话 ID。可以使用 session_regenerate_id() 函数来完成。

  2. 检查其他的会话攻击:包括会话劫持、会话注入和会话猜测等。

  3. 资源释放: 由于该代码使用了数据库连接资源,必须在程序执行结束时显式地关闭或销毁连接以释放资源。

综上所述,为保护系统的安全,应该对所有会话进行一系列的安全措施,如使用 SSL/TLS 加密安全连接、随机化会话 ID、避免在 URL 中公开会话 ID 和使用合适的会话配置文件。另外,还应该定期更新会话密钥,以增强会话的安全性。

SQL Injection (Blind) SQL注入(盲注)

low

<?php

if( isset( $_GET[ 'Submit' ] ) ) {
    // Get input
    $id = $_GET[ 'id' ];
    $exists = false;

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

            $exists = false;
            if ($result !== false) {
                try {
                    $exists = (mysqli_num_rows( $result ) > 0);
                } catch(Exception $e) {
                    $exists = false;
                }
            }
            ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
            break;
        case SQLITE:
            global $sqlite_db_connection;

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            try {
                $results = $sqlite_db_connection->query($query);
                $row = $results->fetchArray();
                $exists = $row !== false;
            } catch(Exception $e) {
                $exists = false;
            }

            break;
    }

    if ($exists) {
        // Feedback for end user
        echo '<pre>User ID exists in the database.</pre>';
    } else {
        // User wasn't found, so the page wasn't!
        header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

        // Feedback for end user
        echo '<pre>User ID is MISSING from the database.</pre>';
    }

}

?> 

分析思路:

该代码中存在 SQL 注入漏洞,攻击者可以在参数 id 中插入恶意的 SQL 代码来操作数据库。

具体来说,代码中的查询语句使用了字符串拼接的方式将输入参数 id 直接拼接到 SQL 查询语句中。 导致攻击者可以通过使用手动或自动测试工具构造特殊的输入来注入任意的 SQL 代码,绕过身份验证和授权并获取敏感数据。

解决方法:

  1. 把字符串拼接的查询语句重构成参数化查询的形式, 使用参数化查询和预处理语句来代替字符串拼接, 以及仅准许执行特定的操作。

  2. 过滤输入参数,可以使用 mysqli_real_escape_string()、SQLite3::escapeString() 等函数来过滤输入参数来消除潜在的 SQL 注入。

综上所述,为避免 SQL 注入问题,我们可以使用符号转义、参数化查询,同时限制查询返回数据的数量和过滤用户的输入参数。此外,要定期更新和优化其安全措施以确保数据和用户信息的保护。

mediumn

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $id = $_POST[ 'id' ];
    $exists = false;

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

            $exists = false;
            if ($result !== false) {
                try {
                    $exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
                } catch(Exception $e) {
                    $exists = false;
                }
            }
            
            break;
        case SQLITE:
            global $sqlite_db_connection;
            
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
            try {
                $results = $sqlite_db_connection->query($query);
                $row = $results->fetchArray();
                $exists = $row !== false;
            } catch(Exception $e) {
                $exists = false;
            }
            break;
    }

    if ($exists) {
        // Feedback for end user
        echo '<pre>User ID exists in the database.</pre>';
    } else {
        // Feedback for end user
        echo '<pre>User ID is MISSING from the database.</pre>';
    }
}

?> 

分析思路:

该代码在一定程度上修复了 SQL 注入漏洞,但还存在潜在的漏洞,如未对传入的参数进行类型验证和处理,可能导致其他类型的攻击漏洞,如 XSS 和 CSRF。

另外,对于 MySQL 的查询语句,虽然 $id 通过 mysqli_real_escape_string() 函数进行了转义,但是缺少单引号,在某些情况下会不完全转义,导致 SQL 注入漏洞。

解决方法:

  1. 通过对传递给数据库查询的参数进行类型验证和处理来进一步减少注入风险。

  2. 对于 MySQL 数据库,应该在查询语句中使用单引号将转义后的字符串包含起来,以确保字符值字符串的安全。

  3. 最好的方法是使用参数化查询和预处理语句来代替字符串拼接,这是避免 SQL 注入漏洞的最佳方法。

  4. 针对其他类型的攻击漏洞,可以选择采取其他措施,在数据存储之前进行输入过滤和验证,如验证特定类型的输入数据、限制数据输入和确保数据合法性等。

综上所述,对于无法避免的输入验证和数据处理,应该使用最佳实践来构造代码,以尽可能地减少安全漏洞,最终避免可能的攻击。

high

<?php

if( isset( $_COOKIE[ 'id' ] ) ) {
    // Get input
    $id = $_COOKIE[ 'id' ];
    $exists = false;

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

            $exists = false;
            if ($result !== false) {
                // Get results
                try {
                    $exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
                } catch(Exception $e) {
                    $exists = false;
                }
            }

            ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
            break;
        case SQLITE:
            global $sqlite_db_connection;

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
            try {
                $results = $sqlite_db_connection->query($query);
                $row = $results->fetchArray();
                $exists = $row !== false;
            } catch(Exception $e) {
                $exists = false;
            }

            break;
    }

    if ($exists) {
        // Feedback for end user
        echo '<pre>User ID exists in the database.</pre>';
    }
    else {
        // Might sleep a random amount
        if( rand( 0, 5 ) == 3 ) {
            sleep( rand( 2, 4 ) );
        }

        // User wasn't found, so the page wasn't!
        header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

        // Feedback for end user
        echo '<pre>User ID is MISSING from the database.</pre>';
    }
}

?> 

分析思路:

该代码中存在使用 Cookie 值作为查询参数的安全风险,黑客可以通过篡改 Cookie 值来轻松绕过身份验证和授权,并获取、篡改和删除数据。

除了 Cookie 注入攻击外,该代码还存在 SQL 注入漏洞,攻击者可以在 id 中插入恶意 SQL 代码来破坏数据库的完整性,获取敏感数据。

解决方法:

  1. 完善身份验证和授权机制,防止黑客利用 Cookie 注入等安全攻击。

  2. 使用参数化查询和预处理语句来代替字符串拼接,以及对传入的输入参数进行数据验证和处理,来尽量减少 SQL 注入风险。

综上所述,为了防止安全漏洞,必须采用多层次的防御措施,包括按需验证、检测和验证数据输入、实施安全鉴别、加密和防护网络等。同时,也要定期检查和更新安全措施以保证数据和用户信息的安全性。

DOM Based Cross Site Scripting (XSS)DOM型XSS

low

<?php

# No protections, anything goes

?> 

medium

<?php

// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
    $default = $_GET['default'];
    
    # Do not allow script tags
    if (stripos ($default, "<script") !== false) {
        header ("location: ?default=English");
        exit;
    }
}

?> 

分析思路:

该代码中存在一个输入验证漏洞。针对 URL 参数 "default",代码首先验证是否存在 "default" 键,然后检查 "default" 的值中是否包含字符串 "<script"。如果包含,它会将用户重定向回默认值,并退出程序。然而,这不是一个有效的验证方法,因为使用其他标记,如 "<span>" 仍然可以在 HTML 中引入恶意 JavaScript 代码并危及您的网站与用户。

解决方法:

应该使用更有效的方法来防止恶意代码的注入,例如:

  1. 在输入中搜索所有实体标记 "&<> 以及空格,将它们替换为其 HTML 等效项。

  2. 使用 HTML Purifier 或类似的第三方库来清除输入中的全部 HTML 标记。

  3. 限制用户输入,例如仅接受特定的字符和空间来减少输入验证的风险。

综上所述,应该使用更安全和可靠的验证策略来验证用户输入,这样才能有效地防止恶意代码的注入,以确保网站和用户信息的安全性。

high

<?php

// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {

    # White list the allowable languages
    switch ($_GET['default']) {
        case "French":
        case "English":
        case "German":
        case "Spanish":
            # ok
            break;
        default:
            header ("location: ?default=English");
            exit;
    }
}

?>

分析思路:

该代码中,针对 URL 参数 "default",首先检查是否存在 "default" 键,然后通过 switch 语句白名单验证是否存在指定的语言。如果存在于白名单之外的语言,代码会将用户重定向到默认语言 "English"。

虽然这种方法比上一个例子中的简单字符串检查更保险,但是仍然存在安全漏洞。因为“白名单”信息通常以硬编码方式存储,这意味着如果出现新的合法语言,您需要更新应用程序并重新启动服务器。

解决方法:

  1. 在指定语言之前,需要对输入进行有效性验证,以确保它不包含恶意代码或不会导致漏洞。

  2. 使用更灵活的动态数据配置,“白名单”可以从配置文件或数据库中动态加载,这样程序可以进行实时更新,而无需重启服务器。

  3. 使用更强大的正则表达式模式来验证输入,以确保其完全符合指定格式和规则。

综上所述,使用动态数据配置的白名单是更有效和安全的方法,可大大提高应用程序的安全性,避免出现未知语言时或新增合法语言时的安全风险。

Reflected Cross Site Scripting (XSS)反射型XSS

low

<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Feedback for end user
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}

?> 

分析思路:

该代码中存在跨站脚本攻击(XSS)漏洞,因为它未对输入的 “name” 进行任何验证或过滤,这使得攻击者可以使用脚本或其他有害代码来注入威胁网站和用户信息的攻击载荷。通过在 URL 中添加参数 "name",攻击者可以向终端用户呈现有害的站点或欺诈性内容。

即使在这种情况下,添加了 X-XSS-Protection 响应头,但实际上并不能完全防御XSS攻击。因此,建议不仅在服务端进行输入验证和过滤,并在客户端进行输出HTML转义处理,以确保用户看到的内容是安全和可信的。

解决方法:

对于 PHP 应用程序中的 XSS 漏洞,建议采用以下方法:

  1. 输入验证和过滤 - 对于所有输入数据(包括 URL 中的参数、cookies、表单输入等),都需要进行彻底的验证和过滤,以确保输入数据的完整性、正确性和安全性。可以考虑使用 PHP 的内置函数 filter_var() 和 filter_input() 来进行输入验证和过滤。

  2. 常规输出HTML转义 - 在显示用户数据之前,应该将特殊字符,如 "<", ">", "&" 等,转义为 HTML 实体,即将它们替换为相应的 HTML 实体名称或数字实体,以确保输入数据不会向用户呈现有害的 HTML 或 JavaScript 代码。

  3. 使用 Content Security Policy(CSP) - 可以通过 Content Security Policy(CSP)指定允许加载哪些 JavaScript 文件和资源,以进一步增强应用程序的安全性。

综上所述,通过对输入进行有效性验证和过滤,并对用户数据进行常规的 HTML 转义,可以实现更安全和可靠的 PHP 应用程序,并有效地避免 XSS 和其他安全漏洞的风险。

medium

<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = str_replace( '<script>', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}

?> 

分析思路:

该代码仍存在跨站脚本攻击 (XSS) 漏洞。这是因为只是简单地将字符串 <script> 替换为空字符串,仍然无法将潜在的攻击载荷完全过滤掉。攻击者仍然可以在 < 或其他特殊字符后面添加有效的 HTML 或 JavaScript 代码来执行攻击并威胁应用程序和用户。

正确的解决方法是使用强大而灵活的 HTML 过滤器,可以使用现有的第三方组件,如 HTML Purifier,或使用 PHP 内置的过滤器,如 strip_tags() 和 htmlspecialchars() 函数。

同时,也可以通过 PHP 内置的函数 filter_var() 来进行输入验证和过滤,以确保输入数据的完整性、正确性和安全性。

解决方法:

  1. 强大而灵活的 HTML 过滤器

HTML 过滤器是一种强大的工具,可以过滤掉所有的 HTML 输入并替换为安全的文本或标记。它们可以帮助过滤掉所有标签、属性和事件监听器,同时保留文本中的链接、加粗等样式。HTML Purifier 是一个广泛使用的第三方库,可以为您的应用程序提供广泛的保护。

  1. 使用内置过滤器

PHP 内置了一些过滤函数,如 strip_tags() 和 htmlspecialchars(),可以使输入字符串中的 HTML 标记和脚本无效。strip_tags() 可以过滤掉 <script> 标记之外的所有标记,而 htmlspecialchars() 可以将标记转换为等价的实体,以此使包含的代码无效。

  1. 输入验证和过滤

使用 PHP 的内置函数 filter_var() 和 filter_input() 对输入数据进行有效性验证和过滤,以确保输入数据的完整性、正确性和安全性。

综上所述,通过使用强大的 HTML 过滤器、内置的过滤函数或者在输入前进行有效的输入验证和过滤,可以实现更安全和可靠的 PHP 应用程序,并有效地避免 XSS 和其他安全漏洞的风险。

high

<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}

?> 

分析思路:

该代码仍然存在跨站脚本攻击 (XSS) 漏洞。这是因为正则表达式并不是完全可靠的过滤器,针对复杂的攻击载荷仍然可能失败。此外,也可能使用类似的技术来规避正则表达式防御,进而进行攻击。

因此,使用正则表达式过滤输入是不建议的,应该使用更安全和可靠的 HTML 过滤器或 PHP 内置的过滤函数进行输入验证和过滤。

解决方法:

  1. 使用 HTML 过滤器

可以使用现有的第三方HTML过滤器,例如 HTML Purifier,或使用PHP内置的过滤器,例如 strip_tags() 和 htmlspecialchars() 函数。

  1. 使用 PHP 内置的过滤函数

PHP内置了一些过滤器函数,例如 strip_tags() 和 htmlspecialchars(),可以过滤掉输入字符串中所有的HTML和JavaScript。

  1. 输入验证和过滤

使用PHP内置的函数filter_var()和 filter_input(),对输入数据进行有效地验证和过滤,确保输入数据的完整性、正确性和安全性。

综上所述,使用可靠的HTML过滤器或PHP内置的过滤器函数,或在接受输入之前对其进行有效检查和过滤,可以实现更安全和可靠的 PHP 应用程序,并有效地避免 XSS 和其他安全漏洞的风险。

Stored Cross Site Scripting (XSS)存储性XSS

low

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitize name input
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>

分析思路:

该代码存在 SQL 注入漏洞。这是因为它未正确处理输入数据中的特殊字符,在将输入传递给 SQL 查询之前未对其进行任何过滤或转义。攻击者可以使用特殊字符和语法规则来构造恶意注入代码,从而对数据库进行非法操作。

尽管该代码使用了 mysqli_real_escape_string() 函数对输入进行转义,但在使用之前,必须确保已经通过 mysqli_connect() 函数与数据库建立了连接并返回了连接对象,否则将无法转义输入。

另外,该代码还存在一些问题,例如使用不能修改或删除数据的 SELECT 查询,没有进行输出查询结果的验证,以及没有定义或限制有效输入格式等等问题。

正确的解决方法是使用参数化查询语句或预处理语句来防止 SQL 注入攻击,并对输入数据进行格式验证和过滤,以保证输入数据的完整性、正确性和安全性。

解决方法:

  1. 参数化查询语句

参数化查询使用占位符代替输入数据,并将输入数据作为参数传递给 SQL 查询。这样可确保参数传递给 SQL 查询时被自动转义或过滤,使攻击者无法插入恶意代码。可以使用 mysqli 函数库或 PDO 函数库来实现参数化查询。

  1. 预处理语句

预处理语句类似于参数化查询,但是在执行预处理语句之前,参数值和 SQL 语句是分开的。这样 SQL 查询可以重复使用,并且防止了 SQL 注入攻击,因为输入数据会被预先处理并只与占位符相关,而不会被视为 SQL 代码的一部分。

  1. 输入验证和过滤

在接受输入数据之前,可以使用PHP内置的函数对其进行基本的验证和过滤,例如 trim(),strip_tags() 和 htmlspecialchars() 等。同时,也可以使用正则表达式等方法,对输入数据进行更严格的格式检查和过滤。

综上所述,使用参数化查询语句或预处理语句可以防止 SQL 注入攻击,而对输入数据进行验证和过滤可以保障应用程序和数据的完整性和安全性。

medium

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = str_replace( '<script>', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?> 

分析思路:

该代码存在多个安全漏洞。

1. SQL 注入漏洞

该代码未正确处理输入数据中的特殊字符,在将输入传递给 SQL 查询之前未对其进行任何过滤或转义。攻击者可以使用特殊字符和语法规则来构造恶意注入代码,从而对数据库进行非法操作。虽然该代码在一定程度上对输入进行了转义,但仍然无法完全避免 SQL 注入攻击。

2. 跨站脚本攻击 (XSS) 漏洞

该代码未对用户提供的输入进行充分验证和过滤,因此可能存在 XSS 攻击的风险。例如,通过设置包含脚本的 “txtName” 输入,可以在页面上动态生成 HTML 或 JavaScript 代码,从而执行恶意攻击。

3. 部分过滤器的使用不当

使用 addslashes() 和 htmlspecialchars() 并不足以保证应用程序的安全,尤其是在需要将数据传递给各种上下文时。例如,在将数据传递给数据库时,应该使用 mysqli_real_escape_string() 或其他类型的数据库转义函数来防止 SQL 注入攻击。

在处理 HTML 和 JavaScript 代码时,应该使用更可靠的过滤器来保护应用程序免遭 XSS 攻击。

解决方法:

1. 参数化查询语句或预处理语句

使用参数化查询语句或预处理语句来对输入进行转义或过滤,以防止 SQL 注入攻击。这些技术可以确保在执行查询时输入将被自动转义或过滤,而不会被视为 SQL 代码的一部分。

2. 完整的输入验证和过滤

使用服务器端验证和过滤机制来确保输入数据的完整性、正确性和安全性,并规定数据的格式和长度等限制,以保护应用程序免受恶意攻击。可以使用 PHP 内置的函数,例如 filter_input() 和 filter_var(),或第三方 HTML 过滤器,例如 HTML Purifier。

3. 安全的 HTML 和 JavaScript 过滤器

使用安全的 HTML 和 JavaScript 过滤器,例如 DOMPurify,可以有效地防止 XSS 攻击。这些过滤器使用白名单机制,仅允许安全的 HTML 和 JavaScript 标签和属性,并过滤掉恶意代码和标签。

综上所述,对输入进行参数化查询或预处理语句,对输入数据进行完整和安全的验证和过滤,使用安全的 HTML 和 JavaScript 过滤器等措施可以有效地保护 PHP 应用程序免受 SQL 注入和 XSS 攻击的风险。

high

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>

分析思路:

该代码存在跨站脚本攻击 (XSS) 漏洞。

在处理“name”输入时,使用了一条正则表达式来删除包含关键字“script”的标签。但是,正则表达式容易受到攻击者通过编码、编程技术等方式的绕过。

例如,攻击者可以使用空格、换行符等方式间隔字母,以避免该正则表达式的匹配。他们也可以使用其他 HTML 标签或 JavaScript 代码来绕过过滤器。

这使得攻击者可以注入带有脚本的恶意代码,并在受害者的浏览器上运行它们。

解决方法:

使用安全的 HTML 过滤器和编码方法,同时防止 XSS 攻击。

在处理 HTML 数据时,可以使用 PHP 内置的函数 htmlspecialchars() 来对 HTML 实体进行编码,例如<>和""。这会将所有的 HTML 标签和其它特殊字符转义为其对应的 HTML 实体,而不是被浏览器解释。而在分析和处理 HTML 数据时,可以使用 HTMLPurifier 等安全的 HTML 过滤器。

在处理 JavaScript 数据时,可以使用 JSON 编码方法,口令用户数据作为 JSON 格式的字符串来存储和传输。在从服务器上取数据时,应进行反序列化 JSON 字符串,前端会特地约束好接口,不能直接传输带有特殊字符的字符串,防止转换出错。

综上所述,使用安全的 HTML 过滤器和编码方法,以及限制用户输入数据的格式和长度,可以有效地防止跨站脚本攻击。同时,也应该使用参数化查询语句或预处理语句来避免 SQL 注入攻击。

转发请注明出错谢谢

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值