Twilio - Validating Incoming Callback Request - Java - java

When Twilio invokes a callback method to fetch the TwiML <Say> for Voice, I see that Twilio sets "x-twilio-signature" in the HTTP header.
I need to verify that the actual request came from Twilio.
I have a simple war file running on Tomcat and the app is built using Spring.
I did something like the following:
//Get the TwilioUtils object initialized
TwilioUtils twilioUtils = new TwilioUtils("******myAuthToken");
//Get the URL from HttpRequest
String url = httpRequest.getRequestURL().toString();
Map<String, String> allRequestParams = getAllRequestParams(httpRequest);
Map<String, String> headers = getAllRequestHeaders(httpRequest);
//Get the signature generated for the Url and request parameters
//allRequestParams is a map of all request values posted to my service by Twilio
String validSig = twilioUtils.getValidationSignature(url, allRequestParams);
//Get the x-twilio-signature value from the http header map
String xTwilioSignature = headers.get("x-twilio-signatureā€¯);
//This is different from what I get below
logger.info("validSig = " + validSig);
logger.info("xTwilioSignature = " + xTwilioSignature );
//This is always false
logger.info("Signature matched : " + twilioUtils.validateRequest(xTwilioSignature, url,
allRequestParams));
I would like to know what am I doing wrong. Is my approach to validate "x-twilio-signature" incorrect?
If it is incorrect, what's the right way to do it?
I am using the helper library class TwilioUtils provided by Twilio to validate it.
All the time the signature from Twilio is different from what I get from the TwilioUtils object.

Megan from Twilio here.
Are you following the steps suggested in the security documentation?
validateRequest expects three arguments. I believe you're missing the url there.
Consider this example:
public class TwilioUtilsExample {
public static void main(String[] args) {
// Account details
String accountSid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
String authToken = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY";
//This is the signature we expect
String expected_sig = "SSSSSSSSSSSSSSSSSSSSSSSSSSSS";
//This is the url that twilio requested
String url = "http://UUUUUUUUUUUUUUU";
//These are the post params twilio sent in its request
Map<String,String> params = new HashMap<String,String>();
// Be sure to see the signing notes at twilio.com/docs/security
TwilioUtils util = new TwilioUtils(authToken, accountSid);
boolean result = util.validateRequest(expected_sig, url, params);
if (result) {
System.out.print( "The signature is valid!\n" );
} else {
System.out.print( "The signature was NOT VALID. It might have been spoofed!\n" );
}
}
}
Hope this is helpful!

Related

SonnarQube's issue: change code to not construct the URL from user-controlled data

I facing a SonarQube bug and am not able to figure out whats the issue. SonnarQube's issue is, change this code to not construct the URL from user-controlled data.
#Value("${...}")
String apiKey;
#Value("${...}")
String apiUrl;
public Response apiResponse(String location) {
HttpHeaders headers = new HttpHeaders();
headers.add("x-apikey", apiKey);
HttpEntity<Object> entity = new HttpEntity<>(headers);
String url = apiUrl + location; // SonarQube issue: tainted value is propagated
Response response = null;
try {
ResponseEntity<Response> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, Response.class); // SonarQube issue: Tainted value is used to perform a security- sensitive operation.
response = responseEntity.getBody();
} catch(Exception){
// doesn't throw anything
}
return response;
}
#Cacheable(...)
Response cacheResponse(String location, String tokenKey) {
return apiResponse(location); // SonarQube issue: tainted value is propagated
}
This fixed the issue, but why is that so? and how can I apply this in the above code?
String url = apiUrl + location; // SonarQube issue: tainted
Instead, I just tried hardcoding the value of location and fixed the issue.
String url = apiUrl + "location";
So weird...
I added validation for the Location variable and this solved the issue
if(!location.matches(...)) {
throw error.....
}
String url = apiUrl + location;
What SonarQube is trying to tell you is that you are exposing your logic to input from the clients. A better solution would be to refactor your code to not depend on a specific header from the client to perform some action. Its hard to suggest sample code without seeing a little more of the codebase.
You are using input from the client/user (namly in the variable location) to construct an URL. So if the client/user supplies an malicious value to location he could form an invalid URL.
In the second example String url = apiUrl + "location"; you are not using user input, as "location" is a hard coded String.
I don't know what you try to achieve with the code. But maybe it's better to hold a list of possible URLs and the user supplies and enum value that maps to an URL.
String url = "https://someurl/%s";
url = String.format(url,location);
sendRequest(url);
Maybe this approach won't give error.

Problem with RestTemplate when including URL addresses in REST API request

I'm trying to obtain data from Botify's REST API to use it inside a project, which is also a REST API. I'm using an instance of Spring's RestTemplate class to make the actual requests to Botify, specifically the .exchange method as I need to pass Botify's key as a header parameter.
My problem comes when I need to call to a method of the endpoint which takes a URL as a part of the request's URI (not a parameter). Documentation of this endpoint is in https://developers.botify.com/api/reference/#!/Analysis/getUrlDetail
Basically the structure of the requests is like this:
/analyses/{username}/{project_slug}/{analysis_slug}/urls/{url}
The last part of that URI is a URL address, which needs to be encoded in UTF-8 to make it possible to separate it from the actual request.
The problem is (I believe) that the .exchange method always encodes the request, so what I try to send like this:
/analyses/myusername/myprojectname/myprojectslug/urls/https%3A%2F%2Fwww.example.com
...ends up like this:
/analyses/myusername/myprojectname/myprojectslug/urls/https%253A%252F%252Fwww.example.com'
Which obviously doesn't work. This is an excerpt from the method that makes the call to Botify:
public String callBotifyEndpoint(String reportType, String parameters) throws UnsupportedEncodingException {
String request = this.baseUri + "/analyses/myusername/myprojectname/myprojectslug/urls/https%3A%2F%2Fwww.example.com"
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Token " + this.apiKey);
HttpEntity<String> entity = new HttpEntity<>(headers);
UriComponentsBuilder botifyQueryBuilder = UriComponentsBuilder.fromUriString(request);
String queryStringBuild = botifyQueryBuilder.build(true).toUriString();
String botifyResult = null;
try {
System.out.println("Calling Botify API: " + queryStringBuild);
ResponseEntity<String> response = botifyTemplate.exchange(queryStringBuild, HttpMethod.GET, entity, String.class);
if(response.hasBody()) {
botifyResult = response.getBody();
}
} catch(RestClientException ex) {
ex.printStackTrace();
}
try {
} catch (Exception e) {
// TODO: handle exception
}
return botifyResult;
}
In this line:
botifyQueryBuilder.build(true).toUriString();
The "true" parameter indicates whether the data is already encoded or not. I've tried to disable it but the result is the same.
I've removed actual request generation process (along with my user and project's name) to simplify things, but this should return a response from Botify with the existing data for that URL.
Instead, it returns a 400 bad request error (which makes sense, because the URL is not correct).
I'm feeling like this may be a bug in RestTemplate's .exchange method, but maybe I'm not using it properly. Any suggestions?
Don't encode prematurly as you do here:
String request = this.baseUri + "/analyses/myusername/myprojectname/myprojectslug/urls/https%3A%2F%2Fwww.example.com";
Use parameter placeholders feature in RestTemplate instead of text concatenation.
Refer to:
Spring RestTemplate GET with parameters

While requesting access token reddit api is returning 429 error

When am trying to retrieve the access token getting 429 :Too Many Requests error,i have followed the steps mentioned in the link --> https://github.com/reddit/reddit/wiki/OAuth2
what could be the reason for the error .here is my code.
public static void main (String[] args) throws Exception
{
String url3 ="https://www.reddit.com/api/v1/access_token?";
OAuthRequest get_info_request = new OAuthRequest(Verb.POST, url3);
get_info_request.setCharset("UTF-8");
get_info_request.addBodyParameter("grant_type", "authorization_code");
get_info_request.addBodyParameter("redirect_uri", "xxxxxxxxxx");
get_info_request.addBodyParameter("code", "xxxxxxxxx");
get_info_request.addBodyParameter("USER_AGENT", "desktop:net.dean.ayati:v0.9.0 (by /u/ayati)");
System.out.println(get_info_request.getCompleteUrl()+get_info_request.getBodyContents());
Response json_response = get_info_request.send();
System.out.println(json_response.getBody());
JSONObject jsonResp = new JSONObject(json_response.getBody());
System.out.println("is" + jsonResp);
}
While HTTP headers are case-insensitive you still have USER_AGENT spelt incorrectly.
It uses a '-' not '_' in it.
Try:
get_info_request.addBodyParameter("User-Agent", "desktop:net.dean.ayati:v0.9.0 (by /u/ayati)");
and see if that works.

How to properly escape a URL to be used in RestTemplate that has flower brackets (JSON) [duplicate]

I am trying to access the contents of an API and I need to send a URL using RestTemplate.
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={\"price\":\"desc\"}";
OutputPage page = restTemplate.getForObject(url1, OutputPage .class);
But, I am getting the following error.
Exception in thread "main" java.lang.IllegalArgumentException: Not enough variable values available to expand '"price"'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:284)
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:220)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:317)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:46)
at org.springframework.web.util.UriComponents.expand(UriComponents.java:162)
at org.springframework.web.util.UriTemplate.expand(UriTemplate.java:119)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:501)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239)
at hello.Application.main(Application.java:26)
If I remove the sort criteria, it is working properly.
I need to parse the JSON using sort criteria.
Any help will be much appreciated.
Thanks
The root cause is that RestTemplate considers curly braces {...} in the given URL as a placeholder for URI variables and tries to replace them based on their name. For example
{pageSize}
would try to get a URI variable called pageSize. These URI variables are specified with some of the other overloaded getForObject methods. You haven't provided any, but your URL expects one, so the method throws an exception.
One solution is to make a String object containing the value
String sort = "{\"price\":\"desc\"}";
and provide a real URI variable in your URL
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={sort}";
You would call your getForObject() like so
OutputPage page = restTemplate.getForObject(url1, OutputPage.class, sort);
I strongly suggest you do not send any JSON in a request parameter of a GET request but rather send it in the body of a POST request.
If the solution suggested by sotirios-delimanolis is a little difficult to implement in a scenario, and if the URI string containing curly braces and other characters is guaranteed to be correct, it might be simpler to pass the encoded URI string to a method of RestTemplate that hits the ReST server.
The URI string can be built using UriComponentsBuilder.build(), encoded using UriComponents.encode(), and sent using RestTemplate.exchange() like this:
public ResponseEntity<Object> requestRestServer()
{
HttpEntity<?> entity = new HttpEntity<>(requestHeaders);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(rawValidUrl)
.queryParams(
(LinkedMultiValueMap<String, String>) allRequestParams);
UriComponents uriComponents = builder.build().encode();
ResponseEntity<Object> responseEntity = restTemplate.exchange(uriComponents.toUri(), HttpMethod.GET,
entity, String.class);
return responseEntity;
}
Building, encoding, and extracting URI have been seperated out for clarity in the above code snippet.
You can URL encode the parameter values:
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort=";
org.apache.commons.codec.net.URLCodec codec = new org.apache.commons.codec.net.URLCodec();
url1 = url1 + codec.encode("{\"price\":\"desc\"}");
OutputPage page = restTemplate.getForObject(url1, OutputPage.class);
You can set a specific UriTemplateHandler in your restTemplate. This handler would just ignore uriVariables :
UriTemplateHandler skipVariablePlaceHolderUriTemplateHandler = new UriTemplateHandler() {
#Override
public URI expand(String uriTemplate, Object... uriVariables) {
return retrieveURI(uriTemplate);
}
#Override
public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
return retrieveURI(uriTemplate);
}
private URI retrieveURI(String uriTemplate) {
return UriComponentsBuilder.fromUriString(uriTemplate).build().toUri();
}
};
restTemplate.setUriTemplateHandler(skipVariablePlaceHolderUriTemplateHandler);
You can encode url before using RestTemplate
URLEncoder.encode(data, StandardCharsets.UTF_8.toString());
You can simply append a variable key to the URL and give the value using the restTemplate.getForObject() method.
Example:
String url = "http://example.com/api?key=12345&sort={data}";
String data="{\"price\":\"desc\"}";
OutputPage page = restTemplate.getForObject(url, OutputPage.class, data);

Facebook login plugin with server side access to users data

I have got few things to work e.g. Using -
FB.login(function(response) {
if (response.authResponse) {
console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Good to see you, ' + response.name + '.');
});
} else {
console.log('User cancelled login or did not fully authorize.');
}
});
I am able to get all the details of the user, name, User ID etc.
My Problem is how to take all this information to the server "safely". I don't want this information to be sniffed on its way to server. I use JAVA(Servet/JSP) language, PLEASE HELP ME ON THIS. I wish there was some way like registration plugin where Facebook sends all the information on a redirect_url link.
Regards,
Jagpreet Singh
EDIT: If anybody requires the Java Code -
// it is important to enable url-safe mode for Base64 encoder
Base64 base64 = new Base64(true);
// split request into signature and data
String[] signedRequest = request.getParameter("signed_request").split("\\.", 2);
logger.info("Received signed_request = " + Arrays.toString(signedRequest));
// parse signature
String sig = new String(base64.decode(signedRequest[0].getBytes("UTF-8")));
// parse data and convert to JSON object
JSONObject data = (JSONObject) JSONSerializer.toJSON(new String(base64.decode(signedRequest[1].getBytes("UTF-8"))));
logger.warn("JSON Value = " + data);
// check signature algorithm
if (!"HMAC-SHA256".equals(data.getString("algorithm"))) {
// unknown algorithm is used
logger.error("HMAC-SHA256 Algo? = false, returning ERROR");
return ERROR;
} else {
logger.error("HMAC-SHA256 Algo? = true, Checking if data is signed correctly...");
}
// check if data is signed correctly
if (!hmacSHA256(signedRequest[1], fbSecretKey).equals(sig)) {
// signature is not correct, possibly the data was tampered with
logger.warn("DATA signed correctly? = false, returning ERROR");
return ERROR;
} else {
logger.warn("DATA signed correctly? = true, checking if user has authorized the APP...");
}
// check if user authorized the APP (FACEBOOK User)
if (!data.has("user_id") || !data.has("oauth_token")) {
// this is guest, create authorization url that will be passed
// to javascript
// note that redirect_uri (page the user will be forwarded to
// after authorization) is set to fbCanvasUrl
logger.warn("User has authorized the APP? = false, returning ERROR");
return ERROR;
} else {
logger.warn("User has authorized the APP? = true, Performing User Registration...");
// this is authorized user, get their info from Graph API using
// received access token
// String accessToken = data.getString("oauth_token");
// FacebookClient facebookClient = new
// DefaultFacebookClient(accessToken);
// User user = facebookClient.fetchObject("me", User.class);
}
Facebook sends a signed_request parameter when you authenticate with a client-side method. You can pass this to your server, authenticate it, and then unpack it to get at the information you need. It is encrypted with your app secret, so you can be sure that it is secure.
See the signed_request documentation for more information.

Categories

Resources