关于“通过每分钟限制 30 次查找来降低遭受全面暴力攻击的可能性”。
实际上并非如此 - 攻击者可以进行 100 次请求。每次请求可能需要 2 秒,但这并不会阻止请求的数量。您需要阻止每 2 秒处理超过一次请求,而不是在每次执行时延迟 2 秒。
(PHP 4, PHP 5, PHP 7, PHP 8)
sleep — 延迟执行
将程序执行延迟给定的seconds
秒。
注意:
要将程序执行延迟几分之一秒,请使用 usleep(),因为 sleep() 函数期望一个 int。例如,
sleep(0.25)
将暂停程序执行0
秒。
seconds
暂停时间,以秒为单位(必须大于或等于 0
)。
成功时返回零。
如果调用被信号中断,sleep() 返回非零值。在 Windows 上,此值将始终为 192
(Windows API 中 WAIT_IO_COMPLETION
常量的值)。在其他平台上,返回值将是剩余的睡眠秒数。
如果指定的seconds
秒数为负数,此函数将抛出 ValueError。
版本 | 描述 |
---|---|
8.0.0 | 该函数在负seconds 情况下抛出 ValueError;以前,会改为引发 E_WARNING ,并且该函数返回 false 。 |
示例 #1 sleep() 示例
<?php
// 当前时间
echo date('h:i:s') . "\n";
// 休眠 10 秒
sleep(10);
// 醒来!
echo date('h:i:s') . "\n";
?>
此示例将输出(10 秒后)
05:31:23 05:31:33
关于“通过每分钟限制 30 次查找来降低遭受全面暴力攻击的可能性”。
实际上并非如此 - 攻击者可以进行 100 次请求。每次请求可能需要 2 秒,但这并不会阻止请求的数量。您需要阻止每 2 秒处理超过一次请求,而不是在每次执行时延迟 2 秒。
注意:set_time_limit() 函数和配置指令 max_execution_time 仅影响脚本本身的执行时间。在脚本执行之外发生的任何活动所花费的时间,例如使用 system() 的系统调用、sleep() 函数、数据库查询等,在确定脚本运行的最大时间时不包括在内。
可能很明显,但这是我的函数,用于使用小数来延迟脚本执行(例如,模仿 sleep(1.5))
<?php
/**
* 延迟脚本执行指定时间。
* @param mixed $time 要暂停脚本执行的时间。可以表示为整数或小数。
* @example msleep(1.5); // 延迟 1.5 秒
* @example msleep(.1); // 延迟 100 毫秒
*/
function msleep($time)
{
usleep($time * 1000000);
}
?>
Diego Andrade 的 msleep 函数与 php7 的 `strict_types` 不兼容,将 usleep 参数强制转换为 int,它将是
usleep((int)($time * 1000000));
您应该在通过和失败的分支中都放置 sleep,因为攻击者可以检查响应是否缓慢并将其用作指示器 - 减少延迟时间。但在两个分支中都延迟可以消除这种可能性。
为了避免使用并非在所有操作系统上都可用的 usleep 函数,下面是一个应该具有相同行为的函数
function wait(int $millisecond = 0) {
if (0 !== $millisecond) {
$seconds = (int) ($millisecond / 1000);
$nanoSeconds = ($millisecond % 1000) * 1000000;
time_nanosleep($seconds, $nanoSeconds);
}
}
根据我的测试,调用 sleep(0); 将执行 `线程自旋`。
如果能够在文档中明确说明这一点会很好,因为它很有用。您可以进行最小的等待,而不会过载 CPU 线程。
使用 sleep() 来实现延迟输出效果是一个糟糕的主意,因为
1) 您必须在休眠之前刷新() 输出
2) 根据您的设置,flush() 不会一直工作到浏览器,因为 Web 服务器可能会应用自己的缓冲,或者浏览器可能不会渲染它认为不完整的输出
例如,Netscape 仅显示完整的行,并且在收到 </table> 标签之前不会显示表格部分
因此,如果您必须等待事件并且不想消耗太多周期,请使用 sleep,但不要将其用于愚蠢的延迟输出效果!
如果您在 sleep() 和 usleep() 响应不符合您的预期时遇到问题,请查看 session_write_close()
正如匿名在评论中指出的那样;
"如果 ajax 函数没有执行 session_write_close(),那么您的外部页面将看起来挂起,并且在新标签页中打开其他页面也会停滞。"
我编写了一个简单的使用浮点数休眠的方法,它还允许您执行毫秒(通过分数秒)。
<?php
function sleepFloatSecs($secs) {
$intSecs = intval($secs);
$microSecs = ($secs - $intSecs) * 1000000;
if($intSecs > 0) {
sleep($intSecs);
}
if($microSecs > 0) {
usleep($microSecs);
}
}
?>
在我的机器上测试,它运行良好
<?php
$x = [0.100,0.250,0.5,1.0,1.5,2.0,2.5];
foreach($x as $secs) {
$t = microtime(true);
sleepFloatSecs($secs);
$t = microtime(true) - $t;
echo "$secs \t => \t $t\n";
}
?>
输出
<?php
0.1 => 0.10017800331116
0.25 => 0.25016593933105
0.5 => 0.50015211105347
1 => 1.0001430511475
1.5 => 1.5003218650818
2 => 2.000167131424
2.5 => 2.5002470016479
?>
请注意,sleep() 会延迟当前会话的执行,而不仅仅是脚本。考虑以下示例,其中两台计算机从浏览器调用同一个脚本,该脚本除了 sleep() 什么也不做。
PC 1 [开始 14:00:00]: script.php?sleep=10 // 将在 10 秒后停止
PC 1 [开始 14:00:03]: script.php?sleep=0 // 将在 7 秒后停止
PC 2 [开始 14:00:05]: script.php?sleep=0 // 将立即停止
https://php.net/session_write_close 可以用来解决这个问题。
以下是一种在完成睡眠周期之前将输出刷新到浏览器的简化方法。请注意,在 sleep() 发生之前,缓冲区必须用 4096 个字符(字节?)“填充”才能使 ob_flush() 工作。
<?php
ob_implicit_flush(true);
$buffer = str_repeat(" ", 4096);
echo "立即查看此内容。<br>";
echo $buffer;
ob_flush();
sleep(5);
echo "已经过去了一些时间";
?>
这将允许你使用负值或低于 1 秒的值。
<?php slaap(0.5); ?>
<?php
function slaap($seconds)
{
$seconds = abs($seconds);
if ($seconds < 1):
usleep($seconds*1000000);
else:
sleep($seconds);
endif;
}
?>
请记住,sleep() 表示“让 PHP 有时间做一些其他的事情”。
这意味着 sleep() 可以被信号中断。如果你使用 pcntl_signal() 和其相关函数,这一点很重要。
要使用浮点数或整数
function pause($seconds)
{
usleep($seconds * 1000000);
}
pause(0.25);
一个简单的函数来报告自上次调用或自首次调用以来的微秒时间。
<?php
function stopWatch($total = false,$reset = true){
global $first_called;
global $last_called;
$now_time = microtime(true);
if ($last_called === null) {
$last_called = $now_time;
$first_called = $now_time;
}
if ($total) {
$time_diff = $now_time - $first_called;
} else {
$time_diff = $now_time - $last_called;
}
if ($reset)
$last_called = $now_time;
return $time_diff;
}
?>
$reset - 如果为 true,则将 last_called 值重置为现在
$total - 如果为 true,则返回自首次调用以来的时间,否则返回自上次调用以来的时间
一种非常简单但有效的方法,可以大幅减缓对错误密码尝试的暴力攻击。
在我的以下示例中,如果最终用户输入了正确的密码,他们就可以以预期的方式全速登录。对于每次错误的密码尝试,用户的响应都会延迟 2 秒;这将最大限度地减少每分钟 30 次查找的暴力攻击。
我希望这种非常简单的方法有助于使你的 Web 应用程序更加安全。
Ashley
<?php
public function handle_login() {
if($uid = user::check_password($_REQUEST['email'], $_REQUEST['password'])) {
return self::authenticate_user($uid);
}
else {
// 将失败的输出延迟 2 秒
// 以防止暴力攻击
sleep(2);
return self::login_failed();
}
}
?>
由于 sleep() 可以被信号中断,因此我编写了一个也可以被中断的函数,但在信号到达后(可能由回调处理)会继续睡眠。当编写守护进程并需要 sleep() 函数按你“命令”的方式工作,但能够在睡眠期间接受信号时,这非常有用。
<?php
function my_sleep($seconds)
{
$start = microtime(true);
for ($i = 1; $i <= $seconds; $i ++) {
@time_sleep_until($start + $i);
}
}
?>
不滥用 sleep( ) 的另一个原因是,除了最多 30 个 sql 连接之外,共享托管环境通常将进程数量限制为 20 个,如果你的网站有很多用户在线,并且在代码中到处使用 sleep( ),你的服务器将抛出一个 508 错误(资源限制已达)并停止服务你的网站。
带毫秒参数的 Sleep 方法
public static function ms_sleep($milliseconds = 0) {
if($milliseconds > 0) {
$test = $milliseconds / 1000;
$seconds = floor($test);
$micro = round(($test - $seconds) * 1000000);
if($seconds > 0) sleep($seconds);
if($micro > 0) usleep($micro);
}
}
使用 sleep() 等时间延迟函数至关重要,因为初学者可能会发现它不起作用,并且他们会看到所有输出同时出现。
一种很好的实现方法是使用 ob_implicit_flush() 函数,这样你就不需要显式使用 flush() 函数。
示例代码
<?php
ob_implicit_flush(true);
for($i=0;$i<5;$i++)
{
$dis=<<<DIS
<div style="width:200px; background-color:lime;border:1px; text-align:center;text-decoration:blink;">
$i
</div>
DIS;
echo $dis;
sleep(5);
//flush();
}
使用 sleep 在不同时间间隔执行一组函数的示例。这不是多线程的替代方案,但它可以帮助那些想要执行一些简单操作的人。您无需使用 eval()。它只是作为一个示例。这与运行标准的 1 秒睡眠循环不同,因为睡眠时间越长,CPU 消耗就越少。
<?php
// 当前时间
echo date('h:i:s') . "\n";
// 一些示例函数
function function_a() { echo 'function_a called @ ' . date('h:i:s') . PHP_EOL; }
function function_b() { echo 'function_b called @ ' . date('h:i:s') . PHP_EOL; }
function function_c() { echo 'function_c called @ ' . date('h:i:s') . PHP_EOL; }
// 添加一些计时器(以秒为单位)以及函数调用
$sleeptimers = array();
$sleeptimers['5'][0]['func'] = 'function_a();';
$sleeptimers['10'][0]['func'] = 'function_b();';
$sleeptimers['15'][0]['func'] = 'function_c();';
// 处理计时器
while(true) {
$currenttime = time();
reset($sleeptimers);
$mintime = key($sleeptimers);
foreach($sleeptimers as $SleepTime => $Jobs) {
foreach($Jobs as $JobIndex => $JobDetail) {
if(!isset($JobDetail['lastrun'])) {
$sleeptimers[$SleepTime][$JobIndex]['lastrun'] = time();
if($SleepTime < $mintime) $mintime = $SleepTime;
} elseif(($currenttime - $JobDetail['lastrun']) >= $SleepTime) {
eval($JobDetail['func']);
$lastrun = time();
$sleeptimers[$SleepTime][$JobIndex]['lastrun'] = $lastrun;
$mysleeptime = $SleepTime - ($currenttime - $lastrun);
if($mysleeptime < 0) $mysleeptime = 0;
if(($currenttime - $JobDetail['lastrun']) < $mintime) $mintime = $mysleeptime; // 考虑函数运行时长
echo 'Sleep time for function ' . $JobDetail['func'] . ' = ' . $mysleeptime . PHP_EOL;
}
}
}
echo 'Sleeping for ' . $mintime . ' seconds' . PHP_EOL;
sleep($mintime);
}
?>
我希望这段代码能帮助某些人解决无法刷新或将缓冲区输出到浏览器的问题(我使用的是 IE7)。
您可能只需要使用 [ echo str_repeat(".", 4096); ] 就可以,甚至不需要使用 ob_... 和 flush。
<?php
ob_start();
ob_implicit_flush(true);
//[ OR ] echo "..."; ob_flush(); flush();
set_time_limit(0);
function sleep_echo($secs) {
$secs = (int) $secs;
$buffer = str_repeat(".", 4096);
//echo $buffer."\r\n<br />\r\n";
for ($i=0; $i<$secs; $i++) {
echo date("H:i:s", time())." (".($i+1).")"."\r\n<br />\r\n".$buffer."\r\n<br />\r\n";
ob_flush();
flush();
sleep(1);
//usleep(1000000);
}
}
sleep_echo(30);
ob_end_flush();
?>