使用PHP构建更直观的聊天过滤器

*API

我已经建立了一个基本的*性API,如果可以识别出它,则回显1,如果消息还可以,则返回0.我遇到了一些愚蠢的问题.

例如,如果“地狱”一词在我的誓言清单中,它还会将“ hello”等词识别为*行为.

每个字都以这种格式保存在txt文件中

badword
badword
badword
lolanotherbadword
naughtyword

说话说话

1 4l50 w4n7 70 1mpl3m3n7 50m3 50r7 0f ​​l337 func710n,50 7h47 1 d0n’7 h4v3 70 l157 3v3ry p0551bl3 v4r14710n 0f 7h3 w0rd. (我也想实现某种leet函数,这样就不必列出单词的所有可能变体.)

绕过聊天过滤器

是否从

api.domain.tld / chat / profanity.php?access_token =任何& filter_string =任何

要么

api.domain.tld /聊天/*/ access_token / filter_string

发生相同的问题.如果人们把&要么 ?在他们的消息之前,它允许他们绕过过滤器(并回显0).在检查日志时,我注意到以&开头的消息要么 ?被记录为空白消息,所以我猜它只是弄乱了变量或其他东西.

间距

人们认为自己很聪明,可以说出“快乐”或“快乐”等.直观的聊天过滤器很可能能够识别这种事情.

数据存储与检索

我也一直在想自己txt文件是否真的是有效的存储和检索机制.现在我只有400个字,但它会继续增长,而且一定会很慢.什么是更好的?嵌入式PHP数组,txt文件还是数据库?

编码

<?php
require('conn.php');

$date     = gmdate('Y-m-d');
$time = gmdate('h:i:s');

$access_token  = $_GET["access_token"];
$filter_string = $_GET["filter_string"];

function wordsExist(&$string, $words)
{
    foreach ($words as &$word) {
        if (stripos($string, $word) !== false) {
            return true;
        }
    }
    return false;
}

if (isset($access_token)) {
    $sql  = "SELECT * FROM api WHERE access_token='" . $access_token . "'";
    $sql2 = "UPDATE api SET calls = calls + 1 WHERE access_token='" . $access_token . "'";
    $sql3 = "UPDATE api SET last_query = CURRENT_TIMESTAMP WHERE access_token='" . $access_token . "'";
    $sql4 = "UPDATE api SET profanity_api_calls = profanity_api_calls + 1 WHERE access_token='" . $access_token . "'";
    $sql5 = "UPDATE api SET last_profanity_query = CURRENT_TIMESTAMP WHERE access_token='" . $access_token . "'";

    $sql6 = "UPDATE api SET profanity_detected = profanity_detected + 1 WHERE access_token='" . $access_token . "'";

    $result  = mysqli_query($conn, $sql);
    $result2 = mysqli_query($conn, $sql2);
    $result3 = mysqli_query($conn, $sql3);
    $result4 = mysqli_query($conn, $sql4);
    $result5 = mysqli_query($conn, $sql5);
    if (mysqli_num_rows($result) >= 1) {
        if (wordsExist($filter_string, file('curse-list.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES))) {
            $result6 = mysqli_query($conn, $sql6);
            file_put_contents('logs/profanity/' . $date . '-log.txt', "1 [$time] $filter_string\n", FILE_APPEND);
            echo '1';
        } else {
            file_put_contents('logs/profanity/' . $date . '-log.txt', "0 [$time] $filter_string\n", FILE_APPEND);
            echo '0';
        }
    }
}

mysqli_kill();
mysqli_close();
?>

我的.htaccess

RewriteEngine On
RewriteRule ^profanity/(.*)/(.*)$profanity.php?access_token=$1&filter_string=$2
RewriteRule ^advertising/(.*)/(.*)$advertising.php?access_token=$1&filter_string=$2

转义用户输入

照原样-以上代码实现的安全性如何?如果它容易受到攻击,那么我可以举一些黑客如何滥用它的具体例子吗?

解决方法:

您可以对代码进行一些快速更改,以解决部分但不是全部问题.

1)您的代码容易受到SQL注入攻击的攻击,攻击者可以在其中编写将成为SQL查询的url,并对数据库执行各种意外操作.尽快修复这些问题:

  $access_token = mysqli_real_escape_string($conn, $access_token);

2)将您的过滤器字符串分成单个单词,这将解决问候问题.客户可以使用单词之间的空格以外的字符. preg_split将允许您指定要分割的字符范围.

$filter_words = preg_split("/[\s,\-_]+/", $string);

3)通过使用单词soundex而不是精确文本来测试模糊匹配.在PHP中,soundex是输入字符串的发音的4个字符表示.预期任何模糊匹配都会产生一些误报.

if(soundex($filter_word) == soundex($word)) ...

有关如何根据空格和下划线分割单词并与单词列表进行比较的其他示例:

function wordsExist($filter_string, $words)
{
    $filter_words = preg_split("/[\s,\-_]+/", $filter_string);

    foreach ($words as $word) {
        foreach($filter_words as $filter_word) {
            if (
                ($filter_word == $word ) ||
                (levenshtein($filter_word, $word) < 2) ||
                (soundex($filter_word) == soundex($word))
                ) {

                return true;
            }

        }
    }
    return false;
}

我添加了soundex和levenshtein作为比较单词的不同方法.在我进行的几次快速测试中,我得到了一些误报,因此您可以决定是否保留这些行.

我还注意到您使用了“&”别名变量的运算符.这与“&”不同在C中可用于通过引用传递.别名通常不会对性能产生任何好处,因为PHP会对变量的复制过程进行监听,直到以后将其中之一写入变量为止.上面有一个很好的SO问题:In PHP (>= 5.0), is passing by reference faster?

上一篇:PHP mysqli_query在实际的托管服务器上不起作用


下一篇:在PHP中显示表中的数据