0

I've devloped a chat bot application using the Facebook Messenger platform. I used Spring Boot with embedded Tomcat for the web platform. The application should run on Amazon aws, open to the WWW, and to be used as a webhook for recieving callbacks from Messenger over https.

I need an advice how to secure the application, so it won't be hacked or flooded with requests that are not coming from Facebook.

I thought to make the application require secured (ssl) connection, but using the "security.require_ssl=true" in application.properties didn't do the work. Perhaps I don't know what is the meaning of this and how to configure it propertly. Is there a best practice how to block requests which are not https requests? Or a way to block requests which are coming outside Messenger in the application level?

Thank you very much!

EDIT

In the meantime, I blocked requests from other IPs in application layer using the handler interceptor:

@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;
                }
            }

}
Uziel Sulkies
  • 598
  • 5
  • 15
  • From facebook reference documentation, I see there is a possibility to set a callback token (https://developers.facebook.com/docs/messenger-platform/webhook-reference), have you configured one? do you receive it on spring application side (as a path or get parameter) ? – Guy Bouallet May 18 '16 at 14:47
  • @GuyBouallet The token is for a first-time verification. The ongoing callbacks do not contain any token. Did you see something that I missed? – Uziel Sulkies May 18 '16 at 15:23
  • 1
    Have you checked if there is a http header called X-Hub-Signature In the requests received by tomcat? – Guy Bouallet May 18 '16 at 23:51
  • @GuyBouallet Thank you! I really did missed this point. Now I just have to find out out to compare the payload with the signature. Do you know how to create a "unicode escaped hex lowercase string" from a simple json? I beleive I can use the interceptor too, right? – Uziel Sulkies May 19 '16 at 05:42
  • I added an answer so that you can accept it. Please see sample implementa – Guy Bouallet May 19 '16 at 08:16

3 Answers3

1

a lot to say so I am sure I will miss some points.

  • setting SSL is a first good thing but make sure you get a certificate. lets encrypt is a good thing if you dont want to pay for SSL certificate.

    Just seeing aws provides an alternative to letsencrypt

  • Security Group You can see Security Group as something similar to a firewall so you can control which port is opened, external and internal flows.

  • Look at IAM which control who and how can get access to your AWS account

  • obvious : change your password. do not let default password for installation you could make on the instance

read some of https://aws.amazon.com/security/security-resources/ to get more information about what you can do

it won't be hacked or flooded with requests

sorry to say but most probably it will be - It does not need to be an advanced hacker to run scanner and scan IPs and check open ports / brute force login etc ...

Community
  • 1
  • 1
Frederic Henri
  • 51,761
  • 10
  • 113
  • 139
  • Your answer gives interesting advices but it is focused on securing the AWS server side in general. The question involves a specific client which is facebook; So securing the communication requires to check what facebook support (SSL + payload signature). – Guy Bouallet May 19 '16 at 09:02
1

You should check the X-Hub-signature HTTP header available in the requests sent by Facebook to your webhook URL.

In your case, you may define a filter or interceptor for the verification of the signature. You can also do it in your controller as in the this example I found in RealTimeUpdateController.java from the spring social project.

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);

    }
Guy Bouallet
  • 2,099
  • 11
  • 16
1

Thanks to Guy Bouallet help I added the signature check. I added it in my controller and not in the interceptor, to avoid the problem of How to read data twice in spring which seems a little complicated.

So here is it:

@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;
    }

}

And this is the Exception handling class:

    @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;
    }
Uziel Sulkies
  • 598
  • 5
  • 15