Meituan interview: The interface is maliciously swiped, what should I do?

If the Java interface is maliciously swiped, we can generally take the following measures:

Use TimeStamp (soldiers never tire of cheating)

For example, provide a timestamp parameter to the client, the value is a 13-digit millisecond-level timestamp, you can make a check digit at the 12th or 13th digit, and perform a checksum on the other 12-digit values ​​through a certain algorithm.

Example: Now the actual time is 1684059940123. I calculated the check digit to be 9 through the first 12 digits, and changed the last digit to 9 in the parameter, which is 1684059940129. After the value is transmitted to the server, the value can also be calculated through the first twelve digits , to determine whether the timestamp is legal.

Calculate the check digit based on the previous 12 digits, you can use the following code to achieve:

/**
 * 计算timestamp的校验位
 */
private int calculateCheckDigit(long timestamp) {
    int sum = 0;
    for (int i = 0; i < TIMESTAMP_DIGITS - 1; i++) {
        char c = String.valueOf(timestamp).charAt(i);
        sum += Integer.parseInt(String.valueOf(c));
    }
    return sum % 10;
}

In the above code, the calculateCheckDigit method receives a 13-digit millisecond-level timestamp as a parameter and returns the check digit of the timestamp. Specifically, the method loops through the first 12 digits of the timestamp and adds the numbers. Finally, the single digit of the sum is calculated and returned.

For example: if the timestamp is 1684059940123, the check digit calculated by this method is 9.

But if a hacker flips through the client code, he will know the check digit. Because this verification logic client must also have a copy. If the client code is public, then an attacker can learn how the check digit is calculated by studying the client code. In this way, it is possible for an attacker to forge the timestamp parameter with the correct check digit, thereby bypassing the current limiting and security mechanism of the Java interface.

Therefore, this solution is mainly suitable for scenarios that require simple defense against some low-intensity attacks, such as preventing spam requests or illegal crawlers. For high-intensity attacks, it is recommended to adopt more complex authentication strategies, such as using OAuth2 authorization, IP whitelist, signature algorithm, etc. At the same time, it is recommended that the client and server use the HTTPS protocol for encryption during communication to prevent data from being eavesdropped or tampered with.

Enhanced Security Authentication

@RequestMapping("/api/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password){
    if(!checkUser(username, password)){
        return "用户名或密码错误";
    }
    String token = getToken();
    saveToken(token);
    return token;
}

private boolean checkUser(String username, String password){
    //校验用户是否合法
}

private String getToken(){
    //生成token
}

private void saveToken(String token){
    //保存token
}

In the above code, when the user calls the login interface, the user name and password need to be provided. At this time, user verification will be performed. If the verification fails, an error message will be returned. Otherwise, a token will be generated and saved, and finally returned to the user.

The function of generating Token is to verify the user's identity when requesting the interface. Specifically, when a user logs in to the system for the first time, the interface can generate a Token string based on user information and save it to the server or client. When the user accesses other interfaces that require authentication, the Token string needs to be included in the request header so that the server can perform authentication.

For the problem of Java interface being maliciously swiped, the function of Token is to prevent illegal requests. Since the token is generated by the server, the attacker cannot generate a valid token by itself, so only users with a valid token can successfully call the relevant interface.

Regarding the verification of Token, it can be realized through interceptors. The interceptor can check whether the request header contains a legal Token before calling the interface, and verify whether the Token has expired or been tampered with. If the token verification fails, an error message is returned and the request is intercepted.

The following is the sample code for generating Token and using the interceptor:

// 生成Token
private String generateToken(User user) {
    // 基于用户名、密码、时间戳等信息生成Token字符串
}

// 鉴权拦截器
public class AuthInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null || !checkToken(token)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        return true;
    }

    private boolean checkToken(String token) {
        // 验证Token是否合法,是否过期等
    }
}

In the above code, the generateToken method is used to generate a Token string and save it to the server or client. The AuthInterceptor class is an interceptor class used to check whether the Token in the request header is legal. If the token verification fails, return a 401 error code and intercept the request. It should be noted that when using this interceptor, it needs to be registered in the Spring MVC framework.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor).addPathPatterns("/api/**");
    }
}

The above code is to register the AuthInterceptor interceptor into the Spring MVC framework to make it effective. Among them, "/api/**" means to intercept all interfaces starting with "/api".

IP ban

public class IpFilter extends OncePerRequestFilter {
    private static final Set<String> IP_SET = new HashSet<>();

    static {
        IP_SET.add("192.168.1.100");
        IP_SET.add("127.0.0.1");
        //添加其他需要封禁的IP
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String ipAddress = request.getRemoteAddr();
        if(IP_SET.contains(ipAddress)){
            response.setStatus(HttpStatus.FORBIDDEN.value());
            return;
        }

        filterChain.doFilter(request, response);
    }
}

In the above code, the IpFilter filter is used to prevent specific IP addresses from accessing the interface. Among them, IP_SET is the set of IP addresses that need to be banned.

Interface current limiting

public class RateLimitInterceptor extends HandlerInterceptorAdapter {
    private static final int DEFAULT_RATE_LIMIT_COUNT = 100; //默认每秒允许100次请求
    private static final int DEFAULT_RATE_LIMIT_TIME_WINDOW = 1000; //默认时间窗口为1秒
    private Map<String, Long> requestCountMap = new ConcurrentHashMap<>();
    private int rateLimitTimeWindow;
    private int rateLimitCount;

    public RateLimitInterceptor(){
        this(DEFAULT_RATE_LIMIT_TIME_WINDOW, DEFAULT_RATE_LIMIT_COUNT);
    }

    public RateLimitInterceptor(int timeWindow, int limitCount){
        this.rateLimitTimeWindow = timeWindow;
        this.rateLimitCount = limitCount;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String key = request.getRemoteAddr() + ":" + request.getRequestURI();
        long now = System.currentTimeMillis();
        if(requestCountMap.containsKey(key)){
            long last = requestCountMap.get(key);
            if(now - last < rateLimitTimeWindow){
                if(requestCountMap.get(key) >= rateLimitCount){
                    response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
                    return false;
                }
                requestCountMap.put(key, requestCountMap.get(key) + 1);
            }else{
                requestCountMap.put(key, now);
            }
        }else{
            requestCountMap.put(key, now);
        }
        return true;
    }
}

In the above code, the interface current limiting function is implemented through the RateLimitInterceptor interceptor. By default, a maximum of 100 requests are allowed per second. If the number of requests in a second exceeds the limit, status code 429 will be returned.

log monitoring

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    String requestURI = httpRequest.getRequestURI();

    try {
        log.info("Request Received. URI: {}", requestURI);
        chain.doFilter(request, response);
    } catch(Exception e) {
        log.error("Exception occurred while processing request. URI: {}", requestURI, e);
        throw e;
    } finally {
        log.info("Request Completed. URI: {} Response Status: {}", requestURI, httpResponse.getStatus());
    }
}

In the above code, log monitoring is implemented through the Filter filter. Record the request URI when the request enters, and record the response status code when the request ends, so that abnormal situations can be found in time.

Upgrade hardware equipment

If the server cannot withstand malicious attacks, the carrying capacity of the server can be increased by upgrading the hardware equipment. For example, hardware resources such as CPU or memory can be increased to reduce the response time of the server.

Notify relevant departments

When the Java interface is maliciously swiped, it is very important to notify the relevant management personnel or security team in time. They can take more effective measures, such as banning IP addresses and strengthening authentication mechanisms, to ensure the security of the interface. Here is some sample code:

//发送邮件通知相关部门
public void sendNotificationEmail(String subject, String content, String... recipients) {
    SimpleMailMessage message = new SimpleMailMessage();
    message.setFrom("[email protected]");
    message.setTo(recipients);
    message.setSubject(subject);
    message.setText(content);
    
    mailSender.send(message);
}

//发送短信通知相关部门
public void sendNotificationSMS(String phoneNumber, String content) {
    //调用第三方短信API发送短信
}

Summarize

1. In most cases, token verification + interceptors are enough, and sensitive interfaces are combined with current limiting, which is also the practice of most enterprises.

2. TimeStamp is enough for simple scenarios. For most malicious access by robots, it is impossible for him to read your code.

3. Try not to block the IP to prevent accidental injury, because many residents in the community are often on the LAN, and many people share the same IP.

4. The more ruthless method is to imitate the practice of "please choose the car in the picture" and reverse the wool. Well, everyone should understand the meaning.

Guess you like

Origin blog.csdn.net/weixin_39570751/article/details/130866218