在Amazon上部署Spring启动应用程序的最佳实践

时间:2016-05-18 10:39:48

标签: spring-mvc amazon-web-services spring-security spring-boot facebook-messenger

我使用Facebook Messenger platform开发了一个聊天机器人应用程序。 我使用Spring Boot嵌入式Tomcat作为Web平台。 该应用程序应该在Amazon aws上运行,对WWW开放,并用作webhook,用于通过https接收来自Messenger的回调。

我需要一个如何保护应用程序的建议,因此不会被来自Facebook的请求所攻击或充斥。

我想让应用程序需要安全(ssl)连接,但在application.properties中使用“security.require_ssl = true”并没有完成工作。也许我不知道这是什么意思以及如何配置它。 是否有最佳实践如何阻止非https请求的请求?或者一种阻止应用程序级别的Messenger外部请求的方法?

非常感谢!

修改

与此同时,我使用处理程序拦截器阻止了来自应用程序层中其他IP的请求:

@Configuration
public class MyWebApplicationInitializer implements WebApplicationInitializer, WebMvcConfigurer{


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptor() {

            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                if (! (request.getRemoteAddr().equals("173.252.88.66") || request.getRemoteAddr().equals("127.0.0.1")|| request.getRemoteAddr().equals("0:0:0:0:0:0:0:1"))){
                    logger.warn("Request is not coming from authorized remote address: " + request.getRemoteAddr()+". Rejecting");
                    response.getWriter().write("Unauthorized Address");
                    response.setStatus(401);
                    return false;
                } else {
                    return true;
                }
            }

}

3 个答案:

答案 0 :(得分:1)

很多话要说,所以我相信我会错过一些观点。

  • 设置SSL是第一件好事,但要确保获得证书。如果您不想支付SSL证书,lets encrypt是一件好事。

    只需seeing aws就可以替代letsencrypt

  • Security Group您可以将安全组视为类似于防火墙的内容,以便您可以控制打开的端口,外部和内部流程。

  • 查看IAM控制谁和如何访问您的AWS账户

  • 显而易见:更改密码。不要让您可以在实例上进行安装的默认密码

阅读一些https://aws.amazon.com/security/security-resources/以获取有关您可以执行的操作的更多信息

  

它不会被黑客攻击或被请求淹没

很遗憾地说,但最有可能的是 - 它不需要是一个高级黑客来运行扫描仪和扫描IP并检查开放端口/强力登录等...

答案 1 :(得分:1)

您应该检查Facebook发送到您的webhook网址的请求中提供的X-Hub签名HTTP标头。

在您的情况下,您可以定义过滤器或拦截器以验证签名。您也可以在控制器中执行此操作,如我在Spring社交项目的RealTimeUpdateController.java中找到的示例中所示。

private boolean verifySignature(String payload, String signature) throws Exception {

        if (!signature.startsWith("sha1=")) {

            return false;

        }

        String expected = signature.substring(5);       

        Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);

        SecretKeySpec signingKey = new SecretKeySpec(applicationSecret.getBytes(), HMAC_SHA1_ALGORITHM);

        mac.init(signingKey);

        byte[] rawHmac = mac.doFinal(payload.getBytes());

        String actual = new String(Hex.encode(rawHmac));

        return expected.equals(actual);

    }

答案 2 :(得分:1)

感谢Guy Bouallet的帮助,我添加了签名检查。 我将它添加到我的控制器而不是拦截器中,以避免How to read data twice in spring的问题,这似乎有点复杂。

所以就是这样:

@RequestMapping(path = "/")
public void doHandleCallback(@RequestBody String body, @RequestHeader(value = "X-Hub-Signature") String signature) throws IOException {
    if (!verifyRequestSignature(body.getBytes(), signature)){
        logger.error ("Signature mismatch.");
        throw new MismatchSignatureException(signature);
    }
    MessengerCallback callback = mapper.readValue(body, MessengerCallback.class);
    logger.info("Incoming Callback: " + body );
    for (EventData entry : callback.getEntry()) {
        for (ReceivedMessagingObject message : entry.getMessaging()) {
            if (message.isMessage() || message.isPostback()) {
                doHandleMessage(message);
            }
            else if (message.isDelivery()){
                doHandleDelivery(message);
            }
        }
    }       
}

private boolean verifyRequestSignature(byte[] payload, String signature) {
    if (!signature.startsWith("sha1="))
        return false;
    String expected = signature.substring(5);
    System.out.println("Expected signature: " + expected); //for debugging purposes

    String hashResult = HmacUtils.hmacSha1Hex(APP_SECRET.getBytes(), payload);
    System.out.println("Calculated signature: " + hashResult);
    if (hashResult.equals(expected)) {
        return true;
    } else {
        return false;
    }

}

这是异常处理类:

    @ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Request Signature mismatch")
    public class MismatchSignatureException extends RuntimeException {
    private String signature;

    public MismatchSignatureException(String signature) {
        this.signature = signature;
    }
    @Override
    public String getMessage() {
        return "Signature mismatch: " + signature;
    }