1

使用谷歌网站管理员工具检查我的网站,我发现了一件奇怪的事情:

有人试图使用这个链接到我的网站(出于明显的安全原因,我更改了我的网站的真实名称):

....://mysite.com/tarifs.php?annee=aaatoseihmt&mois=10&cours=1828

我试了一下,发现这是一个带有结果的 sql 注入:

警告:mktime() 期望参数 6 很长,字符串在第 72 行的 /home/..../public_html/..../tarifs.php 中给出

我的代码行 72 是:

mktime (0, 0, 0, $mois, "01", $annee)

其中一部分:

<?php
include ("include.php");

if (!$link = mysql_connect($host, $user, $pass)) {
    echo "Could not connect to mysql";
    exit;
}

if (!mysql_select_db($bdd, $link)) {
    echo "Could not select database";
    exit;
}

mysql_query("SET NAMES 'utf8'");

$annee = "";
$mois = "";
$stage = "";

if (isset($_GET['annee'])) {$annee=$_GET['annee'];}
if (isset($_GET['mois'])) {$mois=$_GET['mois'];}
if (isset($_GET['stage'])) {$stage=$_GET['stage'];}

if($annee == "")
{

    $annee = date("Y");
}

if($mois == "")
{

    $mois = date("m");
}


$date_du_jour = date("d")."-".date("m")."-".date("Y");

if($mois == "12")
{
    $mois_precedent = "11";
    $mois_suivant = "01";
    $annee_mois_precedent = $annee;
    $annee_mois_suivant = $annee + 1;
}
elseif($mois == "01")
{
    $mois_precedent = "12";
    $mois_suivant = "02";
    $annee_mois_precedent = $annee - 1;
    $annee_mois_suivant = $annee;
}
else
{
    $mois_precedent = sprintf("%02s", $mois-1);
    $mois_suivant = sprintf("%02s", $mois+1);
    $annee_mois_precedent = $annee;
    $annee_mois_suivant = $annee;
}


$jour_en_cours = date("d");

$mois_francais = array("Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre");

$dt_deb_genere = $annee."-".$mois."-01";
$dt_fin_genere = $annee_mois_suivant."-".$mois_suivant."-01";

$dt_date = mktime (0, 0, 0, $mois, "01", $annee);
$jour_de_la_semaine = date("w", $dt_date);
?>

我能做些什么来保护我的网站免受这种情况的影响?

我试图用“类似的问题”来理解它,但我认为我是 php 和 mysql 的新手才能理解。所以任何帮助都非常棒!

谢谢你能帮忙!我现在在我的网站上努力工作了几个月,不想失去我的生意。

.blc。


然后 !!!在 Lucanos 提供的代码之后,我做了一些小改动(谢谢 !!!!):-):

<?php
include ("include.php");

if (!$link = mysql_connect($host, $user, $pass)) {
    echo "Could not connect to mysql";
exit;
}

if (!mysql_select_db($bdd, $link)) {
echo "Could not select database";
exit;
}

mysql_query("SET NAMES 'utf8'");

$annee = '';
$mois = '';
$stage = '';

if( isset( $_GET['annee'] ) )
{
$annee = preg_replace( '/\D/' , '' , $_GET['annee'] );
if( !$annee || !( $annee<=2015 && $annee>=2013 ) )
// Allows you to set an expected range for this value
// code here expects a number between 2013 and 2015 inclusive
$annee = '';
}

if( isset( $_GET['mois'] ) )
{
$mois = preg_replace( '/\D/' , '' , $_GET['mois'] );
if( !$mois || !( $mois<=12 && $mois>=1 ) )
// I assume this is the Month, with a range of 1 to 12
$mois = '';
}

if (isset($_GET['stage'])) {$stage=$_GET['stage'];}

if($annee == '')
{
// Récupération de l'année en cours
$annee = date('Y');
}

if($mois == '')
{
// Récupération du mois en cours
$mois = date('m');
}

// Récupération de la date du jour

$date_du_jour = date( 'd-m-Y' );

if($mois == '12')
{
$mois_precedent = '11';
$mois_suivant = '01';
$annee_mois_precedent = $annee;
$annee_mois_suivant = $annee + 1;
}
elseif($mois == '01')
{
$mois_precedent = '12';
$mois_suivant = '02';
$annee_mois_precedent = $annee - 1;
$annee_mois_suivant = $annee;
}
else
{
$mois_precedent = sprintf('%02s', $mois-1);
$mois_suivant = sprintf('%02s', $mois+1);
$annee_mois_precedent = $annee;
$annee_mois_suivant = $annee;
}

// Récupération du jours en cours
$jour_en_cours = date('d');

$mois_francais = array(
'Janvier' , 'Février' , 'Mars' ,
'Avril' , 'Mai' , 'Juin' ,
'Juillet' , 'Août' , 'Septembre' ,
'Octobre' , 'Novembre' , 'Décembre'
);

$dt_deb_genere = "{$annee}-{$mois}-01";
$dt_fin_genere = "{$annee_mois_suivant}-{$mois_suivant}-01";

$dt_date = mktime( 0 , 0 , 0 , $mois*1 , 1 , $annee*1 );
$jour_de_la_semaine = date( 'w' , $dt_date );
?>

尝试(直到现在还没有成功)添加一个条件测试数据库中是否存在“阶段”以避免像 stage=200 这样的调用,它不存在并在页面中显示一个空日历。但最后我在这里错过了一些东西(我没有在过去的代码中包含它)

$sql_stage = "select * from data where type_data = 'STAGE' and ind_valide = 1 and ind_etat = 1 order by sous_titre, id_type_1, ordre";
$result_stage = mysql_query($sql_stage, $link);
$existingstage = '';


while ($row_stage = mysql_fetch_array($result_stage))
{
$existingstage = $row_stage["id_data"];

if( isset( $_GET['stage'] ) )
{
    $stage = preg_replace( '/\D/' , '' , $_GET['stage'] );

    if( !$stage || !( $stage= $existingstage ) )

    $stage = '';
}
}
4

5 回答 5

1

永远不要相信用户输入

每当您使用表单中的值或从 URL 中提取的值时,请确保在使用之前对其进行测试、清理和/或转义。任何地方。

因此,例如,使用您的代码,我将对其进行如下编辑:

<?php
include ("include.php");

// Might be worth putting this into the "include.php" file, or a function
// to do the same thing. Especially if you connect to the DB regularly.
if (!$link = mysql_connect($host, $user, $pass)) {
    echo "Could not connect to mysql";
    exit;
}

// Same as above...
if (!mysql_select_db($bdd, $link)) {
    echo "Could not select database";
    exit;
}

// And again...
mysql_query("SET NAMES 'utf8'");


$annee = '';
$mois = '';
$stage = '';

if( isset( $_GET['annee'] ) )
{
    $annee = preg_replace( '/\D/' , '' , $_GET['annee'] );
    if( !$annee || !( $annee<=2020 && $annee>=1970 ) )
        // Allows you to set an expected range for this value
        // My code here expects a number between 1970 and 2020 inclusive
        $annee = '';
}
if( isset( $_GET['mois'] ) )
{
    $mois = pre_replace( '/\D/' , '' , $_GET['mois'] );
    if( !$mois || !( $mois<=12 && $mois>=1 ) )
        // I assume this is the Month, with a range of 1 to 12
        $mois = '';
}
if( isset( $_GET['stage'] ) )
{
    $stage = pre_replace( '/\D/' , '' , $_GET['stage'] );
    if( !$stage || !( $stage<=100 && $stage>=0 ) )
        // Again, assuming 1-100
        $stage = '';
}

if( $annee=='' )
    $annee = date( 'Y' );

if( $mois=='' )
    $mois = date( 'n' );

$date_du_jour = date( 'd-m-Y' );

if( $mois=='12' )
{
    $mois_precedent = '11';
    $mois_suivant = '01';
    $annee_mois_precedent = $annee;
    $annee_mois_suivant = $annee + 1;
}
elseif( $mois=='01' )
{
    $mois_precedent = '12';
    $mois_suivant = '02';
    $annee_mois_precedent = $annee - 1;
    $annee_mois_suivant = $annee;
}
else
{
    $mois_precedent = sprintf( '%02s' , $mois-1 );
    $mois_suivant = sprintf( '%02s' , $mois+1 );
    $annee_mois_precedent = $annee;
    $annee_mois_suivant = $annee;
}


$jour_en_cours = date( 'd' );

$mois_francais = array(
    'Janvier' , 'Février' , 'Mars' ,
    'Avril' , 'Mai' , 'Juin' ,
    'Juillet' , 'Août' , 'Septembre' ,
    'Octobre' , 'Novembre' , 'Décembre'
);

$dt_deb_genere = "{$annee}-{$mois}-01";
$dt_fin_genere = "{$annee_mois_suivant}-{$mois_suivant}-01";

$dt_date = mktime( 0 , 0 , 0 , $mois*1 , 1 , $annee*1 );
$jour_de_la_semaine = date( 'w' , $dt_date );

?>
于 2013-09-12T09:43:38.550 回答
0

对于未来,使用准备好的语句。它们允许您先发送查询,然后再发送值,从而防止注入。我更喜欢使用PDO

无论如何,数据库中的第一条规则:验证或清理用户输入。


如果您确定输入应该是一个数字,只需将其强制为一个数字:

$number = intval($_GET['number']);

在这种情况下,如果用户将其更改为一点文本,intval()将返回 0。


对于mysql_函数,如果输入是字符串,请使用mysql_real_ecsape_string()

$string = mysql_real_eascape_string($_GET['string']);

这将转义所有可能影响 SQL 查询的“不需要的”字符。但是,此功能也受到损害。

于 2013-09-12T09:30:35.043 回答
0

使用准备好的语句和参数化查询。这些是与任何参数分开发送到数据库服务器并由其解析的 SQL 语句。这样攻击者就不可能注入恶意 SQL。

你基本上有两种选择来实现这一点:

  1. 使用PDO
  2. 使用mysqli
于 2013-09-12T09:35:04.513 回答
0

必需的 :

  • 使用 mysqli 等更多保护功能保护您的数据库层
  • 逃避你所有的陈述
  • 验证所有输入

可选/懒惰:

  • 摆脱 $_GET 变量并使用 $_POST 或 $_SESSION
于 2013-09-12T09:35:29.143 回答
0

尝试:

if($annee == "" || intval($annee) == 0 ) {
    $annee = date("Y");
}
于 2013-09-12T09:36:25.203 回答