如何限制每秒 Web 请求以避免垃圾邮件和拒绝服务

Posted

技术标签:

【中文标题】如何限制每秒 Web 请求以避免垃圾邮件和拒绝服务【英文标题】:How to Limit web requests per second to avoid spamming and denial of service 【发布时间】:2015-12-10 05:55:36 【问题描述】:

我有一个网页,它接受用户的搜索查询并在数据库中搜索它。由于此查询很耗时,即使有适当的索引,我也想将搜索请求限制为每个用户每 2 秒 1 个。目前我正在使用以下方法(粗略草图)

限制:1) 我的网站没有登录系统。相反,我依赖于请求标头来获取用户的 IP 地址和用户 ID。

算法:

1) Maintain a map of ips and user ids and their latest search timestamp.
2) For every search request, 
   2.1) If user has searched in last two seconds: Show error message
   2.2) Else, allow him to search

我想知道这种方法是否足够?由于 ip 地址和用户 id 都来自请求标头,垃圾邮件发送者是否可以更改请求标头?

【问题讨论】:

可以更改一些请求标头,但不能更改 IP,当您考虑 IP 设置在 Socket 通信级别时。 @QuakeCore 欺骗 IP 地址是完全可能的,而且很常见,我相信如果我要设计 DDOS 攻击我会这样做(事实并非如此)。限制每个 IP 地址的搜索次数只会干扰真实用户,而不是故意的攻击者。但它可以防止无意的用户垃圾邮件。 试图阻止 ddos​​ 攻击的软件对您没有多大帮助,因为该攻击早在它到达像 Java 这样的 vm 语言之前就已经攻击了您的硬件。 @Stefan ,搜索查询平均需要一秒半。我们只是想限制这一点,以便应用程序不会冻结。只是对开发人员进行安全检查。 【参考方案1】:

这是一个简单的 servlet 过滤器,可以让用户在一段时间内无法访问资源(可能需要改进):

// (requires http sessions enabled)
public class AccessControlFilter implements Filter 

private final String ACCESS_CONTROL_SESSION_KEY = String.valueOf("NextAccessAllowedTime".hashCode());

// how long to prevent users from accessing in ms?
private final long penaltyTime = 1000 * 2;

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 
    // is http request?
    if (req instanceof HttpServletRequest) 
        HttpSession session = ((HttpServletRequest) req).getSession();

        // has access value in session?
        Object value = session.getAttribute(ACCESS_CONTROL_SESSION_KEY);
        if (value != null) 

            Date nextAccessAllowed = new Date(((long) value));
            if (new Date().before(nextAccessAllowed)) 

                // handle access denied (better redirect to another url or display message then sending this response code?)
                ((HttpServletResponse) res).sendError(HttpServletResponse.SC_FORBIDDEN, "Access to this resource is denied until: " + nextAccessAllowed);
                res.flushBuffer();
                return;
            
        
        session.setAttribute(ACCESS_CONTROL_SESSION_KEY, System.currentTimeMillis() + penaltyTime);
    
    chain.doFilter(req, res);


@Override
public void init(FilterConfig config) throws ServletException 
    System.out.println("Starting " + this.getClass().getSimpleName() + " on resource: \"" + config.getServletContext().getFilterRegistration(config.getFilterName()).getUrlPatternMappings().iterator().next() + "\" with penalty time: "
            + penaltyTime + "ms!");


@Override
public void destroy() 
    System.out.println("Stopping " + this.getClass().getSimpleName());


像这样在 web.xml 中启用它:

<filter>
    <filter-name>acf</filter-name>
    <filter-class>your.package.AccessControlFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>acf</filter-name>
    <!-- Resource to check. Should be the url your search form posts to! -->
    <url-pattern>/search/*</url-pattern>
</filter-mapping>

如果您正在寻找 http 服务器级别的模块,请查看“mod evasive”。

也许你应该尝试缓存你的搜索结果(即ehcache)并创建一个固定线程池来处理搜索,这样你就可以控制一次搜索的数量。

【讨论】:

以上是关于如何限制每秒 Web 请求以避免垃圾邮件和拒绝服务的主要内容,如果未能解决你的问题,请参考以下文章

将网站电子邮件列入白名单,使其不会作为垃圾邮件被拒绝

避免拒绝服务攻击

如何使用 Apache 实现速率限制? (每秒请求数)

如何在网站上显示电子邮件地址以避免垃圾邮件?

从 yandex smtp 获得大量垃圾邮件拒绝

邮件发送和接收限制