def setUp(self): token = "1c892n40nd03kdnc0112slzkl3091j20" self.validator = RequestValidator(token) self.uri = "http://www.postbin.org/1ed898x" self.params = { "AccountSid": "AC9a9f9392lad99kla0sklakjs90j092j3", "ApiVersion": "2010-04-01", "CallSid": "CAd800bb12c0426a7ea4230e492fef2a4f", "CallStatus": "ringing", "Called": "+15306384866", "CalledCity": "OAKLAND", "CalledCountry": "US", "CalledState": "CA", "CalledZip": "94612", "Caller": "+15306666666", "CallerCity": "SOUTH LAKE TAHOE", "CallerCountry": "US", "CallerName": "CA Wireless Call", "CallerState": "CA", "CallerZip": "89449", "Direction": "inbound", "From": "+15306666666", "FromCity": "SOUTH LAKE TAHOE", "FromCountry": "US", "FromState": "CA", "FromZip": "89449", "To": "+15306384866", "ToCity": "OAKLAND", "ToCountry": "US", "ToState": "CA", "ToZip": "94612", }
def decorator(request, *args, **kwargs): # Here we'll use the twilio library's request validation code to ensure # that the current request actually came from twilio, and isn't a # forgery. If it is a forgery, then we'll return a HTTP 403 error # (forbidden). try: url = request.build_absolute_uri() signature = request.META['HTTP_X_TWILIO_SIGNATURE'] except (AttributeError, KeyError): return HttpResponseForbidden() validator = RequestValidator(settings.TWILIO_AUTH_TOKEN) if not validator.validate(url, request.POST, signature): return HttpResponseForbidden() # Run the wrapped view, and capture the data returned. response = f(request, *args, **kwargs) # If the view returns a string, we'll assume it is XML TwilML data and # pass it back with the appropriate mimetype. We won't check the XML # data because that would be too time consuming for every request. # Instead, we'll let the errors pass through to be dealt with by the # developer. if isinstance(response, Verb): return HttpResponse(response, mimetype='text/xml') else: return response
def incoming_sms(): form = request.form validator = RequestValidator(os.getenv("TWILIO_AUTH_TOKEN")) url = request.url.replace("http:", "https:") sig = request.headers['X-Twilio-Signature'] if not validator.validate(url, form, sig): return "oops, invalid signature, no spoofs accepted" if not form['To'] == os.getenv('RRN_PHONE_NUMBER'): return "ignoring you, wrong To: phone number" if not form['AccountSid'] == os.getenv('TWILIO_ACCOUNT_SID'): return "ignoring you, wrong Twilio SID" text = "<!channel> Message from: {}: {}".format(form['From'], form['Body']) media_keys = [x for x in form.keys() if x.startswith("MediaUrl")] media_urls = [form[x] for x in sorted(media_keys)] if len(media_urls): text = "{} \nAttachments: \n{}".format(text, "\n".join(media_urls)) payload = dict(channel=os.getenv('SLACK_CHANNEL'), username=os.getenv('SLACK_USERNAME'), icon_emoji=os.getenv('SLACK_ICON_EMOJI'), text=text) response = requests.post(os.getenv('SLACK_POST_URL'), json.dumps(payload)) # FIXME check response return code # FIXME deal with MMS attachments # import IPython;IPython.embed() return Response( '<?xml version="1.0" encoding="UTF-8" ?><Response></Response>', mimetype='text/xml')
def test_international_sms(self): token = os.environ["TWILIO_AUTH_TOKEN"] validator = RequestValidator(token) uri = "http://www.postbin.org/1c2pdoc" params = { "AccountSid": "AC4bf2dafb92341f7caf8650403e422d23", "ApiVersion": "2010-04-01", "Body": "Chloéñ", "From": "+15305451766", "FromCity": "SOUTH LAKE TAHOE", "FromCountry": "US", "FromState": "CA", "FromZip": "89449", "SmsMessageSid": "SM51d6d055f53f1072543872c601aae89b", "SmsStatus": "SM51d6d055f53f1072543872c601aae89b", "SmsStatus": "received", "To": "+15304194304", "ToCity": "WOODLAND", "ToCountry": "US", "ToState": "CA", "ToZip": "95695", } expected = "UHkWu+6WLOzPunzb8PuCGPeW1Uw=" self.assertEquals(validator.compute_signature(uri, params), expected) self.assertTrue(validator.validate(uri, params, expected))
def sms(self, body, path="/sms/", to="+15558675309", from_="+15556667777", extra_params=None): params = { "MessageSid": "CAtesting", "AccountSid": "ACxxxxx", "To": to, "From": from_, "Body": body, "Direction": "inbound", "FromCity": "BROOKLYN", "FromState": "NY", "FromCountry": "US", "FromZip": "55555" } if extra_params: for k, v in extra_params.items(): params[k] = v HTTP_HOST = "example.com" validator = RequestValidator("yyyyyyyy") absolute_url = "http://{0}{1}".format(HTTP_HOST, path) signature = validator.compute_signature(absolute_url, params) return self.post(path, params, HTTP_X_TWILIO_SIGNATURE=signature, HTTP_HOST=HTTP_HOST)
def authenticate(): """Verify that request came from Twilio""" if current_app.config.get("DEBUG"): return # Don't auth in dev # Auth that it came from twilio validator = RequestValidator(current_app.config.get("TWILIO_AUTH_TOKEN")) valid = validator.validate(request.url, request.form, request.headers.get('X-Twilio-Signature', '')) if not valid: # If the request was spoofed, then send '403 Forbidden'. current_app.logger.info("IVR detected spoofed incoming call") abort(403) # Final thing - check that the number is registered to the application. # HYPOTHETICALLY - somebody could buy a number through their own # twilio account and point it at our app, and be able to cause # issues. to = request.values.get("To") if not to: current_app.logger.info("IVR missing To parameter") abort(403) p = phonenumbers.parse(to) if not current_app.config["TWILIO_NUMBER"].get(str(p.country_code)) == str( p.national_number): current_app.logger.info("IVR call to unregistered number - %s" % to) abort(403) return
def validate(self, request): validator = RequestValidator(self.auth[1]) signature = request.META.get('HTTP_X_TWILIO_SIGNATURE', '') base_url = settings.TEMBA_HOST url = "https://%s%s" % (base_url, request.get_full_path()) return validator.validate(url, request.POST, signature)
def wrapped(*args, **kwargs): # https://www.twilio.com/docs/api/security validator = RequestValidator(auth_token) signature = request.headers.get('X-Twilio-Signature', '') url = get_original_request_url(request) if not validator.validate(url, request.form, signature): app.logger.error('Invalid signature.') return None return f(*args, **kwargs)
def decorator(request_or_self, methods=['POST'], blacklist=True, *args, **kwargs): class_based_view = not (isinstance(request_or_self, HttpRequest)) if not class_based_view: request = request_or_self else: assert len(args) >= 1 request = args[0] # Turn off Twilio authentication when explicitly requested, or in debug mode. # Otherwise things do not work properly. For more information see the docs. use_forgery_protection = (getattr(settings, 'DJANGO_TWILIO_FORGERY_PROTECTION', not settings.DEBUG)) if use_forgery_protection: if request.method not in methods: return HttpResponseNotAllowed(request.method) # Forgery check try: validator = RequestValidator( django_twilio_settings.TWILIO_AUTH_TOKEN) url = request.build_absolute_uri() signature = request.META['HTTP_X_TWILIO_SIGNATURE'] except (AttributeError, KeyError): return HttpResponseForbidden() if request.method == 'POST': if not validator.validate(url, request.POST, signature): return HttpResponseForbidden() if request.method == 'GET': if not validator.validate(url, request.GET, signature): return HttpResponseForbidden() # Blacklist check checkBlackList = (getattr(settings, 'DJANGO_TWILIO_BLACKLIST_CHECK', blacklist)) if checkBlackList: blacklisted_resp = get_blacklisted_response(request) if blacklisted_resp: return blacklisted_resp response = f(request_or_self, *args, **kwargs) if isinstance(response, str): return HttpResponse(response, content_type='application/xml') elif isinstance(response, Verb): return HttpResponse(str(response), content_type='application/xml') else: return response
def twilio_validator_function(request): """returns true for authentic Twilio request, false for unauthenticated request""" validator = RequestValidator(app.config['TWILIO_AUTH_TOKEN']) URL = request.url params = {} if request.method == 'POST': params = request.values else: params = request.args twilio_signature = request.headers.get('X-Twilio-Signature') return validator.validate(URL, params, twilio_signature)
def _validate(self, request): full_path = self._site + request.path validator = RequestValidator(self._auth_token) signature = request.getHeader('X-Twilio-Signature') signature = signature if signature else '' post_vars = {k: v[0] for k, v in request.args.iteritems()} if validator.validate(full_path, post_vars, signature): return post_vars else: raise CallException(400, 'Twilio validation failed')
def before_request(): AUTH_TOKEN = 'ae6f6c20e96fc7a9803a100292ab5284' validator = RequestValidator(AUTH_TOKEN) def isValid(url, signature, postVars={}): return validator.validate(url, postVars, signature) if request.path in ["/call", "/handle-key"]: if (not isValid(request.url, request.headers['X-Twilio-Signature'], request.form)): return "Invalid"
def validator(self): """ An application-specific instance of :py:class:`twilio.util.RequestValidator`. Primarily for internal use. """ ctx = stack.top if ctx is not None: if not hasattr(ctx, 'twilio_validator'): ctx.twilio_validator = RequestValidator( current_app.config['TWILIO_AUTH_TOKEN']) return ctx.twilio_validator
def validate_twiml(): twilio_signature = request.get_header('X-Twilio-Signature') params = request.forms url = application.config.Twiml.callback_base_url + request.fullpath auth_token = application.config.Twilio.auth_token validator = RequestValidator(auth_token) validation = validator.validate(url, params, twilio_signature) log.debug("Validator: {}".format(validation)) log.debug("Twilio-signature: {}\r".format(twilio_signature) + \ "Params: {}\r".format(params) + \ "Url: {}".format(url)) return validation
def call(): ''' Handles incoming calls ''' validator = RequestValidator(TWILIO_AUTH_TOKEN) signature = request.headers.get('X-Twilio-Signature', '') if not validator.validate(request.url, request.form, signature): return abort(403) resp = twiml.Response() with resp.gather(timeout=5, action="/handle-input", finishOnKey="#", method="POST") as g: g.say("Hi, please enter your FizzBuzz number and then press pound") return str(resp)
def verify_request(self): 'verify request comes from Twilio' # this bit taken from Randal Degges' django-twilio library, which as of # 1c020e2a7c6f4845e7309d7277380c8b76d38ba4 has been released into the # public domain try: validator = RequestValidator(settings.TWILIO_AUTH_TOKEN) url = self.request.build_absolute_uri() signature = self.request.META['HTTP_X_TWILIO_SIGNATURE'] except (AttributeError, KeyError): raise PermissionDenied() if not validator.validate(url, self.request.POST, signature): raise PermissionDenied()
def decorator(request, *args, **kwargs): # Attempt to gather all required information to allow us to check the # incoming HTTP request for forgery. If any of this information is not # available, then we'll throw a HTTP 403 error (forbidden). # Ensure the request method is POST if request.method != "POST": logger.error("Twilio: Expected POST request", extra={"request": request}) return HttpResponseNotAllowed(request.method) if not getattr(settings, "TWILIO_SKIP_SIGNATURE_VALIDATION"): # Validate the request try: validator = RequestValidator(settings.TWILIO_AUTH_TOKEN) url = request.build_absolute_uri() # Ensure the original requested url is tested for validation # Prevents breakage when processed behind a proxy server if "HTTP_X_FORWARDED_SERVER" in request.META: protocol = "https" if request.META["HTTP_X_TWILIO_SSL"] == "Enabled" else "http" url = "{0}://{1}{2}".format( protocol, request.META["HTTP_X_FORWARDED_SERVER"], request.META["REQUEST_URI"] ) signature = request.META["HTTP_X_TWILIO_SIGNATURE"] except (AttributeError, KeyError) as e: logger.exception("Twilio: Missing META param", extra={"request": request}) return HttpResponseForbidden("Missing META param: %s" % e) # Now that we have all the required information to perform forgery # checks, we'll actually do the forgery check. if not validator.validate(url, request.POST, signature): logger.error( "Twilio: Invalid url signature %s - %s - %s", url, request.POST, signature, extra={"request": request} ) return HttpResponseForbidden("Invalid signature") # Run the wrapped view, and capture the data returned. response = f(request, *args, **kwargs) # If the view returns a string (or a ``twilio.Verb`` object), we'll # assume it is XML TwilML data and pass it back with the appropriate # mimetype. We won't check the XML data because that would be too time # consuming for every request. Instead, we'll let the errors pass # through to be dealt with by the developer. if isinstance(response, six.text_type): return HttpResponse(response, mimetype="application/xml") elif isinstance(response, Verb): return HttpResponse(force_text(response), mimetype="application/xml") else: return response
def send(self, method, url, authtoken): validator = RequestValidator(authtoken) params = urllib.urlencode(self.url_params) if method == "GET": url = "{0}?{1}".format(url, params) sig = validator.compute_signature(url, {}) req = urllib2.Request(url) elif method == "POST": sig = validator.compute_signature(url, self.url_params) req = urllib2.Request(url, params) else: raise CwurlioUserException("Invalid method: %s" % method) req.add_header("X-Twilio-Signature", sig) return urllib2.urlopen(req).read()
def decorated_function(*args, **kwargs): # Create an instance of the RequestValidator class validator = RequestValidator(os.environ.get('TWILIO_AUTH_TOKEN')) # Validate the request using its URL, POST data, # and X-TWILIO-SIGNATURE header request_valid = validator.validate( request.url, request.form, request.headers.get('X-TWILIO-SIGNATURE', '')) # Continue processing the request if it's valid (or if DEBUG is True) # and return a 403 error if it's not if request_valid or current_app.debug: return f(*args, **kwargs) else: return abort(403)
def decorator(request, *args, **kw): try: validator = RequestValidator(settings.TWILIO_AUTH_TOKEN) url = request.build_absolute_uri() signature = request.META['HTTP_X_TWILIO_SIGNATURE'] except (AttributeError, KeyError): return HttpResponseForbidden() # Now that we have all the required information to perform forgery # checks, we'll actually do the forgery check. if not validator.validate(url, request.POST, signature): return HttpResponseForbidden() twiml = viewfunc(request, *args, **kw) return HttpResponse(str(twiml), content_type='application/xml')
def decorated_function(request, *args, **kwargs): # Create an instance of the RequestValidator class validator = RequestValidator(os.environ.get('TWILIO_AUTH_TOKEN')) # Validate the request using its URL, POST data, # and X-TWILIO-SIGNATURE header request_valid = validator.validate( request.build_absolute_uri(), request.POST, request.META.get('HTTP_X_TWILIO_SIGNATURE', '')) # Continue processing the request if it's valid, return a 403 error if # it's not if request_valid: return f(request, *args, **kwargs) else: return HttpResponseForbidden()
def test_good_signature_GET(self): 'good signature is allowed on GET' with self.settings(TWILIO_AUTH_TOKEN='fred'): req = self.factory.get('/', data={'a': 'b'}) validator = RequestValidator(settings.TWILIO_AUTH_TOKEN) req.META['HTTP_X_TWILIO_SIGNATURE'] = validator.compute_signature( req.build_absolute_uri(), req.POST ) self.view.request = req try: self.view.verify_request() except PermissionDenied: self.fail('Raised PermissionDenied when not expecting it')
def handler(event, context): """Lambda function handler for Twilio response.""" resp = twiml.Response() print("Event:", event) if u'twilioSignature' in event and u'Body' in event \ and event['Body'].lower() == "secret": form_parameters = { k: urllib.unquote(v) for k, v in event.items() if k != u'twilioSignature' } validator = RequestValidator(os.environ['AUTH_TOKEN']) request_valid = validator.validate( os.environ['REQUEST_URL'], form_parameters, event[u'twilioSignature'] ) # print("Form params:",form_parameters,"validity:",request_valid) # If the request is valid and this is from the master number, # give the secret! if request_valid and u'From' in form_parameters and \ form_parameters[u'From'] == \ os.environ['MASTER_NUMBER'].encode('utf8'): # The message is validated and is from the master number secret_message = "I am putting myself to the fullest possible" \ " use, which is all I think that any conscious" \ " entity can ever hope to do." msg = resp.message(secret_message) # The following lines add media to an MMS reply msg.media('https://upload.wikimedia.org/wikipedia/commons/thumb' '/f/f6/HAL9000.svg/200px-HAL9000.svg.png') return str(resp) else: # Validation failed in some way; don't give up the secret secret_message = "I'm sorry, Dave. I'm afraid I can't do that." resp.message(secret_message) return str(resp) else: resp.message("Hello world! -Lambda") return str(resp)
def protect_forged_request(request): if request.method not in ['GET', 'POST']: return HttpResponseNotAllowed(request.method) try: validator = RequestValidator(settings.TWILIO_AUTH_TOKEN) url = request.build_absolute_uri() signature = request.META['HTTP_X_TWILIO_SIGNATURE'] except (AttributeError, KeyError): return HttpResponseForbidden() if request.method == 'POST': if not validator.validate(url, request.POST, signature): return HttpResponseForbidden() if request.method == 'GET': if not validator.validate(url, request.GET, signature): return HttpResponseForbidden()
def decorated_function(*args, **kwargs): validator = RequestValidator(TWILIO_SECRET) url = list(urlparse(request.url)) url[1] = url[1].encode("idna").decode("utf-8") url = urlunparse(url) signature = request.headers.get("X-TWILIO-SIGNATURE", "") request_valid = validator.validate(url, request.form, signature) if request_valid or current_app.debug: resp = Response() f(resp, *args, **kwargs) return str(resp) else: return abort(403)
def get(self): # Validate that request came from Twilio service. validator = RequestValidator(strings.AUTH_TOKEN) url = self.request.url url = url.replace("http", "https") signature = self.request.headers.get("X-Twilio-Signature", "") if not validator.validate(url, {}, signature): logging.warn("Request did not come from Twilio.") self.response.status = 403 return phone = self.request.get("From", None) account = Account.query().filter(Account.phone == phone).get() # Sending messages separately, send empty TwiML for now. twiml = twilio.twiml.Response() response = get_response(response_strings.CALL_HELLO, response_strings.VAR_NAME) if account and account.first: response = response.replace(response_strings.VAR_NAME, account.first) else: response = response.replace(response_strings.VAR_NAME, "") twiml.addSay(response) tracks = [ "better_things", "dare", "enchanted", "makes_me_wonder", "the_reeling", "viva_la_vida" ] track_num = random.randint(0, len(tracks) - 1) twiml.addPlay(self.request.application_url + "/static/mp3/" + tracks[track_num] + ".mp3") response = get_response(response_strings.CALL_GOODBYE) twiml.addSay(response) twiml.addHangup() self.response.write(twiml)
def validate(self, attrs): AUTH_TOKEN = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY' validator = RequestValidator(AUTH_TOKEN) # the callback URL you provided to Twilio url = "http://www.example.com/my/callback/url.xml" # the POST variables attached to the request (eg "From", "To") post_vars = {} # X-Twilio-Signature header value signature = "HpS7PBa1Agvt4OtO+wZp75IuQa0=" # will look something like that if validator.validate(url, post_vars, signature): print "Confirmed to have come from Twilio." else: print "NOT VALID. It might have been spoofed!"
def get(self): # Validate that request came from Twilio service. validator = RequestValidator(strings.AUTH_TOKEN) url = self.request.url url = url.replace("http", "https") signature = self.request.headers.get("X-Twilio-Signature", "") if not validator.validate(url, {}, signature): logging.warn("Request did not come from Twilio.") self.response.status = 403 return # Sending messages separately, send empty TwiML for now. twiml = twilio.twiml.Response() self.response.write(twiml) twilio_client = TwilioRestClient(strings.ACCOUNT_SID, strings.AUTH_TOKEN) phone = self.request.get("From", None) account = Account.query().filter(Account.phone == phone).get() if account is None: account = create.controller(self.request) was_command = False if not account.state_locked: was_command = command.controller(self.request, account) if not was_command: if account.state == account_model.STATE_ONBOARD: onboard.controller(self.request, account) elif account.state == account_model.STATE_DELETE: delete.controller(self.request, account) else: response = get_response(response_strings.NO_MATCH, response_strings.VAR_NAME) response = response.replace(response_strings.VAR_NAME, account.first) twilio_client.messages.create(to=account.phone, from_=strings.SERVICE_NUMBER, body=response)
def receiver(request): resp = twilio.twiml.Response() validator = RequestValidator(live_credentials) if validator.validate(request.build_absolute_uri(), request.POST, request.META.get('HTTP_X_TWILIO_SIGNATURE', "fail")): print 'Yay for twilio' else: print 'Not twilio resopnse!' return HttpResponseForbidden() resp.say( "Welcome to Kevin's PhoneBuzz! To play, please enter a number on the keypad and then press pound." ) with resp.gather(finishOnKey='#', action='player', method='POST', timeout=10) as g: resp.redirect('') resp.redirect('player') return HttpResponse(str(resp))
def post(self, request, *args, **kwargs): from twilio.util import RequestValidator call = IVRCall.objects.filter(pk=kwargs['pk']).first() if not call: return HttpResponse("Not found", status=404) client = call.channel.get_ivr_client() if request.REQUEST.get('hangup', 0): if not request.user.is_anonymous(): user_org = request.user.get_org() if user_org and user_org.pk == call.org.pk: client.calls.hangup(call.external_id) return HttpResponse(json.dumps(dict(status='Canceled')), content_type="application/json") else: return HttpResponse("Not found", status=404) validator = RequestValidator(client.auth[1]) signature = request.META.get('HTTP_X_TWILIO_SIGNATURE', '') base_url = settings.TEMBA_HOST url = "https://%s%s" % (base_url, request.get_full_path()) # make sure this is coming from twilio if validator.validate(url, request.POST, signature): call.update_status(request.POST.get('CallStatus', None), request.POST.get('CallDuration', None)) call.save() if call.status == IN_PROGRESS: if call.is_flow(): response = Flow.handle_call(call, request.POST) return HttpResponse(unicode(response)) else: return build_json_response(dict(message="Updated call status")) else: # pragma: no cover # raise an exception that things weren't properly signed raise ValidationError("Invalid request signature") return build_json_response(dict(message="Unhandled"))